6 Commits

Author SHA1 Message Date
8f08e67e4c 미사용 고아 리소스 invincible belief.sprite 제거
카드 5장 통일로 이미지 카드가 손패에서 빠져 이 스프라이트는 미참조 고아가 됨.
descriptor Id가 무효라 플레이마다 LEA-3021 에러를 유발하므로 제거.
(맵 Background.WebUrl의 휴면 참조는 Type=Template이라 미사용 — 무해, 유지)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 01:24:25 +09:00
4bf7e29315 Maker 세션 재저장분(맵 02/05/06/07/10/11) 복구 포함
stash해 둔 로컬 맵 재저장분 복구. 몬스터 2종·old 스프라이트 미사용·타일셋 교체·유효 GUID 무결성 검증 완료.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 01:13:46 +09:00
b1921ee843 gen-slaydeck: 유효한 GUID 생성으로 수정 (DeckHud·카드 자식 entity id)
기존 guid() prefix+4hex는 8-4-4-4-12 형식이 아니어서 Maker가 적용 거부(LEA-3054).
네임스페이스 바이트 기반 hex GUID로 변경하고, 기존 자식 id도 재생성 시 정규화.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 01:11:44 +09:00
f508952960 재생성: 카드 클릭 사용·균일 카드·핸들러 클로저 반영
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 01:08:40 +09:00
5bc5b3dc5c 덱 컨트롤러 생성기: 핸들러 클로저화·카드데이터 단일화·카드클릭 사용·pcall 제거
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-08 01:07:46 +09:00
6c392764d5 덱 컨트롤러 코드리뷰 수정 설계 문서 추가
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 01:01:42 +09:00
12 changed files with 3045 additions and 2463 deletions

View File

@@ -84,6 +84,13 @@
"SyncDirection": 0, "SyncDirection": 0,
"Attributes": [], "Attributes": [],
"Name": "EndTurnHandler" "Name": "EndTurnHandler"
},
{
"Type": "any",
"DefaultValue": "nil",
"SyncDirection": 0,
"Attributes": [],
"Name": "Cards"
} }
], ],
"Methods": [ "Methods": [
@@ -111,7 +118,7 @@
"Name": null "Name": null
}, },
"Arguments": [], "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.MaxEnergy = 3\nself.Turn = 0\nself.DiscardPile = {}\nself.Hand = {}\nself.Cards = {\n\tStrike = { name = \"타격\", cost = 1, desc = \"피해 6\", kind = \"Attack\" },\n\tDefend = { name = \"방어\", cost = 1, desc = \"방어도 5\", kind = \"Skill\" },\n\tBash = { name = \"강타\", cost = 2, desc = \"피해 10\", kind = \"Attack\" },\n}\nself.DrawPile = { \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Strike\", \"Defend\", \"Defend\", \"Defend\", \"Defend\", \"Bash\" }\nself:Shuffle(self.DrawPile)\nself:BindButtons()\nself:StartPlayerTurn()",
"Scope": 2, "Scope": 2,
"ExecSpace": 6, "ExecSpace": 6,
"Attributes": [], "Attributes": [],
@@ -149,7 +156,7 @@
"Name": null "Name": null
}, },
"Arguments": [], "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",
"Scope": 2, "Scope": 2,
"ExecSpace": 6, "ExecSpace": 6,
"Attributes": [], "Attributes": [],
@@ -285,7 +292,7 @@
"Name": "cardId" "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\telse\n\t\tok, color = pcall(function() return Color(0.46, 0.68, 0.52, 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, "Scope": 2,
"ExecSpace": 6, "ExecSpace": 6,
"Attributes": [], "Attributes": [],
@@ -364,6 +371,52 @@
"ExecSpace": 6, "ExecSpace": 6,
"Attributes": [], "Attributes": [],
"Name": "AnimateCardFrom" "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.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\nself:Toast(c.name .. \" — \" .. c.desc)\ntable.remove(self.Hand, slot)\ntable.insert(self.DiscardPile, cardId)\nself:RenderHand(false)\nself:RenderPiles()",
"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"
} }
], ],
"EntityEventHandlers": [] "EntityEventHandlers": []

View File

@@ -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"
}
}
}

View File

@@ -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', `<lua>`, [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미사용·타일셋교체) 검증 후 포함

View File

