import { readFileSync, writeFileSync } from 'node:fs'; // 맵 몬스터에 적 타입(EnemyId)을 부여하고, BeginPlay 시 /common 컨트롤러에 자기등록하는 마커. // 카드 전투 시 컨트롤러가 등록 목록으로 인카운터를 구성한다. const MAP_NUMBERS = Array.from({ length: 11 }, (_, i) => i + 1); // map01~11 const NAME_TO_ENEMY = { '주황버섯': 'orange_mushroom', '파란버섯': 'blue_mushroom' }; const DEFAULT_ENEMY = 'orange_mushroom'; 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() { const cb = { Id: '', GameId: '', EntryKey: 'codeblock://combatmonster', 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: 'CombatMonster', Language: 1, Name: 'CombatMonster', Type: 1, Source: 0, Target: null, Properties: [prop('string', 'EnemyId', '""'), prop('string', 'Group', '"combat"'), prop('number', 'RegTries', '0')], Methods: [ method('OnBeginPlay', `self.RegTries = 0 local eventId = 0 local function reg() self.RegTries = self.RegTries + 1 local c = _EntityService:GetEntityByPath("/common") if c ~= nil and c.SlayDeckController ~= nil then local mapName = "" if self.Entity.CurrentMapName ~= nil then mapName = self.Entity.CurrentMapName end c.SlayDeckController:RegisterMonster(self.Entity, self.EnemyId, self.Group, mapName) _TimerService:ClearTimer(eventId) elseif self.RegTries > 50 then _TimerService:ClearTimer(eventId) end end eventId = _TimerService:SetTimerRepeat(reg, 0.1)`), ], EntityEventHandlers: [], } }, }; writeFileSync('RootDesk/MyDesk/CombatMonster.codeblock', JSON.stringify(cb, null, 2) + '\n', 'utf8'); } const isMonster = (e) => typeof e.componentNames === 'string' && e.componentNames.includes('script.Monster'); function patchMap(nn) { const tag = String(nn).padStart(2, '0'); const file = `map/map${tag}.map`; const map = JSON.parse(readFileSync(file, 'utf8')); let added = 0, kept = 0; for (const e of map.ContentProto.Entities.filter(isMonster)) { const comps = e.jsonString && e.jsonString['@components']; if (!Array.isArray(comps)) { console.warn(`[gen-combat-monster] entity "${(e.jsonString && e.jsonString.name) || e.path}" has no @components — skipped`); continue; } const name = (e.jsonString && e.jsonString.name) || ''; const existing = comps.find((c) => c['@type'] === 'script.CombatMonster'); if (existing) { // 사용자가 메이커에서 설정한 값 보존 — 누락된 키만 기본값 채움 if (existing.Enable === undefined) existing.Enable = true; if (existing.EnemyId == null) existing.EnemyId = NAME_TO_ENEMY[name] || DEFAULT_ENEMY; if (existing.Group == null) existing.Group = 'combat'; kept++; } else { comps.push({ '@type': 'script.CombatMonster', Enable: true, EnemyId: NAME_TO_ENEMY[name] || DEFAULT_ENEMY, Group: 'combat' }); added++; } const names = (e.componentNames || '').split(',').filter((s) => s && s !== 'script.CombatMonster'); names.push('script.CombatMonster'); e.componentNames = names.join(','); } writeFileSync(file, JSON.stringify(map, null, 2), 'utf8'); return `map${tag}(+${added}/keep${kept})`; } writeCodeblock(); const patched = MAP_NUMBERS.map(patchMap); console.log('CombatMonster codeblock written; patched maps:', patched.join(', '));