feat(lobby): LobbyNpc(근접·클릭 상호작용)·LobbyMobility(이동 해제) codeblock (P15)
LobbyNpc: 거리 폴링→머리위 마크 토글, TouchEvent/UpArrow→Interact→컨트롤러 OnLobbyNpcInteract. LobbyMobility: 진입 시 pc.Enable·WalkAcceleration=0.7 복원(정찰 확정). 공격 키는 컨트롤러에서 처리. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
60
RootDesk/MyDesk/LobbyMobility.codeblock
Normal file
60
RootDesk/MyDesk/LobbyMobility.codeblock
Normal file
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://lobbymobility",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "LobbyMobility",
|
||||
"Language": 1,
|
||||
"Name": "LobbyMobility",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "number",
|
||||
"DefaultValue": "0",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "Tries"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.Tries = 0\nlocal eventId = 0\nlocal function apply()\n\tself.Tries = self.Tries + 1\n\tlocal lp = _UserService.LocalPlayer\n\tif lp ~= nil and lp.PlayerControllerComponent ~= nil then\n\t\tlocal pc = lp.PlayerControllerComponent\n\t\tpc.Enable = true\n\t\tpc.FixedLookAt = false\n\t\tlocal rb = lp.RigidbodyComponent\n\t\tif rb ~= nil then rb.WalkAcceleration = 0.7 end\n\t\t_TimerService:ClearTimer(eventId)\n\telseif self.Tries > 50 then\n\t\t_TimerService:ClearTimer(eventId)\n\tend\nend\neventId = _TimerService:SetTimerRepeat(apply, 0.1)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
89
RootDesk/MyDesk/LobbyNpc.codeblock
Normal file
89
RootDesk/MyDesk/LobbyNpc.codeblock
Normal file
@@ -0,0 +1,89 @@
|
||||
{
|
||||
"Id": "",
|
||||
"GameId": "",
|
||||
"EntryKey": "codeblock://lobbynpc",
|
||||
"ContentType": "x-mod/codeblock",
|
||||
"Content": "",
|
||||
"Usage": 0,
|
||||
"UsePublish": 1,
|
||||
"UseService": 0,
|
||||
"CoreVersion": "26.5.0.0",
|
||||
"StudioVersion": "",
|
||||
"DynamicLoading": 0,
|
||||
"ContentProto": {
|
||||
"Use": "Json",
|
||||
"Json": {
|
||||
"CoreVersion": {
|
||||
"Major": 0,
|
||||
"Minor": 2
|
||||
},
|
||||
"ScriptVersion": {
|
||||
"Major": 1,
|
||||
"Minor": 0
|
||||
},
|
||||
"Description": "",
|
||||
"Id": "LobbyNpc",
|
||||
"Language": 1,
|
||||
"Name": "LobbyNpc",
|
||||
"Type": 1,
|
||||
"Source": 0,
|
||||
"Target": null,
|
||||
"Properties": [
|
||||
{
|
||||
"Type": "string",
|
||||
"DefaultValue": "\"\"",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "NpcId"
|
||||
},
|
||||
{
|
||||
"Type": "string",
|
||||
"DefaultValue": "\"\"",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "MarkName"
|
||||
},
|
||||
{
|
||||
"Type": "boolean",
|
||||
"DefaultValue": "false",
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": "InRange"
|
||||
}
|
||||
],
|
||||
"Methods": [
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "self.InRange = false\nlocal mark = _EntityService:GetEntityByPath(\"/maps/lobby/\" .. self.MarkName)\nif mark ~= nil then mark:SetVisible(false) end\nself.Entity:ConnectEvent(TouchEvent, function(e)\n\tself:Interact()\nend)\n_InputService:ConnectEvent(KeyDownEvent, function(e)\n\tif self.InRange and e.key == KeyboardKey.UpArrow then\n\t\tself:Interact()\n\tend\nend)\nlocal eventId = 0\nlocal function tick()\n\tlocal lp = _UserService.LocalPlayer\n\tif lp == nil then return end\n\tif mark == nil then mark = _EntityService:GetEntityByPath(\"/maps/lobby/\" .. self.MarkName) end\n\tlocal a = lp.TransformComponent.WorldPosition\n\tlocal b = self.Entity.TransformComponent.WorldPosition\n\tlocal d = Vector2.Distance(Vector2(a.x, a.y), Vector2(b.x, b.y))\n\tlocal near = d < 1.2\n\tif near ~= self.InRange then\n\t\tself.InRange = near\n\t\tif mark ~= nil then mark:SetVisible(near) end\n\tend\nend\neventId = _TimerService:SetTimerRepeat(tick, 0.15)",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "OnBeginPlay"
|
||||
},
|
||||
{
|
||||
"Return": {
|
||||
"Type": "void",
|
||||
"DefaultValue": null,
|
||||
"SyncDirection": 0,
|
||||
"Attributes": [],
|
||||
"Name": null
|
||||
},
|
||||
"Arguments": [],
|
||||
"Code": "local c = _EntityService:GetEntityByPath(\"/common\")\nif c ~= nil and c.SlayDeckController ~= nil then\n\tc.SlayDeckController:OnLobbyNpcInteract(self.NpcId)\nend",
|
||||
"Scope": 2,
|
||||
"ExecSpace": 6,
|
||||
"Attributes": [],
|
||||
"Name": "Interact"
|
||||
}
|
||||
],
|
||||
"EntityEventHandlers": []
|
||||
}
|
||||
}
|
||||
}
|
||||
92
tools/player/gen-lobby-npc.mjs
Normal file
92
tools/player/gen-lobby-npc.mjs
Normal file
@@ -0,0 +1,92 @@
|
||||
import { writeFileSync } from 'node:fs';
|
||||
|
||||
// 로비 codeblock 2종 emit (맵/엔티티 부착은 tools/map/gen-lobby-map.mjs 소관):
|
||||
// · LobbyNpc — NPC 엔티티에 부착. 근접 폴링→머리위 마크 토글, TouchEvent(클릭)/UpArrow(근접)→Interact→컨트롤러 호출.
|
||||
// · LobbyMobility — 로비 맵 루트에 부착. 진입 시 플레이어 이동 잠금 해제(정찰 확정: WalkAcceleration이 진짜 레버).
|
||||
// 공격 키(LeftControl) 바인딩은 SlayDeckController(/common, 1회 등록·로비 가드)에서 처리 — 여기 두지 않음.
|
||||
const WALK_ACCEL = 0.7; // 정찰: freeze가 0으로 만든 RigidbodyComponent.WalkAcceleration 복원값(이동·점프 정상 확인)
|
||||
const PROX = 1.2; // 근접 임계(맵 NPC 간격 2.5와 분리)
|
||||
|
||||
function prop(Type, Name, DefaultValue = 'nil') {
|
||||
return { Type, DefaultValue, SyncDirection: 0, Attributes: [], Name };
|
||||
}
|
||||
function method(Name, Code, Arguments = [], ExecSpace = 6) {
|
||||
return {
|
||||
Return: { Type: 'void', DefaultValue: null, SyncDirection: 0, Attributes: [], Name: null },
|
||||
Arguments, Code, Scope: 2, ExecSpace, Attributes: [], Name,
|
||||
};
|
||||
}
|
||||
function writeCodeblock(name, properties, methods) {
|
||||
const cb = {
|
||||
Id: '', GameId: '', EntryKey: `codeblock://${name.toLowerCase()}`, ContentType: 'x-mod/codeblock',
|
||||
Content: '', Usage: 0, UsePublish: 1, UseService: 0, CoreVersion: '26.5.0.0', StudioVersion: '', DynamicLoading: 0,
|
||||
ContentProto: { Use: 'Json', Json: {
|
||||
CoreVersion: { Major: 0, Minor: 2 }, ScriptVersion: { Major: 1, Minor: 0 },
|
||||
Description: '', Id: name, Language: 1, Name: name, Type: 1, Source: 0, Target: null,
|
||||
Properties: properties, Methods: methods, EntityEventHandlers: [],
|
||||
} },
|
||||
};
|
||||
writeFileSync(`RootDesk/MyDesk/${name}.codeblock`, JSON.stringify(cb, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
// ── LobbyNpc ──────────────────────────────────────────────────────────────
|
||||
const npcInteract = method('Interact', `local c = _EntityService:GetEntityByPath("/common")
|
||||
if c ~= nil and c.SlayDeckController ~= nil then
|
||||
c.SlayDeckController:OnLobbyNpcInteract(self.NpcId)
|
||||
end`);
|
||||
|
||||
const npcBegin = method('OnBeginPlay', `self.InRange = false
|
||||
local mark = _EntityService:GetEntityByPath("/maps/lobby/" .. self.MarkName)
|
||||
if mark ~= nil then mark:SetVisible(false) end
|
||||
self.Entity:ConnectEvent(TouchEvent, function(e)
|
||||
self:Interact()
|
||||
end)
|
||||
_InputService:ConnectEvent(KeyDownEvent, function(e)
|
||||
if self.InRange and e.key == KeyboardKey.UpArrow then
|
||||
self:Interact()
|
||||
end
|
||||
end)
|
||||
local eventId = 0
|
||||
local function tick()
|
||||
local lp = _UserService.LocalPlayer
|
||||
if lp == nil then return end
|
||||
if mark == nil then mark = _EntityService:GetEntityByPath("/maps/lobby/" .. self.MarkName) end
|
||||
local a = lp.TransformComponent.WorldPosition
|
||||
local b = self.Entity.TransformComponent.WorldPosition
|
||||
local d = Vector2.Distance(Vector2(a.x, a.y), Vector2(b.x, b.y))
|
||||
local near = d < ${PROX}
|
||||
if near ~= self.InRange then
|
||||
self.InRange = near
|
||||
if mark ~= nil then mark:SetVisible(near) end
|
||||
end
|
||||
end
|
||||
eventId = _TimerService:SetTimerRepeat(tick, 0.15)`);
|
||||
|
||||
writeCodeblock('LobbyNpc', [
|
||||
prop('string', 'NpcId', '""'),
|
||||
prop('string', 'MarkName', '""'),
|
||||
prop('boolean', 'InRange', 'false'),
|
||||
], [npcBegin, npcInteract]);
|
||||
|
||||
// ── LobbyMobility ─────────────────────────────────────────────────────────
|
||||
const mobBegin = method('OnBeginPlay', `self.Tries = 0
|
||||
local eventId = 0
|
||||
local function apply()
|
||||
self.Tries = self.Tries + 1
|
||||
local lp = _UserService.LocalPlayer
|
||||
if lp ~= nil and lp.PlayerControllerComponent ~= nil then
|
||||
local pc = lp.PlayerControllerComponent
|
||||
pc.Enable = true
|
||||
pc.FixedLookAt = false
|
||||
local rb = lp.RigidbodyComponent
|
||||
if rb ~= nil then rb.WalkAcceleration = ${WALK_ACCEL} end
|
||||
_TimerService:ClearTimer(eventId)
|
||||
elseif self.Tries > 50 then
|
||||
_TimerService:ClearTimer(eventId)
|
||||
end
|
||||
end
|
||||
eventId = _TimerService:SetTimerRepeat(apply, 0.1)`);
|
||||
|
||||
writeCodeblock('LobbyMobility', [prop('number', 'Tries', '0')], [mobBegin]);
|
||||
|
||||
console.log('[gen-lobby-npc] LobbyNpc / LobbyMobility codeblock 생성 완료');
|
||||
Reference in New Issue
Block a user