@@ -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)
적 턴, 카드 효과의 실제 전투 적용, 신규 카드 종류.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -19,7 +19,10 @@ const ALIGN_CENTER = 0;
const ALIGN_BOTTOM_CENTER = 6; const ALIGN_BOTTOM_CENTER = 6;
function guid(prefix, n) { 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 : 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 }) { function transform({ parentW, parentH, anchor, pivot, size, pos, align = 0 }) {
@@ -193,6 +196,14 @@ function upsertUi() {
sp.ImageRUID = { DataId: '' }; sp.ImageRUID = { DataId: '' };
sp.Type = 1; sp.Type = 1;
sp.Color = cards[i - 1].tint; sp.Color = cards[i - 1].tint;
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.enable = true;
card.jsonString.visible = true; card.jsonString.visible = true;
@@ -221,6 +232,7 @@ function upsertUi() {
ui.ContentProto.Entities.push(child); ui.ContentProto.Entities.push(child);
byPath.set(path, child); byPath.set(path, child);
} else { } else {
child.id = guid('dck', i * 10 + children.findIndex(([s]) => s === suffix));
child.jsonString.enable = true; child.jsonString.enable = true;
child.jsonString.visible = true; child.jsonString.visible = true;
child.jsonString['@components'][2].Text = cfg.value; child.jsonString['@components'][2].Text = cfg.value;
@@ -383,12 +395,18 @@ function writeCodeblocks() {
prop('number', 'Turn', '0'), prop('number', 'Turn', '0'),
prop('number', 'TweenEventId', '0'), prop('number', 'TweenEventId', '0'),
prop('any', 'EndTurnHandler'), prop('any', 'EndTurnHandler'),
prop('any', 'Cards'),
], [ ], [
method('OnBeginPlay', `self:StartCombat()`), method('OnBeginPlay', `self:StartCombat()`),
method('StartCombat', `self.MaxEnergy = 3 method('StartCombat', `self.MaxEnergy = 3
self.Turn = 0 self.Turn = 0
self.DiscardPile = {} self.DiscardPile = {}
self.Hand = {} 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.DrawPile = { "Strike", "Strike", "Strike", "Strike", "Strike", "Defend", "Defend", "Defend", "Defend", "Bash" }
self:Shuffle(self.DrawPile) self:Shuffle(self.DrawPile)
self:BindButtons() self:BindButtons()
@@ -400,15 +418,20 @@ for i = #list, 2, -1 do
\tlocal j = math.random(1, i) \tlocal j = math.random(1, i)
\tlist[i], list[j] = list[j], list[i] \tlist[i], list[j] = list[j], list[i]
end`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'list' }]), end`, [{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'list' }]),
method('BindButtons', `local buttonEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton") method('BindButtons', `local endTurn = _EntityService:GetEntityByPath("/ui/DefaultGroup/DeckHud/EndTurnButton")
if buttonEntity == nil or buttonEntity.ButtonComponent == nil then if endTurn ~= nil and endTurn.ButtonComponent ~= nil then
\treturn
end
if self.EndTurnHandler ~= nil then if self.EndTurnHandler ~= nil then
\tbuttonEntity:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler) endTurn:DisconnectEvent(ButtonClickEvent, self.EndTurnHandler)
\tself.EndTurnHandler = nil self.EndTurnHandler = nil
end end
self.EndTurnHandler = buttonEntity:ConnectEvent(ButtonClickEvent, self.EndPlayerTurn)`), 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`),
method('StartPlayerTurn', `self.Turn = self.Turn + 1 method('StartPlayerTurn', `self.Turn = self.Turn + 1
self.Energy = self.MaxEnergy self.Energy = self.MaxEnergy
self:DrawCards(5) self:DrawCards(5)
@@ -460,43 +483,22 @@ for i = 1, 5 do
\tend \tend
end end
self:RenderPiles()`, [{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'animate' }]), self:RenderPiles()`, [{ Type: 'boolean', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'animate' }]),
method('ApplyCardVisual', `local name = cardId method('ApplyCardVisual', `local c = self.Cards[cardId]
local cost = 0 if c == nil then
local desc = "" c = { name = cardId, cost = 0, desc = "", kind = "Skill" }
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"
end end
self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Cost", tostring(cost)) self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Cost", tostring(c.cost))
self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Name", name) self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Name", c.name)
self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Desc", desc) self:SetText("/ui/DefaultGroup/CardHand/Card" .. tostring(slot) .. "/Desc", c.desc)
local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(slot)) local cardEntity = _EntityService:GetEntityByPath("/ui/DefaultGroup/CardHand/Card" .. tostring(slot))
if cardEntity ~= nil and cardEntity.SpriteGUIRendererComponent ~= nil then if cardEntity ~= nil and cardEntity.SpriteGUIRendererComponent ~= nil then
\tlocal ok = false if c.kind == "Attack" then
\tlocal color = nil cardEntity.SpriteGUIRendererComponent.Color = Color(0.86, 0.42, 0.38, 1)
\tif kind == "Attack" then elseif c.kind == "Skill" then
\t\tok, color = pcall(function() return Color(0.86, 0.42, 0.38, 1) end) cardEntity.SpriteGUIRendererComponent.Color = Color(0.42, 0.55, 0.85, 1)
\telseif kind == "Skill" then else
\t\tok, color = pcall(function() return Color(0.42, 0.55, 0.85, 1) end) cardEntity.SpriteGUIRendererComponent.Color = Color(0.46, 0.68, 0.52, 1)
\telse end
\t\tok, color = pcall(function() return Color(0.46, 0.68, 0.52, 1) end)
\tend
\tif ok == true and color ~= nil then
\t\tcardEntity.SpriteGUIRendererComponent.Color = color
\tend
end`, [ end`, [
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' },
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' }, { Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'cardId' },
@@ -530,6 +532,28 @@ end, 1 / 60)`, [
{ Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toPos' }, { Type: 'any', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toPos' },
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'duration' }, { Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'duration' },
]), ]),
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' }]),
]); ]);
for (const m of combat.ContentProto.Json.Methods) { for (const m of combat.ContentProto.Json.Methods) {
m.ExecSpace = 6; m.ExecSpace = 6;

View File

@@ -1209,7 +1209,7 @@
{ {
"id": "cad00001-0000-4000-8000-000000000001", "id": "cad00001-0000-4000-8000-000000000001",
"path": "/ui/DefaultGroup/CardHand/Card1", "path": "/ui/DefaultGroup/CardHand/Card1",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent",
"jsonString": { "jsonString": {
"name": "Card1", "name": "Card1",
"path": "/ui/DefaultGroup/CardHand/Card1", "path": "/ui/DefaultGroup/CardHand/Card1",
@@ -1342,13 +1342,60 @@
"RaycastTarget": true, "RaycastTarget": true,
"Type": 1, "Type": 1,
"Enable": true "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 "@version": 1
} }
}, },
{ {
"id": "cad00002-0000-4000-8000-000000000002", "id": "0ca0000a-0000-4000-8000-00000ca0000a",
"path": "/ui/DefaultGroup/CardHand/Card1/Cost", "path": "/ui/DefaultGroup/CardHand/Card1/Cost",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -1536,7 +1583,7 @@
} }
}, },
{ {
"id": "cad00003-0000-4000-8000-000000000003", "id": "0ca0000b-0000-4000-8000-00000ca0000b",
"path": "/ui/DefaultGroup/CardHand/Card1/Name", "path": "/ui/DefaultGroup/CardHand/Card1/Name",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -1724,7 +1771,7 @@
} }
}, },
{ {
"id": "cad00004-0000-4000-8000-000000000004", "id": "0ca0000c-0000-4000-8000-00000ca0000c",
"path": "/ui/DefaultGroup/CardHand/Card1/Desc", "path": "/ui/DefaultGroup/CardHand/Card1/Desc",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -1914,7 +1961,7 @@
{ {
"id": "cad00005-0000-4000-8000-000000000005", "id": "cad00005-0000-4000-8000-000000000005",
"path": "/ui/DefaultGroup/CardHand/Card2", "path": "/ui/DefaultGroup/CardHand/Card2",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent",
"jsonString": { "jsonString": {
"name": "Card2", "name": "Card2",
"path": "/ui/DefaultGroup/CardHand/Card2", "path": "/ui/DefaultGroup/CardHand/Card2",
@@ -2047,13 +2094,60 @@
"RaycastTarget": true, "RaycastTarget": true,
"Type": 1, "Type": 1,
"Enable": true "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 "@version": 1
} }
}, },
{ {
"id": "cad00006-0000-4000-8000-000000000006", "id": "0ca00014-0000-4000-8000-00000ca00014",
"path": "/ui/DefaultGroup/CardHand/Card2/Cost", "path": "/ui/DefaultGroup/CardHand/Card2/Cost",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -2241,7 +2335,7 @@
} }
}, },
{ {
"id": "cad00007-0000-4000-8000-000000000007", "id": "0ca00015-0000-4000-8000-00000ca00015",
"path": "/ui/DefaultGroup/CardHand/Card2/Name", "path": "/ui/DefaultGroup/CardHand/Card2/Name",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -2429,7 +2523,7 @@
} }
}, },
{ {
"id": "cad00008-0000-4000-8000-000000000008", "id": "0ca00016-0000-4000-8000-00000ca00016",
"path": "/ui/DefaultGroup/CardHand/Card2/Desc", "path": "/ui/DefaultGroup/CardHand/Card2/Desc",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -2619,7 +2713,7 @@
{ {
"id": "cad00009-0000-4000-8000-000000000009", "id": "cad00009-0000-4000-8000-000000000009",
"path": "/ui/DefaultGroup/CardHand/Card3", "path": "/ui/DefaultGroup/CardHand/Card3",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent",
"jsonString": { "jsonString": {
"name": "Card3", "name": "Card3",
"path": "/ui/DefaultGroup/CardHand/Card3", "path": "/ui/DefaultGroup/CardHand/Card3",
@@ -2752,13 +2846,60 @@
"RaycastTarget": true, "RaycastTarget": true,
"Type": 1, "Type": 1,
"Enable": true "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 "@version": 1
} }
}, },
{ {
"id": "cad0000a-0000-4000-8000-00000000000a", "id": "0ca0001e-0000-4000-8000-00000ca0001e",
"path": "/ui/DefaultGroup/CardHand/Card3/Cost", "path": "/ui/DefaultGroup/CardHand/Card3/Cost",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -2946,7 +3087,7 @@
} }
}, },
{ {
"id": "cad0000b-0000-4000-8000-00000000000b", "id": "0ca0001f-0000-4000-8000-00000ca0001f",
"path": "/ui/DefaultGroup/CardHand/Card3/Name", "path": "/ui/DefaultGroup/CardHand/Card3/Name",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -3134,7 +3275,7 @@
} }
}, },
{ {
"id": "cad0000c-0000-4000-8000-00000000000c", "id": "0ca00020-0000-4000-8000-00000ca00020",
"path": "/ui/DefaultGroup/CardHand/Card3/Desc", "path": "/ui/DefaultGroup/CardHand/Card3/Desc",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -3324,7 +3465,7 @@
{ {
"id": "cad0000d-0000-4000-8000-00000000000d", "id": "cad0000d-0000-4000-8000-00000000000d",
"path": "/ui/DefaultGroup/CardHand/Card4", "path": "/ui/DefaultGroup/CardHand/Card4",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent",
"jsonString": { "jsonString": {
"name": "Card4", "name": "Card4",
"path": "/ui/DefaultGroup/CardHand/Card4", "path": "/ui/DefaultGroup/CardHand/Card4",
@@ -3457,13 +3598,60 @@
"RaycastTarget": true, "RaycastTarget": true,
"Type": 1, "Type": 1,
"Enable": true "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 "@version": 1
} }
}, },
{ {
"id": "cad0000e-0000-4000-8000-00000000000e", "id": "0ca00028-0000-4000-8000-00000ca00028",
"path": "/ui/DefaultGroup/CardHand/Card4/Cost", "path": "/ui/DefaultGroup/CardHand/Card4/Cost",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -3651,7 +3839,7 @@
} }
}, },
{ {
"id": "cad0000f-0000-4000-8000-00000000000f", "id": "0ca00029-0000-4000-8000-00000ca00029",
"path": "/ui/DefaultGroup/CardHand/Card4/Name", "path": "/ui/DefaultGroup/CardHand/Card4/Name",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -3839,7 +4027,7 @@
} }
}, },
{ {
"id": "cad00010-0000-4000-8000-000000000010", "id": "0ca0002a-0000-4000-8000-00000ca0002a",
"path": "/ui/DefaultGroup/CardHand/Card4/Desc", "path": "/ui/DefaultGroup/CardHand/Card4/Desc",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -4029,7 +4217,7 @@
{ {
"id": "cad00011-0000-4000-8000-000000000011", "id": "cad00011-0000-4000-8000-000000000011",
"path": "/ui/DefaultGroup/CardHand/Card5", "path": "/ui/DefaultGroup/CardHand/Card5",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent",
"jsonString": { "jsonString": {
"name": "Card5", "name": "Card5",
"path": "/ui/DefaultGroup/CardHand/Card5", "path": "/ui/DefaultGroup/CardHand/Card5",
@@ -4162,13 +4350,60 @@
"RaycastTarget": true, "RaycastTarget": true,
"Type": 1, "Type": 1,
"Enable": true "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 "@version": 1
} }
}, },
{ {
"id": "dck0032-0000-4000-8000-000000000032", "id": "0ca00032-0000-4000-8000-00000ca00032",
"path": "/ui/DefaultGroup/CardHand/Card5/Cost", "path": "/ui/DefaultGroup/CardHand/Card5/Cost",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -4356,7 +4591,7 @@
} }
}, },
{ {
"id": "dck0033-0000-4000-8000-000000000033", "id": "0ca00033-0000-4000-8000-00000ca00033",
"path": "/ui/DefaultGroup/CardHand/Card5/Name", "path": "/ui/DefaultGroup/CardHand/Card5/Name",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -4544,7 +4779,7 @@
} }
}, },
{ {
"id": "dck0034-0000-4000-8000-000000000034", "id": "0ca00034-0000-4000-8000-00000ca00034",
"path": "/ui/DefaultGroup/CardHand/Card5/Desc", "path": "/ui/DefaultGroup/CardHand/Card5/Desc",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -4732,7 +4967,7 @@
} }
}, },
{ {
"id": "hud0000-0000-4000-8000-000000000000", "id": "0d000000-0000-4000-8000-00000d000000",
"path": "/ui/DefaultGroup/DeckHud", "path": "/ui/DefaultGroup/DeckHud",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
@@ -4873,7 +5108,7 @@
} }
}, },
{ {
"id": "hud0001-0000-4000-8000-000000000001", "id": "0d000001-0000-4000-8000-00000d000001",
"path": "/ui/DefaultGroup/DeckHud/DrawPile", "path": "/ui/DefaultGroup/DeckHud/DrawPile",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
@@ -5014,7 +5249,7 @@
} }
}, },
{ {
"id": "hud0002-0000-4000-8000-000000000002", "id": "0d000002-0000-4000-8000-00000d000002",
"path": "/ui/DefaultGroup/DeckHud/DrawPile/Label", "path": "/ui/DefaultGroup/DeckHud/DrawPile/Label",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -5202,7 +5437,7 @@
} }
}, },
{ {
"id": "hud0003-0000-4000-8000-000000000003", "id": "0d000003-0000-4000-8000-00000d000003",
"path": "/ui/DefaultGroup/DeckHud/DrawPile/Count", "path": "/ui/DefaultGroup/DeckHud/DrawPile/Count",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -5390,7 +5625,7 @@
} }
}, },
{ {
"id": "hud0004-0000-4000-8000-000000000004", "id": "0d000004-0000-4000-8000-00000d000004",
"path": "/ui/DefaultGroup/DeckHud/DiscardPile", "path": "/ui/DefaultGroup/DeckHud/DiscardPile",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent",
"jsonString": { "jsonString": {
@@ -5531,7 +5766,7 @@
} }
}, },
{ {
"id": "hud0005-0000-4000-8000-000000000005", "id": "0d000005-0000-4000-8000-00000d000005",
"path": "/ui/DefaultGroup/DeckHud/DiscardPile/Label", "path": "/ui/DefaultGroup/DeckHud/DiscardPile/Label",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -5719,7 +5954,7 @@
} }
}, },
{ {
"id": "hud0006-0000-4000-8000-000000000006", "id": "0d000006-0000-4000-8000-00000d000006",
"path": "/ui/DefaultGroup/DeckHud/DiscardPile/Count", "path": "/ui/DefaultGroup/DeckHud/DiscardPile/Count",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -5907,7 +6142,7 @@
} }
}, },
{ {
"id": "hud0007-0000-4000-8000-000000000007", "id": "0d000007-0000-4000-8000-00000d000007",
"path": "/ui/DefaultGroup/DeckHud/EndTurnButton", "path": "/ui/DefaultGroup/DeckHud/EndTurnButton",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {
@@ -6142,7 +6377,7 @@
} }
}, },
{ {
"id": "hud0008-0000-4000-8000-000000000008", "id": "0d000008-0000-4000-8000-00000d000008",
"path": "/ui/DefaultGroup/DeckHud/Energy", "path": "/ui/DefaultGroup/DeckHud/Energy",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent", "componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": { "jsonString": {