From bce13fc78849acebc5af23da84da4c5282e89913 Mon Sep 17 00:00:00 2001 From: gahusb Date: Sat, 6 Jun 2026 14:07:10 +0900 Subject: [PATCH] =?UTF-8?q?=EB=A7=B5=20=EC=83=9D=EC=84=B1=EA=B8=B0:=20?= =?UTF-8?q?=EC=88=98=ED=99=95=ED=95=9C=20=EB=8B=A4=EC=96=91=ED=95=9C=20?= =?UTF-8?q?=EB=AA=AC=EC=8A=A4=ED=84=B0=202=EC=A2=85(StS2=20=EC=9A=B0?= =?UTF-8?q?=EC=B8=A1=20=EB=B0=B0=EC=B9=98)=20+=20=EB=A7=B5=EB=B3=84=20?= =?UTF-8?q?=ED=83=80=EC=9D=BC=EC=85=8B=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 4.6 --- tools/gen-maps.mjs | 62 ++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/tools/gen-maps.mjs b/tools/gen-maps.mjs index c364f5b..c8ff8de 100644 --- a/tools/gen-maps.mjs +++ b/tools/gen-maps.mjs @@ -20,9 +20,34 @@ const BACKGROUNDS = [ 'b7c47cfae79e40e9b1352469a78af0bd', // Ludibrium ]; -// Task 1 결과: B 폴백 (라이브러리 변형 미사용). 비어 있으면 기존 템플릿 몬스터를 그대로 사용. -// 각 항목: { sprite, stand, hit, die } (모두 RUID 문자열) -const MONSTER_VARIANTS = []; +// 공식 맵에서 수확한 타일셋 RUID (맵마다 다르게). map01 기본(9dfea380…) 제외. +const TILESETS = [ + '46701ff2021b4d1fb21fbf5790b1ab14', + '7b6bd117bd0446a5bacec8ea6831c997', + '9bf18287398c44699c20fc5123d1a1ae', + 'd6a94bc26c8f43e2a7abfabfae0c4fc4', + 'e80a4b6e22d34348837d2ecf30e7cf74', + '23e80224ef624ea5af497cc50aa0e752', + '2667829326dd46de80ef26f6bb7f26ae', + '48afa7d90aa24fadae9c52f30977342e', + '901f885ef94f4a32961bf6cc64e3ec86', + '3ca52bc385574e56aaffa15eea5c23aa', + 'df1a1fee05874794a624c2bccbb1574d', + 'e1703f6cb2f84969bc54afcd12006b4e', +]; + +// 공식 맵에서 수확한 몬스터 변형 (기존 map01 4종 미사용). 항목: { sprite, stand, hit, die } +const MONSTER_VARIANTS = [ + { sprite: '96e955c1bf27415e84f96deea200a8f1', stand: '96e955c1bf27415e84f96deea200a8f1', hit: 'aec9504d5dc24aceb5646b79d30abad4', die: '65a2bfb039614f2e9e4ccc354340153d' }, + { sprite: 'f86992ba9c41487c8480fcb893fcbda6', stand: 'f86992ba9c41487c8480fcb893fcbda6', hit: 'd305b942b1704c8084548108ff3b7a6b', die: '5a563e5fd98c4132b61057dc6bb8aaf2' }, + { sprite: 'a2204a21d88942b281d2cac6053ffbaa', stand: 'a2204a21d88942b281d2cac6053ffbaa', hit: 'afc08936b8a64b26bc3dd8c03ead1f26', die: 'fc1c6d9ba9bc413ab53b6dbfae3ac45b' }, + { sprite: 'd8f014043ce8418f96700c2b6c9ebf6c', stand: 'd8f014043ce8418f96700c2b6c9ebf6c', hit: 'c3cf643b618346c7bfa6574187b396f9', die: 'a88d9b3d60f941e4890dc89a6ccaa8ee' }, + { sprite: '17b55730c26f4fd6b8fcfa288da388de', stand: '17b55730c26f4fd6b8fcfa288da388de', hit: 'eac48e84a9fc4580a4018de5cf52ddb3', die: '51c2f4b59a2c413db26035aa57002fc8' }, + { sprite: '48c10437ae8344a9b2a1d3f36185728f', stand: '48c10437ae8344a9b2a1d3f36185728f', hit: '9044063647854f5e9128efcf80e909be', die: 'f414577d18c94cc387c275df4abdbc3b' }, + { sprite: '4ca39dbfa1c6492283ba8bd352d12b0a', stand: '4ca39dbfa1c6492283ba8bd352d12b0a', hit: '7ac78511036e4ebe988b97c35fc275d1', die: '740f3f2b2e7a4b71bec5eac84e8539f9' }, + { sprite: 'ed3908e24d694bb786023fc1ed073489', stand: 'ed3908e24d694bb786023fc1ed073489', hit: '4763c9bebc9245998c9c499b6316aa9f', die: 'b168793b92a844a3a3a6f4ce647a14d2' }, + { sprite: '3109357701ae41a4bcc7543f52f1f4c3', stand: '3109357701ae41a4bcc7543f52f1f4c3', hit: 'ce0269079e884545b5bb6ea075e2a67f', die: 'a5e65650e00e47878cac1be7a5b999a0' }, +]; // 결정론적 시드 RNG (맵 번호 기반) function rng(seed) { @@ -51,25 +76,26 @@ function buildMap(nn) { const map = JSON.parse(JSON.stringify(template)); // deep clone map.EntryKey = `map://map${tag}`; - // 비-몬스터 엔티티만 유지 const ents = map.ContentProto.Entities.filter((e) => !isMonster(e)); - - // 몬스터 2마리 추가 (템플릿 몬스터 복제) + // 정적 베이스(StS2 위치 고정 — 배회 방지). 변형이 sprite/animation을 덮어쓰므로 외형은 베이스와 무관. + const base = monsterTemplates.find((e) => (e.path || '').includes('Static')) || monsterTemplates[0]; + // 서로 다른 변형 2종 선택 (맵 내 중복 금지) + const vi = Math.floor(rand() * MONSTER_VARIANTS.length); + const vj = (vi + 1 + Math.floor(rand() * (MONSTER_VARIANTS.length - 1))) % MONSTER_VARIANTS.length; + const chosen = [MONSTER_VARIANTS[vi], MONSTER_VARIANTS[vj]]; + const STS2_X = [3.5, 5.5]; // 화면 우측 전투 포메이션 for (let i = 0; i < 2; i++) { - const src = monsterTemplates[Math.floor(rand() * monsterTemplates.length)]; - const m = JSON.parse(JSON.stringify(src)); + const m = JSON.parse(JSON.stringify(base)); m.jsonString.name = `Monster${i + 1}`; m.path = `/maps/map${tag}/Monster${i + 1}`; m.jsonString.path = m.path; const tr = compOf(m, 'MOD.Core.TransformComponent'); - if (tr) tr.Position.x = Math.round((rand() * 8 - 4) * 100) / 100; // -4..4 바닥 위 - if (MONSTER_VARIANTS.length > 0) { - const v = MONSTER_VARIANTS[Math.floor(rand() * MONSTER_VARIANTS.length)]; - const sp = compOf(m, 'MOD.Core.SpriteRendererComponent'); - if (sp) sp.SpriteRUID = v.sprite; - const sa = compOf(m, 'MOD.Core.StateAnimationComponent'); - if (sa) sa.ActionSheet = { stand: v.stand, hit: v.hit, die: v.die }; - } + if (tr) tr.Position.x = STS2_X[i]; + const v = chosen[i]; + const sp = compOf(m, 'MOD.Core.SpriteRendererComponent'); + if (sp) sp.SpriteRUID = v.sprite; + const sa = compOf(m, 'MOD.Core.StateAnimationComponent'); + if (sa && v.stand) sa.ActionSheet = { stand: v.stand, hit: v.hit, die: v.die }; ents.push(m); } @@ -84,6 +110,10 @@ function buildMap(nn) { const bg = compOf(e, 'MOD.Core.BackgroundComponent'); if (bg) bg.TemplateRUID = BACKGROUNDS[(nn - 2) % BACKGROUNDS.length]; } + if ((e.path || '').endsWith('/TileMap')) { + const tm = compOf(e, 'MOD.Core.TileMapComponent'); + if (tm && TILESETS.length > 0) tm.TileSetRUID = { DataId: TILESETS[(nn - 2) % TILESETS.length] }; + } } // GUID 재발급 (자기참조 root/sub_entity_id 보정)