사용자 메이커 저작 버튼들이 ButtonComponent 없는 단순 스프라이트라 BindXxx가
ButtonComponent ~= nil 조건에서 스킵 → 어떤 버튼도 클릭 안 됨(시작 화면 포함).
바인드 조건 41곳의 `X.ButtonComponent ~= nil`을
`(X.ButtonComponent ~= nil or X:AddComponent("ButtonComponent") ~= nil)`로 바꿔
없으면 런타임 부착 후 통과(있으면 short-circuit). Entity:AddComponent(ControlOnly) 실측 확인.
.ui 무수정(연결만). 메뉴·로비·charselect·전투·상점·덱·맵 버튼 전부 일괄 적용.
검증(플레이테스트): 부트 후 NewGame/Start/Warrior 핸들러 바인딩 완료·버튼 ButtonComponent
부착 확인. 메뉴 상태서 타 UIGroup 활성 자식 0(레이캐스트 블로커 없음). 실제 클릭은 사용자 확인.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
231 lines
7.9 KiB
JavaScript
231 lines
7.9 KiB
JavaScript
import { method, RUN_LENGTH, GOLD_PER_WIN, CARD_PRICE, REST_HEAL, RELIC_PRICE, ACT_COUNT, ACT_MAPS, LOBBY_MAP, LOBBY_SPAWN } from '../lib/codeblock.mjs';
|
|
import { CARDS, ENEMIES, CLASSES, JOBS, SOUL_UNLOCKS, CARDFRAMES, RARITIES, MAP_ROWS, MAP_COLS, CHEST_CLOSED_RUID, CHEST_OPEN_RUID, NODEICONS, CHARS, CAM, RELICS, POTIONS, luaSoulShopTable, frameRuid, luaFramesTable, luaNodeIconsTable, luaRelicsTable, luaPotionsTable, luaIntentsArray, luaEnemiesTable, luaStr, luaJobsTable, luaCardsTable, luaDeckTable } from '../lib/data.mjs';
|
|
import { UI_FILE, COMMON_FILE, UI_ROOT, GENERATED_UI_SECTIONS, UI_APPEND_ORDER, DISABLED_STOCK_CONTROLS, TRANSPARENT, DARK, GOLD, ATTACK, DEFEND, SKILL, DAMAGE_DIGIT_RUIDS, DAMAGE_POP_MAX_DIGITS, DAMAGE_POP_DIGIT_W, DAMAGE_POP_DIGIT_H, DAMAGE_POP_DIGIT_SPACING, MAX_MONSTERS, HEAD_OFFSET_Y, HP_BAR_W, WHITE, CARD_NAME_TEXT, CARD_DESC_TEXT, cardFaceLayout, CARD_W, CARD_H, CARD_SPACING, CARD_XS, ALIGN_CENTER, ALIGN_BOTTOM_CENTER, guid, transform, sprite, button, text, scrollLayoutGroup, popupLayerFor, uiOrderFor, displayOrderFor, applySortingOverride, entity, uiPath, sectionRoot, isGeneratedUiEntity, appendUiSection } from '../lib/ui-helpers.mjs';
|
|
|
|
export const mapMethods = [
|
|
method('ShowMap', `self:ShowState("map")
|
|
self:RenderMap()`),
|
|
method('GenerateMap', `-- 절차 생성 — tools/map/rogue-map.mjs(JS 미러)와 로직 동기화 유지
|
|
self.MapNodes = {}
|
|
self.MapStart = {}
|
|
self.VisitedNodes = {}
|
|
self.Depth = 0
|
|
self.MapNodes["boss"] = { type = "boss", row = ${MAP_ROWS} + 1, col = 0, next = {} }
|
|
local cols = { 1, 2, 3, 4 }
|
|
for i = #cols, 2, -1 do
|
|
local j = math.random(1, i)
|
|
cols[i], cols[j] = cols[j], cols[i]
|
|
end
|
|
local starts = { cols[1], cols[2], math.random(1, ${MAP_COLS}), math.random(1, ${MAP_COLS}) }
|
|
for p = 1, 4 do
|
|
local c = starts[p]
|
|
local sid = "r1c" .. tostring(c)
|
|
if self.MapNodes[sid] == nil then
|
|
self.MapNodes[sid] = { type = "combat", row = 1, col = c, next = {} }
|
|
end
|
|
local found = false
|
|
for i = 1, #self.MapStart do
|
|
if self.MapStart[i] == sid then found = true end
|
|
end
|
|
if found == false then
|
|
table.insert(self.MapStart, sid)
|
|
end
|
|
for r = 1, ${MAP_ROWS} - 1 do
|
|
local nc = c + math.random(-1, 1)
|
|
if nc < 1 then nc = 1 end
|
|
if nc > ${MAP_COLS} then nc = ${MAP_COLS} end
|
|
local nid = "r" .. tostring(r + 1) .. "c" .. tostring(nc)
|
|
if self.MapNodes[nid] == nil then
|
|
self.MapNodes[nid] = { type = "combat", row = r + 1, col = nc, next = {} }
|
|
end
|
|
local fid = "r" .. tostring(r) .. "c" .. tostring(c)
|
|
local dup = false
|
|
for i = 1, #self.MapNodes[fid].next do
|
|
if self.MapNodes[fid].next[i] == nid then dup = true end
|
|
end
|
|
if dup == false then
|
|
table.insert(self.MapNodes[fid].next, nid)
|
|
end
|
|
c = nc
|
|
end
|
|
local lid = "r" .. tostring(${MAP_ROWS}) .. "c" .. tostring(c)
|
|
local bdup = false
|
|
for i = 1, #self.MapNodes[lid].next do
|
|
if self.MapNodes[lid].next[i] == "boss" then bdup = true end
|
|
end
|
|
if bdup == false then
|
|
table.insert(self.MapNodes[lid].next, "boss")
|
|
end
|
|
end
|
|
for r = 3, ${MAP_ROWS} do
|
|
for c = 1, ${MAP_COLS} do
|
|
local id = "r" .. tostring(r) .. "c" .. tostring(c)
|
|
local node = self.MapNodes[id]
|
|
if node ~= nil then
|
|
-- 부모 노드 타입 수집 (rest/shop/elite 는 부모와 같은 타입 연속 금지)
|
|
local parentTypes = {}
|
|
for pid, pn in pairs(self.MapNodes) do
|
|
if pn.row == r - 1 then
|
|
for i = 1, #pn.next do
|
|
if pn.next[i] == id then parentTypes[pn.type] = true end
|
|
end
|
|
end
|
|
end
|
|
local w
|
|
if r == ${MAP_ROWS} then
|
|
w = { { "rest", 50 }, { "combat", 25 }, { "shop", 10 }, { "elite", 8 }, { "treasure", 7 } }
|
|
elseif r >= 4 then
|
|
w = { { "combat", 45 }, { "elite", 16 }, { "shop", 12 }, { "rest", 12 }, { "treasure", 15 } }
|
|
else
|
|
w = { { "combat", 45 }, { "shop", 12 }, { "rest", 12 } }
|
|
end
|
|
local total = 0
|
|
for i = 1, #w do
|
|
local t = w[i][1]
|
|
if (t == "elite" or t == "rest" or t == "shop") and parentTypes[t] == true then
|
|
w[i][2] = 0
|
|
end
|
|
total = total + w[i][2]
|
|
end
|
|
local roll = math.random() * total
|
|
local acc = 0
|
|
for i = 1, #w do
|
|
acc = acc + w[i][2]
|
|
if roll <= acc then
|
|
node.type = w[i][1]
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
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('RenderMapNode', `local base = "/ui/RunUIGroup/MapHud/Node_" .. id
|
|
local e = _EntityService:GetEntityByPath(base)
|
|
if e == nil then
|
|
return
|
|
end
|
|
local node = self.MapNodes[id]
|
|
if node == nil then
|
|
e.Enable = false
|
|
return
|
|
end
|
|
e.Enable = true
|
|
local ruid = self.NodeIcons[node.type]
|
|
if ruid == nil then
|
|
ruid = self.NodeIcons["combat"]
|
|
end
|
|
if e.SpriteGUIRendererComponent ~= nil and ruid ~= nil then
|
|
e.SpriteGUIRendererComponent.ImageRUID = ruid
|
|
end
|
|
local reachable = self:IsReachable(id)
|
|
local visited = false
|
|
if self.VisitedNodes ~= nil then
|
|
for i = 1, #self.VisitedNodes do
|
|
if self.VisitedNodes[i] == id then visited = true end
|
|
end
|
|
end
|
|
if e.SpriteGUIRendererComponent ~= nil then
|
|
if id == self.CurrentNodeId then
|
|
e.SpriteGUIRendererComponent.Color = Color(1, 0.82, 0.3, 1)
|
|
elseif visited == true then
|
|
e.SpriteGUIRendererComponent.Color = Color(0.5, 0.5, 0.55, 0.9)
|
|
elseif reachable == true then
|
|
e.SpriteGUIRendererComponent.Color = Color(1, 1, 1, 1)
|
|
else
|
|
e.SpriteGUIRendererComponent.Color = Color(0.68, 0.68, 0.72, 0.85)
|
|
end
|
|
end
|
|
if (e.ButtonComponent ~= nil or e:AddComponent("ButtonComponent") ~= nil) then
|
|
e.ButtonComponent.Enable = reachable
|
|
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
|
method('RenderMapDots', `local node = self.MapNodes[fromId]
|
|
local has = false
|
|
if node ~= nil then
|
|
for i = 1, #node.next do
|
|
if node.next[i] == toId then has = true end
|
|
end
|
|
end
|
|
for k = 1, 3 do
|
|
local d = _EntityService:GetEntityByPath("/ui/RunUIGroup/MapHud/Dot_" .. dotId .. "_" .. tostring(k))
|
|
if d ~= nil then
|
|
d.Enable = has
|
|
if has == true and d.SpriteGUIRendererComponent ~= nil then
|
|
if fromId == self.CurrentNodeId then
|
|
d.SpriteGUIRendererComponent.Color = Color(0.95, 0.8, 0.3, 1)
|
|
else
|
|
d.SpriteGUIRendererComponent.Color = Color(0.5, 0.5, 0.55, 0.8)
|
|
end
|
|
end
|
|
end
|
|
end`, [
|
|
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'dotId' },
|
|
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'fromId' },
|
|
{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'toId' },
|
|
]),
|
|
method('RenderMap', `for r = 1, ${MAP_ROWS} do
|
|
for c = 1, ${MAP_COLS} do
|
|
self:RenderMapNode("r" .. tostring(r) .. "c" .. tostring(c))
|
|
end
|
|
end
|
|
self:RenderMapNode("boss")
|
|
for r = 1, ${MAP_ROWS} - 1 do
|
|
for c = 1, ${MAP_COLS} do
|
|
local fid = "r" .. tostring(r) .. "c" .. tostring(c)
|
|
for c2 = c - 1, c + 1 do
|
|
if c2 >= 1 and c2 <= ${MAP_COLS} then
|
|
self:RenderMapDots(fid .. "_" .. tostring(c2), fid, "r" .. tostring(r + 1) .. "c" .. tostring(c2))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for c = 1, ${MAP_COLS} do
|
|
local fid = "r" .. tostring(${MAP_ROWS}) .. "c" .. tostring(c)
|
|
self:RenderMapDots(fid .. "_b", fid, "boss")
|
|
end
|
|
`),
|
|
method('PickNode', `if self.RunActive ~= true then
|
|
return
|
|
end
|
|
if self:IsReachable(id) ~= true then
|
|
return
|
|
end
|
|
self.CurrentNodeId = id
|
|
if self.VisitedNodes == nil then
|
|
self.VisitedNodes = {}
|
|
end
|
|
table.insert(self.VisitedNodes, id)
|
|
local hud = _EntityService:GetEntityByPath("/ui/RunUIGroup/MapHud")
|
|
if hud ~= nil then
|
|
hud.Enable = false
|
|
end
|
|
local node = self.MapNodes[id]
|
|
self.Depth = node.row
|
|
self:RenderRun()
|
|
if node.type == "shop" then
|
|
self:ShowShop()
|
|
elseif node.type == "rest" then
|
|
self:ShowRest()
|
|
elseif node.type == "treasure" then
|
|
self:ShowTreasure()
|
|
else
|
|
self.CurrentEnemyId = ""
|
|
self:StartCombat()
|
|
end`, [{ Type: 'string', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
|
];
|