Project_WL/AgentScripts/WL_MapSwitch.cs

104 lines
5.5 KiB
C#
Raw Permalink Normal View History

WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// WL_MapSwitch.cs — PD 지시 #772 : 시작 맵 전환 도구
// 목록 : unity command run_script --file AgentScripts/WL_MapSwitch.cs --entry WL_MapSwitch.List
// 전환 : unity command run_script --file AgentScripts/WL_MapSwitch.cs --entry WL_MapSwitch.Set --args '["WL_Nature03"]'
// 복귀 : ... --entry WL_MapSwitch.Set --args '["WL_Nature"]'
//
// 동작 = BattleMapConfig.json 의 n_MapID "-1" 행(= 게임 시작 시 InGameInfo.Load_Map(-1) 이 읽는 행)의
// s_Scene 값만 바꾼다. 다른 행·필드는 건드리지 않는다.
// 백업 = 첫 변경 시 AgentScripts/staging/WL_Maps/out/BattleMapConfig.json.bak_yyyyMMdd_HHmm (Assets 밖) 을 남기고,
// 매 변경마다 같은 폴더 MapSwitch_history.txt 에 기록한다.
// 인코딩 = UTF-8 BOM · 1줄 minified 원본 포맷을 그대로 유지한다 (문자열 치환 방식).
//
// 🔴 신규 맵을 인게임 맵 선택 UI 에서 고를 수는 없다 (MapChoiceUI_WorldMap 이 MapID = 탭인덱스+1 하드코딩).
// 본 도구가 그 대체 수단이다.
using System;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
public static class WL_MapSwitch
{
const string Json = "Assets/ResWork/Table/Export/BattleMapConfig.json";
const string MapDir = "Assets/Res_Addr/Map";
const string SceneDir = "Assets/Scenes";
const string HistDir = "AgentScripts/staging/WL_Maps/out";
static string Abs(string rel) { return System.IO.Path.Combine(System.IO.Directory.GetCurrentDirectory(), rel.Replace('/', System.IO.Path.DirectorySeparatorChar)); }
static string ReadJson() { return System.IO.File.ReadAllText(Abs(Json), new UTF8Encoding(true)); }
static void WriteJson(string s) { System.IO.File.WriteAllText(Abs(Json), s, new UTF8Encoding(true)); }
// -1 행 안의 s_Scene 값을 잡는 정규식 (행 경계 = 다음 '}' 까지)
static readonly Regex RxStartRow = new Regex("\\{\\s*\"n_MapID\"\\s*:\\s*\"-1\"[^}]*\\}", RegexOptions.Compiled);
public static object List()
{
var sb = new StringBuilder();
var t = ReadJson();
var m = RxStartRow.Match(t);
var cur = "(못 찾음)";
if (m.Success)
{
var s = Regex.Match(m.Value, "\"s_Scene\"\\s*:\\s*\"([^\"]*)\"");
if (s.Success) cur = s.Groups[1].Value;
}
sb.AppendLine("현재 시작 맵 (BattleMapConfig n_MapID=-1 · s_Scene) = " + cur);
sb.AppendLine();
sb.AppendLine("전환 가능한 맵 (프리팹 · 씬 · NavMesh 3종이 모두 있는 것만):");
#if UNITY_EDITOR
foreach (var g in AssetDatabase.FindAssets("t:Prefab", new[] { MapDir }).Select(AssetDatabase.GUIDToAssetPath).OrderBy(x => x))
{
var name = System.IO.Path.GetFileNameWithoutExtension(g);
bool sceneOk = System.IO.File.Exists(Abs(SceneDir + "/" + name + ".unity"));
bool navOk = System.IO.File.Exists(Abs("Assets/WL/Settings/" + name + "_NavMesh.asset"));
bool inBuild = EditorBuildSettings.scenes.Any(x => x.path.EndsWith("/" + name + ".unity") && x.enabled);
sb.AppendLine(string.Format(" {0,-16} 씬 {1} · NavMesh {2} · BuildSettings {3} {4}",
name, sceneOk ? "O" : "X", navOk ? "O" : "X", inBuild ? "O" : "X",
(sceneOk && inBuild) ? (name == cur ? " ← 현재" : "") : " (전환 불가)"));
}
#endif
return sb.ToString();
}
public static object Set(string sceneName)
{
if (string.IsNullOrEmpty(sceneName)) return "sceneName 이 비었다";
var scenePath = SceneDir + "/" + sceneName + ".unity";
if (!System.IO.File.Exists(Abs(scenePath))) return "씬이 없다: " + scenePath;
#if UNITY_EDITOR
if (!EditorBuildSettings.scenes.Any(x => x.path == scenePath && x.enabled))
return "EditorBuildSettings 에 등록(enabled)되어 있지 않다: " + scenePath;
#endif
var t = ReadJson();
var m = RxStartRow.Match(t);
if (!m.Success) return "BattleMapConfig 에서 n_MapID=-1 행을 못 찾았다";
var row = m.Value;
var sm = Regex.Match(row, "\"s_Scene\"\\s*:\\s*\"([^\"]*)\"");
if (!sm.Success) return "-1 행에 s_Scene 이 없다";
var before = sm.Groups[1].Value;
if (before == sceneName) return "이미 " + sceneName + " 이다 (변경 없음)";
// 백업 (첫 1회) — Assets 밖에 둔다 (Assets 안에 두면 Unity 가 에셋으로 임포트한다)
var bakDir = Abs(HistDir);
System.IO.Directory.CreateDirectory(bakDir);
var bak = System.IO.Path.Combine(bakDir, "BattleMapConfig.json.bak_" + DateTime.Now.ToString("yyyyMMdd_HHmm"));
if (!System.IO.File.Exists(bak)) System.IO.File.Copy(Abs(Json), bak);
var newRow = row.Substring(0, sm.Groups[1].Index) + sceneName + row.Substring(sm.Groups[1].Index + before.Length);
var outText = t.Substring(0, m.Index) + newRow + t.Substring(m.Index + m.Length);
WriteJson(outText);
System.IO.Directory.CreateDirectory(Abs(HistDir));
System.IO.File.AppendAllText(Abs(HistDir + "/MapSwitch_history.txt"),
string.Format("{0} {1} -> {2}\n", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), before, sceneName), new UTF8Encoding(true));
#if UNITY_EDITOR
AssetDatabase.ImportAsset(Json, ImportAssetOptions.ForceUpdate);
AssetDatabase.SaveAssets();
#endif
return string.Format("시작 맵 {0} -> {1} (백업 {1}: {2})", before, sceneName, System.IO.Path.GetFileName(bak));
}
}