using System; using System.Collections.Generic; using UnityEngine; using UnityEngine.UI; using VS.Sim; using VS.View; namespace VS.EditorTools { public static partial class EditorRender { static void SystemShots(GameApp app) { var g = app.G; // 3일차 · 선물 해금 → 슬롯 채움 + 채팅 흐름 g.DebugSetPlayDay(3); g.S.giftUnlockedLifetime = true; g.ActivateX2Boost(); g.ActivateAutoClick(); app.DebugTick(0.25f, 80); app.CloseAllPopups(); Shot(app, "main_gifts_chat"); int t = -1; foreach (var s in g.GiftSlot) if (s >= 0) { t = s; break; } if (t < 0) { g.GiftSlot[0] = GiftTypes.BigMoneyPack; t = GiftTypes.BigMoneyPack; } app.OpenGiftPopup(t); Shot(app, "pop_gift"); app.ClosePopup(); app.OpenBoosterX2(); Shot(app, "pop_booster_x2"); app.ClosePopup(); g.S.afkPending = "987654"; g.AfkSeconds = 5400; app.OpenAfkPopup(false); Shot(app, "pop_afk"); app.ClosePopup(); app.OpenTasks(); Shot(app, "pop_task"); app.ClosePopup(); app.OpenDaily(); Shot(app, "pop_daily"); app.ClosePopup(); app.OpenTicketShop(); Shot(app, "pop_ticketshop"); app.ClosePopup(); g.S.rankIndex = Math.Max(g.S.rankIndex, g.NextHouseRankReq()); app.OpenBuyNewHouse(); Shot(app, "pop_newhouse"); app.ClosePopup(); g.S.afkPending = "0"; BattleShots(app); UnitShots(app); Stage50Shots(app); // 스테이지 50 — 선택 · 정보 · 안내 카드 · 지형 · 필드 · 새 공격 · 성 능력(EditorRender.Stage50.cs) app.SwitchTab(0); } // 전투 탭 — 기본 흐름 · 맵 4종 · 성 기믹 3종 · 성 투사체 5종 (카드 = 아래 가운데에서 즉시 소환) static void BattleShots(GameApp app) { var g = app.G; app.BattleTest = null; // 스테이지 선택 — 전투 탭 = 고르는 화면부터(첫 화면 = 다음에 깰 스테이지 1) · 잠긴 스테이지 · 지도 = 정보 창 · 시작 = 바로 전투 app.SwitchTab(1); app.RefreshAll(); StageCheck(app, "stage_select", 1, true); Shot(app, "stage_select"); app.ShowStage(3); StageCheck(app, "stage_locked", 3, false); Shot(app, "stage_locked"); app.OpenStageInfo(); Shot(app, "stage_info"); app.ClosePopup(); // 처음 나오는 요소 = 안내 카드(스테이지 1 = 일반 화살) → 「전투 시작」 app.StartStage(1, 5); if (app.OpenPopupId != "StageIntro") { fails++; notes.Add("stage_intro: 스테이지 1 시작 때 안내 카드가 안 뜸"); } else { Shot(app, "stage_intro"); app.ConfirmIntro(); } if (app.StageRoot.gameObject.activeSelf || !app.BattleRoot.gameObject.activeSelf || app.B.State != BattleState.Fighting || app.B.Stage != 1) { fails++; notes.Add("stage_start: 시작을 눌렀는데 전투 화면이 아님"); } if (app.Root.Find("TabBar").gameObject.activeSelf) { fails++; notes.Add("stage_start: 전투 중에 아래 메뉴가 보임"); } app.B.Mana = 10; app.DeployCard(0); app.DebugTick(0.05f, 40); app.DeployCard(0); app.DebugTick(0.05f, 40); app.DeployCard(0); g.S.battleSlots = 6; app.B.MaxMana = 15; app.B.Mana = 15; app.DeployCard(1); app.DeployCard(4); app.DeployCard(5); app.DebugTick(0.05f, 240); Shot(app, "battle_fight"); RivalCheck(app, "battle_fight"); LayoutCheck(app, "battle_fight"); app.OpenBattleUpgrade(); Shot(app, "battle_upgrade"); app.ClosePopup(); app.OpenBattleTest(); Shot(app, "battle_test"); app.ClosePopup(); app.B.CastleHpDebugKill(); app.DebugTick(0.05f, 2); Shot(app, "battle_won"); // 이긴 뒤 스테이지 선택 = 1 클리어 표시 · 2 열림(첫 화면) app.OpenStageSelect(1); StageCheck(app, "stage_cleared", 1, true); Shot(app, "stage_cleared"); app.OpenStageSelect(app.G.StageFocus); StageCheck(app, "stage_next", 2, true); Shot(app, "stage_next"); // 맵 4종 (바위·언덕 켬 · 성 공격 끔 — 길 찾기 장면) string[] mapKey = { "plain", "b1", "b2", "b3" }; for (int k = 0; k < 4; k++) { Scene(app, new BattleSetup { mapType = k, obstacles = 1, shots = new List(), gimmicks = new List() }, 30 + k); app.DebugTick(0.05f, 300); Shot(app, "battle_map_" + mapKey[k]); } // 성 기믹 · 투사체 — 다리 2개 · 장애물 없음 · 그것만 켜고 발동 순간 CastleScene(app, "battle_gim_fireball", L(), L("fireball"), () => app.B.Fx.Exists(f => f.type == "blast")); CastleScene(app, "battle_gim_lightning", L(), L("lightning"), () => app.B.Fx.Exists(f => f.type == "lightning")); CastleScene(app, "battle_gim_summon", L(), L("summonMelee", "summonRanged"), () => app.B.Minions.Count >= MaxMinions(app.B) && app.B.Minions.Exists(m => m.attacking)); CastleScene(app, "battle_shot_normal", L("normal"), L(), () => app.B.Shots.Exists(s => s.type == "normal" && UnityEngine.Vector2.Distance(s.pos, app.B.CastlePos) > app.B.C.castle.radius + 0.8f)); CastleScene(app, "battle_shot_splash", L("splash"), L(), () => app.B.Fx.Exists(f => f.type == "blast")); CastleScene(app, "battle_shot_multi", L("multi"), L(), () => app.B.Shots.FindAll(s => s.type == "multi" && s.left < app.B.Range(s.def) - 1.2f).Count >= 3); CastleScene(app, "battle_shot_slow", L("slow"), L(), () => app.B.Units.Exists(u => u.slow > 0f) && app.B.Shots.Exists(s => s.type == "slow")); CastleScene(app, "battle_shot_laser", L("laser"), L(), () => app.B.Fx.Exists(f => f.type == "laser" && f.t < 0.1f)); // 깃발 — 왼쪽 다리 건너편에 꽂으면 전원이 그리로 먼저 Scene(app, new BattleSetup { mapType = 2, obstacles = 0, castleX = 9f, shots = L(), gimmicks = L() }, 41); app.DebugTick(0.05f, 20); app.PlaceFlag(new Vector2(app.B.Map.Bridges[0], app.B.C.riverY + 3f)); app.B.Mana = 15; app.DeployCard(0); app.DeployCard(1); app.DebugTick(0.05f, 110); var flag = app.BattleRoot.Find("Arena/Flag"); if (flag == null || !flag.gameObject.activeSelf || !app.B.Units.Exists(u => u.flag != app.B.FlagVer)) { fails++; notes.Add("battle_flag: 깃발 · 깃발로 가는 유닛 못 잡음"); } Shot(app, "battle_flag"); // 물량 — 1단계 성 구성(stageLoadouts)에 검사를 마나 차는 대로 계속 보내 성벽에 많이 붙은 순간 (카드 1장 = 1마리) var lo1 = g.BC.Loadout(1); app.BattleTest = new BattleSetup { shots = new List(lo1.shots), gimmicks = new List(lo1.gimmicks) }; app.NewBattleRound(7); app.B.Start(); bool swarm = false; for (int i = 0; i < 2400 && !swarm; i++) { if (app.B.Mana >= g.BattleUnit(0).cost) app.DeployCard(0); app.DebugTick(0.05f, 1); swarm = app.B.Units.FindAll(u => u.attacking).Count >= 15; } if (!swarm) { fails++; notes.Add("battle_swarm: 물량 장면 못 잡음"); } LayoutCheck(app, "battle_swarm"); Shot(app, "battle_swarm"); // 성벽 회복 — 맞은 뒤 regenDelay 초 지나 초록 빛 app.BattleTest = new BattleSetup { mapType = 0, obstacles = 0, castleX = 9f, shots = L(), gimmicks = L() }; app.NewBattleRound(42); app.B.Start(); app.B.HitCastle(app.B.CastleMaxHp * 0.4f); bool heal = false; for (int i = 0; i < 400 && !heal; i++) { app.DebugTick(0.05f, 1); heal = app.B.Fx.Exists(f => f.type == "heal" && f.t < 0.15f) && app.B.CastleHeals >= 2; } if (!heal) { fails++; notes.Add("battle_regen: 회복 장면 못 잡음"); } Shot(app, "battle_regen"); // 상대방 칸 — 투사체 5 + 기믹 4 전부 켜도 카드 칸과 안 겹침 var all = L(); foreach (var d in g.BC.skills) all.Add(d.id); foreach (var d in g.BC.summons) all.Add(d.id); Scene(app, new BattleSetup { mapType = 1, obstacles = 0, shots = g.BC.shots.ConvertAll(d => d.id), gimmicks = all }, 43); app.DebugTick(0.05f, 200); RivalCheck(app, "battle_rival_all"); Shot(app, "battle_rival_all"); app.BattleTest = null; } /// 오른쪽 위 = 상대방 얼굴 + 스킬 줄(성 구성 수만큼) · 남은 시간 칸 없음 · 카드 칸(아래 view.trayH)과 안 겹침 static void RivalCheck(GameApp app, string name) { var root = app.BattleRoot; var rv = root.Find("Rival") as RectTransform; int want = app.B.CastleShots.Count + app.B.CastleSkills.Count + app.B.Summoners.Count + app.B.Powers.Count, rows = 0; if (rv != null) foreach (Transform ch in rv) if (ch.name.StartsWith("Skill_")) rows++; bool timer = root.Find("Timer") != null; if (timer || rv == null || rows != want) { fails++; notes.Add($"{name}: 상대방 칸 이상 (남은 시간 칸 {timer} · 스킬 줄 {rows}/{want})"); return; } float h = root.rect.height, bottom = h + rv.anchoredPosition.y - rv.sizeDelta.y; if (h > 0f && bottom < app.G.BC.view.trayH) { fails++; notes.Add($"{name}: 상대방 칸이 카드 칸과 겹침 (아래 끝 {bottom:0})"); } } /// 클래시 로얄 비율(battle.json view) — 맵 18칸이 화면 폭의 85% 이상 · 카드 칸 높이 = trayH · 맵이 카드 칸 위에서 화면 안 · 유닛 그림 한 변 ≥ 1.5칸 static void LayoutCheck(GameApp app, string name) { var c = app.G.BC; var v = c.view; var root = app.BattleRoot; var ar = root.Find("Arena") as RectTransform; var tray = root.Find("CardPanel") as RectTransform; float w = root.rect.width, h = root.rect.height, playW = c.arenaW * v.tileX, bottom = ar.anchoredPosition.y - ar.sizeDelta.y * 0.5f, top = bottom + ar.sizeDelta.y; float minUnit = float.MaxValue; foreach (Transform t in ar.Find("Units")) if (t.name.StartsWith("U")) minUnit = Mathf.Min(minUnit, ((RectTransform)t).sizeDelta.y); bool ok = w <= 0f || (playW >= w * 0.85f && Mathf.Abs(tray.sizeDelta.y - v.trayH) < 0.5f && bottom >= v.trayH - 0.5f && top <= h + 0.5f && (minUnit == float.MaxValue || minUnit >= 1.5f * v.tileX)); if (!ok) { fails++; notes.Add($"{name}: 화면 비율 이상 (맵 {playW:0}/{w:0} · 카드 칸 {tray.sizeDelta.y:0} · 맵 아래 {bottom:0} 위 {top:0}/{h:0} · 유닛 그림 {minUnit:0})"); } else Debug.Log($"[VS] LAYOUT {name} 맵 {playW:0}/{w:0} px({playW / Mathf.Max(1f, w) * 100f:0}%) · 칸 {v.tileX:0}×{v.tileY:0} · 카드 칸 {tray.sizeDelta.y:0} · 맵 {bottom:0}~{top:0}/{h:0} · 가장 작은 유닛 그림 {minUnit:0} px"); } /// 스테이지 선택 화면 — 보임(전투 화면은 숨김) · 제목 「번호. 이름」 · 시작 버튼 열림/잠김 · 화살표(처음 = 왼쪽 없음 · 끝 = 오른쪽 없음) · 클리어 표시 static void StageCheck(GameApp app, string name, int stage, bool open) { var root = app.StageRoot; int n = app.G.StageCount; var title = root.Find("Title")?.GetComponent(); var start = root.Find("Start")?.GetComponent