Merge pull request 'feat(ascension): 승천 시스템 A1~A10 + UserDataStorage 개인 저장 (배포 퀄리티 P11)' (#46) from feature/p11-ascension into main

This commit was merged in pull request #46.
This commit is contained in:
2026-06-12 14:25:19 +09:00
5 changed files with 1100 additions and 15 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,23 @@
# P11 — 승천 + UserDataStorage 구현 계획
> **For agentic workers:** REQUIRED SUB-SKILL: superpowers:executing-plans. 설계: `2026-06-12-ascension-design.md`
### Task 1: 생성기 — ExecSpace 보존 + 서버 RPC 3종
- [ ] `for m of Methods: m.ExecSpace = 6``if (m.ExecSpace === 0) m.ExecSpace = 6;` (명시값 보존)
- [ ] props: `AscensionLevel`(number 0)·`AscensionUnlocked`(number 0)
- [ ] `ReqLoadAscension(userId)`[ExecSpace 1]·`RecvAscension(n, userId)`[2]·`SaveAscension(n, userId)`[1] — 설계 코드 그대로, OnBeginPlay(6)에서 LocalPlayer.UserId로 ReqLoad
- [ ] 커밋
### Task 2: 생성기 — 모디파이어·해금·메뉴 UI
- [ ] 헬퍼 5종(AscHpMult/AscAtkMult/AscEliteBonus/AscGoldMult/AscStartHpPenalty) + StartRun/BuildMonsters/CheckCombatEnd/RenderRun 적용
- [ ] EndRun 클리어 분기: 해금+1·SaveAscension·"런 클리어! 승천 N 해금!"
- [ ] MainMenu `AscMinus/AscLabel/AscPlus` + `AdjustAscension`/`RenderAscension` + BindMenuButtons 연결
- [ ] 커밋
### Task 3: 재생성·메이커 검증·PR
- [ ] 재생성·테스트 44건 유지·grep -c 카운트 → 커밋
- [ ] 메이커: 메뉴 승천 라벨/[-][+]·승천2로 런 시작(HP·적 배율 로그 확인)·강제 클리어→해금+1·재플레이 로드 → 스크린샷
- [ ] push → gitea-pr.mjs PR·머지 → main pull → 메모리 갱신
## Self-Review
- RPC 파라미터 any 금지(허용 타입: string/number) 준수 ✓ / RecvAscension 마지막 인자 userId(특정 클라 응답) ✓ / 시뮬 비대상 명시 ✓

View File

@@ -0,0 +1,47 @@
# P11 — 승천 시스템 + UserDataStorage 설계
날짜: 2026-06-12 (사용자 승인 — P9/P10/P11 중 3단계)
브랜치: `feature/p11-ascension`
## 범위
1. **개인별 승천 저장**`_DataStorageService:GetUserDataStorage(userId)` (유저별 영구, 메이커↔배포 분리). 이 프로젝트 첫 서버-클라 RPC.
2. **승천 선택 UI** — 메인 메뉴에서 0~해금치 선택([-]/[+]), 런 클리어 시 해금 +1 (최대 10), 클리어 문구 "승천 N 해금!"
3. **승천 모디파이어 A1~A10** (누적):
| 단계 | 효과 | 단계 | 효과 |
|---|---|---|---|
| A1 | 적 HP +10% | A6 | 적 HP 추가 +10% |
| A2 | 적 피해 +10% | A7 | 적 피해 추가 +10% |
| A3 | 시작 HP -10 | A8 | 시작 HP 추가 -10 |
| A4 | 정예·보스 배율 +0.2 | A9 | 정예·보스 배율 추가 +0.2 |
| A5 | 승리 메소 -25% | A10 | 승리 메소 추가 -25% |
4. TopBar에 `· 승천N` 표시 (0이면 생략)
## 서버-클라 구조 (ExecSpace)
codeblock JSON ExecSpace 실측(프로브): **Server=5**(클라→서버), **Client=6**(서버→클라·마지막 인자 userId로 특정 유저 라우팅), 1=ServerOnly(클라 호출 무시), 2=ClientOnly(서버 호출 무시). 기존 메서드의 6은 Client라 클라 호출 시 제자리 실행으로 동작 동일:
- `ReqLoadAscension(userId)` **[Server=5]** — 클라 OnBeginPlay에서 호출 → 서버에서 `GetAndWait("ascensionUnlocked")``RecvAscension(n, userId)` 호출
- `RecvAscension(n, userId)` **[Client=6]** — 마지막 파라미터 userId로 **요청한 클라이언트에만** 응답 (MSW 공식 패턴) → `AscensionUnlocked` 갱신·메뉴 렌더
- `SaveAscension(n, userId)` **[Server=5]** — `SetAndWait`
- 생성기의 `m.ExecSpace = 6` 일괄 적용을 "명시값(≠0)은 보존"으로 수정
## 적용 지점
- `StartRun`: 시작 HP에서 A3/A8 차감, `AscensionLevel`은 메뉴 선택값 유지
- `BuildMonsters`: maxHp ×(1+A1/A6), Attack 인텐트 ×(1+A2/A7), elite/boss 그룹이면 막 배율 +A4/A9
- `CheckCombatEnd`: 승리 메소 ×(1-A5/A10) floor
- `EndRun("런 클리어!")`: `AscensionLevel >= AscensionUnlocked and Unlocked < 10`이면 해금+1·저장·결과 문구 교체
- 헬퍼: `AscHpMult`/`AscAtkMult`/`AscEliteBonus`/`AscGoldMult`/`AscStartHpPenalty`
## UI (MainMenu)
- `AscRow`: `AscMinus`[-] · `AscLabel`("승천 L / 해금 U") · `AscPlus`[+] — 새 게임 버튼 아래
- `AdjustAscension(delta)` clamp 0..Unlocked, `RenderAscension`
## 검증
1. 시뮬: 모디파이어는 Lua 전용(런 메타) — 시뮬 비대상 명시. 기존 44건 유지
2. 메이커: 로드(첫 0)→승천 2 선택→적 HP/피해 배율·시작 HP 확인→클리어→해금+1·저장→재시작 후 로드 확인(메이커 스토리지), 빌드·런타임 0에러

View File

@@ -2110,6 +2110,48 @@ function upsertUi() {
text({ value: '새 게임', fontSize: 30, bold: true, color: GOLD, alignment: 0 }),
],
}));
// 승천 선택 (P11): [-] 라벨 [+]
menu.push(entity({
id: guid('menu', 195),
path: '/ui/DefaultGroup/MainMenu/AscMinus',
modelId: 'uibutton',
entryId: 'UIButton',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
displayOrder: 5,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 52, y: 52 }, pos: { x: -170, y: -185 }, 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: 4 }),
],
}));
menu.push(entity({
id: guid('menu', 196),
path: '/ui/DefaultGroup/MainMenu/AscLabel',
modelId: 'uitext',
entryId: 'UIText',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
displayOrder: 6,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 250, y: 40 }, pos: { x: 0, y: -185 }, align: ALIGN_CENTER }),
sprite({ color: TRANSPARENT }),
text({ value: '승천 0 / 해금 0', fontSize: 22, bold: true, color: { r: 0.85, g: 0.7, b: 0.95, a: 1 }, alignment: 4 }),
],
}));
menu.push(entity({
id: guid('menu', 197),
path: '/ui/DefaultGroup/MainMenu/AscPlus',
modelId: 'uibutton',
entryId: 'UIButton',
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent',
displayOrder: 7,
components: [
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 52, y: 52 }, pos: { x: 170, y: -185 }, 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: 4 }),
],
}));
menu.push(entity({
id: guid('menu', 4),
path: '/ui/DefaultGroup/MainMenu/ContinueButton',
@@ -2360,8 +2402,12 @@ function writeCodeblocks() {
prop('any', 'NewGameHandler'),
prop('any', 'WarriorSelectHandler'),
prop('any', 'MageSelectHandler'),
prop('any', 'AscMinusHandler'),
prop('any', 'AscPlusHandler'),
prop('any', 'JobOpts'),
prop('any', 'Jobs'),
prop('number', 'AscensionLevel', '0'),
prop('number', 'AscensionUnlocked', '0'),
prop('any', 'StartGameHandler'),
prop('string', 'SelectedClass', '""'),
prop('any', 'DrawPileHandler'),
@@ -2418,7 +2464,57 @@ function writeCodeblocks() {
prop('boolean', 'ChestOpened', 'false'),
prop('string', 'PlayerJob', '""'),
], [
method('OnBeginPlay', `self:ShowMainMenu()`),
method('OnBeginPlay', `self:ShowMainMenu()
local lp = _UserService.LocalPlayer
if lp ~= nil then
self:ReqLoadAscension(lp.PlayerComponent.UserId)
end`),
method('ReqLoadAscension', `local ds = _DataStorageService:GetUserDataStorage(userId)
local errCode, value = ds:GetAndWait("ascensionUnlocked")
local n = 0
if errCode == 0 and value ~= nil and value ~= "" then
n = tonumber(value) or 0
end
self:RecvAscension(n, userId)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'userId' }], 5),
method('RecvAscension', `self.AscensionUnlocked = n
if self.AscensionLevel > self.AscensionUnlocked then
self.AscensionLevel = self.AscensionUnlocked
end
self:RenderAscension()`, [
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'n' },
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'userId' },
], 6),
method('SaveAscension', `local ds = _DataStorageService:GetUserDataStorage(userId)
ds:SetAndWait("ascensionUnlocked", tostring(n))`, [
{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'n' },
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'userId' },
], 5),
method('AdjustAscension', `local v = self.AscensionLevel + delta
if v < 0 then v = 0 end
if v > self.AscensionUnlocked then v = self.AscensionUnlocked end
self.AscensionLevel = v
self:RenderAscension()`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'delta' }]),
method('RenderAscension', `self:SetText("/ui/DefaultGroup/MainMenu/AscLabel", "승천 " .. string.format("%d", self.AscensionLevel) .. " / 해금 " .. string.format("%d", self.AscensionUnlocked))`),
method('AscHpMult', `local m = 1
if self.AscensionLevel >= 1 then m = m + 0.1 end
if self.AscensionLevel >= 6 then m = m + 0.1 end
return m`, [], 0, 'number'),
method('AscAtkMult', `local m = 1
if self.AscensionLevel >= 2 then m = m + 0.1 end
if self.AscensionLevel >= 7 then m = m + 0.1 end
return m`, [], 0, 'number'),
method('AscEliteBonus', `local b = 0
if self.AscensionLevel >= 4 then b = b + 0.2 end
if self.AscensionLevel >= 9 then b = b + 0.2 end
return b`, [], 0, 'number'),
method('AscGoldMult', `local m = 1
if self.AscensionLevel >= 5 then m = m - 0.25 end
if self.AscensionLevel >= 10 then m = m - 0.25 end
return m`, [], 0, 'number'),
method('AscStartHpPenalty', `local p = 0
if self.AscensionLevel >= 3 then p = p + 10 end
if self.AscensionLevel >= 8 then p = p + 10 end
return p`, [], 0, 'number'),
method('HideGameHud', `self:SetEntityEnabled("/ui/DefaultGroup/Button_Attack", false)
self:SetEntityEnabled("/ui/DefaultGroup/Button_Jump", false)
self:SetEntityEnabled("/ui/DefaultGroup/UIJoystick", false)
@@ -2451,6 +2547,7 @@ elseif state == "treasure" then
self:SetEntityEnabled("/ui/DefaultGroup/TreasureHud", true)
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'state' }]),
method('ShowMainMenu', `self.SelectedClass = ""
self:RenderAscension()
self:ShowState("menu")
self:SetText("/ui/DefaultGroup/MainMenu/Title", "메이플 덱 어드벤처")
self:SetText("/ui/DefaultGroup/MainMenu/Subtitle", "캐릭터를 고르고 덱을 만들어 모험을 시작하세요")
@@ -2487,6 +2584,22 @@ if start ~= nil and start.ButtonComponent ~= nil then
self.StartGameHandler = nil
end
self.StartGameHandler = start:ConnectEvent(ButtonClickEvent, function() self:StartNewGame() end)
end
local ascMinus = _EntityService:GetEntityByPath("/ui/DefaultGroup/MainMenu/AscMinus")
if ascMinus ~= nil and ascMinus.ButtonComponent ~= nil then
if self.AscMinusHandler ~= nil then
ascMinus:DisconnectEvent(ButtonClickEvent, self.AscMinusHandler)
self.AscMinusHandler = nil
end
self.AscMinusHandler = ascMinus:ConnectEvent(ButtonClickEvent, function() self:AdjustAscension(-1) end)
end
local ascPlus = _EntityService:GetEntityByPath("/ui/DefaultGroup/MainMenu/AscPlus")
if ascPlus ~= nil and ascPlus.ButtonComponent ~= nil then
if self.AscPlusHandler ~= nil then
ascPlus:DisconnectEvent(ButtonClickEvent, self.AscPlusHandler)
self.AscPlusHandler = nil
end
self.AscPlusHandler = ascPlus:ConnectEvent(ButtonClickEvent, function() self:AdjustAscension(1) end)
end`),
method('ShowCharacterSelect', `self.SelectedClass = ""
self:ShowState("charselect")
@@ -2537,6 +2650,7 @@ else
self.PlayerMaxHp = ${CLASSES.warrior.maxHp}
self.RunDeck = { ${CARDS.starterDecks.warrior.map(luaStr).join(', ')} }
end
self.PlayerMaxHp = self.PlayerMaxHp - self:AscStartHpPenalty()
self.PlayerHp = self.PlayerMaxHp
self.Gold = 0
self.Floor = 1
@@ -2626,6 +2740,9 @@ for i = 1, #reg do
end
table.sort(list, function(a, b) return a.x < b.x end)
local mult = 1 + (self.Floor - 1) * 0.6
if g == "elite" or g == "boss" then
mult = mult + self:AscEliteBonus()
end
local n = #list
if n > ${MAX_MONSTERS} then n = ${MAX_MONSTERS} end
for i = 1, n do
@@ -2635,12 +2752,14 @@ for i = 1, n do
local intents = {}
for k = 1, #e.intents do
local v = e.intents[k].value
if e.intents[k].kind ~= "Debuff" then
if e.intents[k].kind == "Attack" then
v = math.floor(v * mult * self:AscAtkMult())
elseif e.intents[k].kind ~= "Debuff" then
v = math.floor(v * mult)
end
intents[k] = { kind = e.intents[k].kind, value = v, effect = e.intents[k].effect }
end
local maxHp = math.floor(e.maxHp * mult)
local maxHp = math.floor(e.maxHp * mult * self:AscHpMult())
self.Monsters[i] = { entity = item.entity, enemyId = item.enemyId, name = e.name,
hp = maxHp, maxHp = maxHp, block = 0, str = 0, weak = 0, vuln = 0, poison = 0,
intents = intents, intentIdx = 1, alive = true, slot = i }
@@ -3476,7 +3595,7 @@ for i = 1, #self.Monsters do
end
if anyAlive == false then
self.CombatOver = true
self.Gold = self.Gold + ${GOLD_PER_WIN}
self.Gold = self.Gold + math.floor(${GOLD_PER_WIN} * self:AscGoldMult())
self:ApplyRelics("combatEnd")
self:ApplyRelics("combatReward")
self:MaybeDropPotion()
@@ -3614,7 +3733,17 @@ 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('EndRun', `self:ShowResult(text)
method('EndRun', `local msg = text
if text == "런 클리어!" and self.AscensionLevel >= self.AscensionUnlocked and self.AscensionUnlocked < 10 then
self.AscensionUnlocked = self.AscensionUnlocked + 1
local lp = _UserService.LocalPlayer
if lp ~= nil then
self:SaveAscension(self.AscensionUnlocked, lp.PlayerComponent.UserId)
end
self:RenderAscension()
msg = "런 클리어! 승천 " .. string.format("%d", self.AscensionUnlocked) .. " 해금!"
end
self:ShowResult(msg)
self.RunActive = false
_TimerService:SetTimerOnce(function() self:ShowMainMenu() end, 4)`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'text' }]),
method('BuffsLabel', `local parts = {}
@@ -3733,7 +3862,11 @@ end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], N
self.TargetIndex = slot
self:RenderCombat()
end`, [{ Type: 'number', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'slot' }]),
method('RenderRun', `self:SetText("/ui/DefaultGroup/CombatHud/TopBar/Floor", "막 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength) .. " · " .. string.format("%d", self.Depth) .. "층")
method('RenderRun', `local floorText = "막 " .. string.format("%d", self.Floor) .. "/" .. string.format("%d", self.RunLength) .. " · " .. string.format("%d", self.Depth) .. "층"
if self.AscensionLevel > 0 then
floorText = floorText .. " · 승천" .. string.format("%d", self.AscensionLevel)
end
self:SetText("/ui/DefaultGroup/CombatHud/TopBar/Floor", floorText)
self:SetText("/ui/DefaultGroup/CombatHud/TopBar/Gold", "메소 " .. string.format("%d", self.Gold))`),
method('CardPool', `local pool = {}
for id, c in pairs(self.Cards) do
@@ -4409,7 +4542,7 @@ _TimerService:SetTimerOnce(function()
end, 0.55)`),
]);
for (const m of combat.ContentProto.Json.Methods) {
m.ExecSpace = 6;
if (m.ExecSpace === 0) m.ExecSpace = 6; // 기본은 ClientOnly(6), 서버 RPC(Server=1·Client=2) 명시값은 보존
}
writeFileSync('RootDesk/MyDesk/SlayDeckController.codeblock', JSON.stringify(combat, null, 2), 'utf8');
}

View File

@@ -287218,6 +287218,664 @@
"@version": 1
}
},
{
"id": "0e0000c3-0000-4000-8000-00000e0000c3",
"path": "/ui/DefaultGroup/MainMenu/AscMinus",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent",
"jsonString": {
"name": "AscMinus",
"path": "/ui/DefaultGroup/MainMenu/AscMinus",
"nameEditable": true,
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 5,
"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": -144,
"y": -159
},
"OffsetMin": {
"x": -196,
"y": -211
},
"Pivot": {
"x": 0.5,
"y": 0.5
},
"RectSize": {
"x": 52,
"y": 52
},
"UIMode": 1,
"UIScale": {
"x": 1,
"y": 1,
"z": 1
},
"UIVersion": 2,
"anchoredPosition": {
"x": -170,
"y": -185
},
"Position": {
"x": -170,
"y": -185,
"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": 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": 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": "0e0000c4-0000-4000-8000-00000e0000c4",
"path": "/ui/DefaultGroup/MainMenu/AscLabel",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent",
"jsonString": {
"name": "AscLabel",
"path": "/ui/DefaultGroup/MainMenu/AscLabel",
"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": 125,
"y": -165
},
"OffsetMin": {
"x": -125,
"y": -205
},
"Pivot": {
"x": 0.5,
"y": 0.5
},
"RectSize": {
"x": 250,
"y": 40
},
"UIMode": 1,
"UIScale": {
"x": 1,
"y": 1,
"z": 1
},
"UIVersion": 2,
"anchoredPosition": {
"x": 0,
"y": -185
},
"Position": {
"x": 0,
"y": -185,
"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.85,
"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": "승천 0 / 해금 0",
"UseOutLine": true,
"Enable": true
}
],
"@version": 1
}
},
{
"id": "0e0000c5-0000-4000-8000-00000e0000c5",
"path": "/ui/DefaultGroup/MainMenu/AscPlus",
"componentNames": "MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent,MOD.Core.TextComponent",
"jsonString": {
"name": "AscPlus",
"path": "/ui/DefaultGroup/MainMenu/AscPlus",
"nameEditable": true,
"enable": true,
"visible": true,
"localize": true,
"displayOrder": 7,
"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": 196,
"y": -159
},
"OffsetMin": {
"x": 144,
"y": -211
},
"Pivot": {
"x": 0.5,
"y": 0.5
},
"RectSize": {
"x": 52,
"y": 52
},
"UIMode": 1,
"UIScale": {
"x": 1,
"y": 1,
"z": 1
},
"UIVersion": 2,
"anchoredPosition": {
"x": 170,
"y": -185
},
"Position": {
"x": 170,
"y": -185,
"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": 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": 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",