feat: P15 — 로비를 전용 맵 + 월드 NPC로 (근접·클릭 상호작용, 로비 한정 이동·공격) #54
File diff suppressed because one or more lines are too long
@@ -2425,17 +2425,17 @@ function upsertUi() {
|
|||||||
displayOrder: 11,
|
displayOrder: 11,
|
||||||
components: [
|
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 }),
|
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.07, g: 0.08, b: 0.13, a: 1 }, type: 1, raycast: true }),
|
// 로비가 물리 맵이 됨 — 투명 + 비레이캐스트로 맵을 가리지 않고 월드 NPC 클릭이 통과되게 함.
|
||||||
|
sprite({ color: TRANSPARENT, type: 1, raycast: false }),
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
lobbyRoot.jsonString.enable = false;
|
lobbyRoot.jsonString.enable = false;
|
||||||
lobby.push(lobbyRoot);
|
lobby.push(lobbyRoot);
|
||||||
const lobTexts = [
|
const lobTexts = [
|
||||||
['Title', 0, 470, 760, '메이플 로비', 48, GOLD],
|
['Title', 0, 478, 760, '메이플 로비', 40, GOLD],
|
||||||
['Subtitle', 0, 408, 900, 'NPC를 클릭해 런을 준비하세요', 24, { r: 0.82, g: 0.86, b: 0.92, a: 1 }],
|
['SoulLabel', 700, 478, 320, '영혼 0', 28, { r: 0.6, g: 0.85, b: 1, a: 1 }],
|
||||||
['SoulLabel', 700, 470, 320, '영혼 0', 28, { r: 0.6, g: 0.85, b: 1, a: 1 }],
|
['AscLabel', -560, 478, 380, '승천 0 / 해금 0', 22, { r: 0.9, g: 0.7, b: 0.5, a: 1 }],
|
||||||
['AscLabel', -560, 470, 380, '승천 0 / 해금 0', 22, { r: 0.9, g: 0.7, b: 0.5, a: 1 }],
|
['Hint', 0, -478, 1500, 'NPC에게 다가가 ↑ 또는 클릭으로 대화 · ← → 이동 · Ctrl 공격', 20, { r: 0.72, g: 0.76, b: 0.82, a: 1 }],
|
||||||
['Hint', 0, -430, 1300, '런을 클리어하거나 패배하면 로비로 돌아옵니다', 20, { r: 0.6, g: 0.64, b: 0.7, a: 1 }],
|
|
||||||
];
|
];
|
||||||
for (const [suffix, x, y, w, value, fs, color] of lobTexts) {
|
for (const [suffix, x, y, w, value, fs, color] of lobTexts) {
|
||||||
lobby.push(entity({
|
lobby.push(entity({
|
||||||
@@ -2466,62 +2466,7 @@ function upsertUi() {
|
|||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
const npcs = [
|
// NPC 4종은 로비 물리 맵의 월드 엔티티(map/lobby.map + LobbyNpc codeblock)로 이동. UI 버튼 행 제거.
|
||||||
{ key: 'NpcRun', x: -540, name: '모험가', role: '런 시작', tint: { r: 0.74, g: 0.32, b: 0.28, a: 1 } },
|
|
||||||
{ key: 'NpcCodex', x: -180, name: '사서', role: '카드 도감', tint: { r: 0.3, g: 0.55, b: 0.5, a: 1 } },
|
|
||||||
{ key: 'NpcShop', x: 180, name: '상인', role: '영혼 상점', tint: { r: 0.55, g: 0.45, b: 0.75, a: 1 } },
|
|
||||||
{ key: 'NpcBoard', x: 540, name: '안내원', role: '게시판', tint: { r: 0.4, g: 0.5, b: 0.7, a: 1 } },
|
|
||||||
];
|
|
||||||
for (const npc of npcs) {
|
|
||||||
const base = `/ui/DefaultGroup/LobbyHud/${npc.key}`;
|
|
||||||
lobby.push(entity({
|
|
||||||
id: guid('lob', lobId++),
|
|
||||||
path: base,
|
|
||||||
modelId: 'uibutton', entryId: 'UIButton',
|
|
||||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.ButtonComponent',
|
|
||||||
displayOrder: 4,
|
|
||||||
components: [
|
|
||||||
transform({ parentW: 1920, parentH: 1080, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 240, y: 320 }, pos: { x: npc.x, y: 20 }, align: ALIGN_CENTER }),
|
|
||||||
sprite({ color: { r: 0.13, g: 0.15, b: 0.2, a: 1 }, type: 1, raycast: true }),
|
|
||||||
button(),
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
lobby.push(entity({
|
|
||||||
id: guid('lob', lobId++),
|
|
||||||
path: `${base}/Figure`,
|
|
||||||
modelId: 'uisprite', entryId: 'UISprite',
|
|
||||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent',
|
|
||||||
displayOrder: 1,
|
|
||||||
components: [
|
|
||||||
transform({ parentW: 240, parentH: 320, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 130, y: 130 }, pos: { x: 0, y: 30 } }),
|
|
||||||
sprite({ color: npc.tint, type: 1 }),
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
lobby.push(entity({
|
|
||||||
id: guid('lob', lobId++),
|
|
||||||
path: `${base}/Name`,
|
|
||||||
modelId: 'uitext', entryId: 'UIText',
|
|
||||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
|
||||||
displayOrder: 2,
|
|
||||||
components: [
|
|
||||||
transform({ parentW: 240, parentH: 320, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 220, y: 40 }, pos: { x: 0, y: 122 } }),
|
|
||||||
sprite({ color: TRANSPARENT }),
|
|
||||||
text({ value: npc.name, fontSize: 28, bold: true, color: GOLD, alignment: 4 }),
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
lobby.push(entity({
|
|
||||||
id: guid('lob', lobId++),
|
|
||||||
path: `${base}/Role`,
|
|
||||||
modelId: 'uitext', entryId: 'UIText',
|
|
||||||
componentNames: 'MOD.Core.UITransformComponent,MOD.Core.SpriteGUIRendererComponent,MOD.Core.TextComponent',
|
|
||||||
displayOrder: 2,
|
|
||||||
components: [
|
|
||||||
transform({ parentW: 240, parentH: 320, anchor: { x: 0.5, y: 0.5 }, pivot: { x: 0.5, y: 0.5 }, size: { x: 220, y: 36 }, pos: { x: 0, y: -122 } }),
|
|
||||||
sprite({ color: TRANSPARENT }),
|
|
||||||
text({ value: npc.role, fontSize: 22, bold: false, color: { r: 0.86, g: 0.9, b: 0.94, a: 1 }, alignment: 4 }),
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
emit('LobbyHud', lobby);
|
emit('LobbyHud', lobby);
|
||||||
|
|
||||||
// ── BoardHud — 게시판(공지/팁) ──
|
// ── BoardHud — 게시판(공지/팁) ──
|
||||||
@@ -2743,6 +2688,8 @@ function writeCodeblocks() {
|
|||||||
const RELIC_PRICE = 60;
|
const RELIC_PRICE = 60;
|
||||||
const ACT_COUNT = 5;
|
const ACT_COUNT = 5;
|
||||||
const ACT_MAPS = ['map01', 'map02', 'map03', 'map04', 'map05'];
|
const ACT_MAPS = ['map01', 'map02', 'map03', 'map04', 'map05'];
|
||||||
|
const LOBBY_MAP = 'lobby';
|
||||||
|
const LOBBY_SPAWN = 'Vector3(-5, 0.03, 0)'; // 정찰: map01 지면 좌측
|
||||||
const combat = codeblock('SlayDeckController', 'SlayDeckController', [
|
const combat = codeblock('SlayDeckController', 'SlayDeckController', [
|
||||||
prop('any', 'DrawPile'),
|
prop('any', 'DrawPile'),
|
||||||
prop('any', 'DiscardPile'),
|
prop('any', 'DiscardPile'),
|
||||||
@@ -2771,6 +2718,7 @@ function writeCodeblocks() {
|
|||||||
prop('any', 'AllDeckCloseHandler'),
|
prop('any', 'AllDeckCloseHandler'),
|
||||||
prop('number', 'SoulPoints', '0'),
|
prop('number', 'SoulPoints', '0'),
|
||||||
prop('boolean', 'LobbyBound', 'false'),
|
prop('boolean', 'LobbyBound', 'false'),
|
||||||
|
prop('number', 'LobbyTpTries', '0'),
|
||||||
prop('boolean', 'CodexMode', 'false'),
|
prop('boolean', 'CodexMode', 'false'),
|
||||||
prop('any', 'CodexCards'),
|
prop('any', 'CodexCards'),
|
||||||
prop('any', 'SoulUnlocks'),
|
prop('any', 'SoulUnlocks'),
|
||||||
@@ -2837,7 +2785,15 @@ local lp = _UserService.LocalPlayer
|
|||||||
if lp ~= nil then
|
if lp ~= nil then
|
||||||
self:ReqLoadAscension(lp.PlayerComponent.UserId)
|
self:ReqLoadAscension(lp.PlayerComponent.UserId)
|
||||||
self:ReqLoadSouls(lp.PlayerComponent.UserId)
|
self:ReqLoadSouls(lp.PlayerComponent.UserId)
|
||||||
end`),
|
end
|
||||||
|
_InputService:ConnectEvent(KeyDownEvent, function(e)
|
||||||
|
if e.key == KeyboardKey.LeftControl then
|
||||||
|
local lp2 = _UserService.LocalPlayer
|
||||||
|
if lp2 ~= nil and lp2.CurrentMapName == "${LOBBY_MAP}" and self.RunActive ~= true then
|
||||||
|
self:PlayerAttackMotion()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)`),
|
||||||
method('ReqLoadAscension', `local ds = _DataStorageService:GetUserDataStorage(userId)
|
method('ReqLoadAscension', `local ds = _DataStorageService:GetUserDataStorage(userId)
|
||||||
local errCode, value = ds:GetAndWait("ascensionUnlocked")
|
local errCode, value = ds:GetAndWait("ascensionUnlocked")
|
||||||
local n = 0
|
local n = 0
|
||||||
@@ -2990,7 +2946,35 @@ self:ShowState("lobby")
|
|||||||
self:SetEntityEnabled("/ui/DefaultGroup/BoardHud", false)
|
self:SetEntityEnabled("/ui/DefaultGroup/BoardHud", false)
|
||||||
self:SetEntityEnabled("/ui/DefaultGroup/SoulShopHud", false)
|
self:SetEntityEnabled("/ui/DefaultGroup/SoulShopHud", false)
|
||||||
self:BindLobbyButtons()
|
self:BindLobbyButtons()
|
||||||
self:BindMenuButtons()`),
|
self:BindMenuButtons()
|
||||||
|
self:GoLobbyMap()`),
|
||||||
|
method('GoLobbyMap', `self.LobbyTpTries = 0
|
||||||
|
local eventId = 0
|
||||||
|
local function go()
|
||||||
|
self.LobbyTpTries = self.LobbyTpTries + 1
|
||||||
|
local lp = _UserService.LocalPlayer
|
||||||
|
if lp ~= nil then
|
||||||
|
if lp.CurrentMapName ~= "${LOBBY_MAP}" then
|
||||||
|
_TeleportService:TeleportToMapPosition(lp, ${LOBBY_SPAWN}, "${LOBBY_MAP}")
|
||||||
|
end
|
||||||
|
_TimerService:ClearTimer(eventId)
|
||||||
|
elseif self.LobbyTpTries > 50 then
|
||||||
|
_TimerService:ClearTimer(eventId)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
eventId = _TimerService:SetTimerRepeat(go, 0.1)`),
|
||||||
|
method('OnLobbyNpcInteract', `if self.RunActive == true then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if id == "run" then
|
||||||
|
self:ShowCharacterSelect()
|
||||||
|
elseif id == "codex" then
|
||||||
|
self:ShowCodex()
|
||||||
|
elseif id == "shop" then
|
||||||
|
self:ShowSoulShop()
|
||||||
|
elseif id == "board" then
|
||||||
|
self:ShowBoard()
|
||||||
|
end`, [{ Type: 'string', DefaultValue: '""', SyncDirection: 0, Attributes: [], Name: 'id' }]),
|
||||||
method('RenderSoulLabel', `local s = self.SoulPoints or 0
|
method('RenderSoulLabel', `local s = self.SoulPoints or 0
|
||||||
self:SetText("/ui/DefaultGroup/LobbyHud/SoulLabel", "영혼 " .. string.format("%d", s))
|
self:SetText("/ui/DefaultGroup/LobbyHud/SoulLabel", "영혼 " .. string.format("%d", s))
|
||||||
self:SetText("/ui/DefaultGroup/SoulShopHud/Souls", "영혼 " .. string.format("%d", s))`),
|
self:SetText("/ui/DefaultGroup/SoulShopHud/Souls", "영혼 " .. string.format("%d", s))`),
|
||||||
@@ -3004,10 +2988,6 @@ local function bindClick(path, fn)
|
|||||||
e:ConnectEvent(ButtonClickEvent, fn)
|
e:ConnectEvent(ButtonClickEvent, fn)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
bindClick("/ui/DefaultGroup/LobbyHud/NpcRun", function() self:ShowCharacterSelect() end)
|
|
||||||
bindClick("/ui/DefaultGroup/LobbyHud/NpcCodex", function() self:ShowCodex() end)
|
|
||||||
bindClick("/ui/DefaultGroup/LobbyHud/NpcShop", function() self:ShowSoulShop() end)
|
|
||||||
bindClick("/ui/DefaultGroup/LobbyHud/NpcBoard", function() self:ShowBoard() end)
|
|
||||||
bindClick("/ui/DefaultGroup/LobbyHud/AscMinus", function() self:AdjustAscension(-1) end)
|
bindClick("/ui/DefaultGroup/LobbyHud/AscMinus", function() self:AdjustAscension(-1) end)
|
||||||
bindClick("/ui/DefaultGroup/LobbyHud/AscPlus", function() self:AdjustAscension(1) end)
|
bindClick("/ui/DefaultGroup/LobbyHud/AscPlus", function() self:AdjustAscension(1) end)
|
||||||
bindClick("/ui/DefaultGroup/BoardHud/Close", function() self:CloseBoard() end)
|
bindClick("/ui/DefaultGroup/BoardHud/Close", function() self:CloseBoard() end)
|
||||||
@@ -3229,6 +3209,7 @@ self:BindButtons()
|
|||||||
self:AddRelic("${RELICS.startingRelic}")
|
self:AddRelic("${RELICS.startingRelic}")
|
||||||
self:ApplySoulUnlocks()
|
self:ApplySoulUnlocks()
|
||||||
self:RenderPotions()
|
self:RenderPotions()
|
||||||
|
self:TeleportToActMap()
|
||||||
self:ShowMap()`),
|
self:ShowMap()`),
|
||||||
method('StartCombat', `self:ShowState("combat")
|
method('StartCombat', `self:ShowState("combat")
|
||||||
self:SetEntityEnabled("/ui/DefaultGroup/CombatHud/Result", false)
|
self:SetEntityEnabled("/ui/DefaultGroup/CombatHud/Result", false)
|
||||||
|
|||||||
3076
ui/DefaultGroup.ui
3076
ui/DefaultGroup.ui
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user