메이커 실측으로 확인: - 이동에는 RigidbodyComponent.WalkAcceleration과 MovementComponent.InputSpeed가 둘 다 양수여야 함(WalkAccel만으론 안 걸림). LobbyMobility에 InputSpeed=5·JumpForce=5 추가. - pc.FixedLookAt은 boolean이 아니라 int32 → false→0 (빌드 에러 해소). - PlayerLock에 InputSpeed/JumpForce=0 대칭 재잠금 추가(전투맵 누설 방어). - NPC 베이스 모델 inheritance 경고는 비치명적이라 proven-good(모델 유지) 결정 주석화. 검증: 로비 이동·점프, NpcCodex 근접(d=1.10<1.2)·↑키→카드 도감, 런 시작→map01 텔레포트+이동 잠금(InputSpeed=0), 로비 복귀→이동 재해제 전부 정상. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
102 lines
4.5 KiB
JavaScript
102 lines
4.5 KiB
JavaScript
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회 등록·로비 가드)에서 처리 — 여기 두지 않음.
|
|
// 정찰: 이동에는 RigidbodyComponent.WalkAcceleration(가속)과 MovementComponent.InputSpeed(속도)가
|
|
// 둘 다 양수여야 함 — freeze-turn-player가 둘 다 0으로 만들었으므로 둘 다 복원해야 걷는다(실측 확인).
|
|
const WALK_ACCEL = 0.7;
|
|
const WALK_SPEED = 5;
|
|
const JUMP_FORCE = 5;
|
|
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 = 0
|
|
local rb = lp.RigidbodyComponent
|
|
if rb ~= nil then rb.WalkAcceleration = ${WALK_ACCEL} end
|
|
local mv = lp.MovementComponent
|
|
if mv ~= nil then
|
|
mv.InputSpeed = ${WALK_SPEED}
|
|
mv.JumpForce = ${JUMP_FORCE}
|
|
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 생성 완료');
|