220 lines
17 KiB
C#
220 lines
17 KiB
C#
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<string>(), gimmicks = new List<string>() }, 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<string>(lo1.shots), gimmicks = new List<string>(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<Text>(); var start = root.Find("Start")?.GetComponent<Button>();
|
||
bool prev = root.Find("Prev").gameObject.activeSelf, next = root.Find("Next").gameObject.activeSelf, clear = root.Find("Clear").gameObject.activeSelf;
|
||
bool ok = root.gameObject.activeSelf && !app.BattleRoot.gameObject.activeSelf && app.Root.Find("TabBar").gameObject.activeSelf && title != null && title.text.StartsWith(stage + ". ") && start != null && start.interactable == open
|
||
&& prev == (stage > 1) && next == (stage < n) && clear == app.G.StageCleared(stage);
|
||
if (!ok) { fails++; notes.Add($"{name}: 스테이지 선택 화면 이상 (제목 {title?.text} · 시작 {start?.interactable} · 화살표 {prev}/{next} · 클리어 {clear})"); }
|
||
}
|
||
|
||
// 유닛 메뉴 — 아래 메뉴 3개 · 덱(프리셋 · 6칸 · 잠긴 칸) · 유닛 목록 · 상세 · 프리셋 2 + 빈 칸 · 바꿀 칸 고르기
|
||
static void UnitShots(GameApp app)
|
||
{
|
||
var g = app.G; var us = g.BC.units; int slots0 = g.S.battleSlots;
|
||
g.S.battleSlots = 3;
|
||
app.SwitchTab(2); UnitCheck(app, "units_deck"); Shot(app, "units_deck");
|
||
app.ScrollUnits(0.5f); Shot(app, "units_locked"); app.ScrollUnits(1f);
|
||
app.OpenUnitDetail(us[0].id); Shot(app, "unit_detail");
|
||
app.OpenUnitDetail(us[us.Count - 1].id); Shot(app, "unit_detail_last");
|
||
app.CloseUnitDetail();
|
||
// 편성 — 「편성」 → 유닛 누름 = 덱 칸마다 화살표 → 덱 칸 누름 = 그 칸으로 교체(프리셋 2 · 빈 칸 하나)
|
||
app.SelectPreset(1); g.DeckRemove(us[0].id);
|
||
app.ToggleUnitEdit(); EditCheck(app, "units_edit", false); Shot(app, "units_edit");
|
||
app.TapUnitCard(us[0].id); EditCheck(app, "units_edit_pick", true); Shot(app, "units_edit_pick");
|
||
app.PickDeckSlot(3); EditCheck(app, "units_edit_put", false);
|
||
if (g.ActiveDeck[3] != us[0].id || g.DeckSel != 1) { fails++; notes.Add("units_edit: 고른 칸에 안 들어감"); }
|
||
app.ToggleUnitEdit(); app.SelectPreset(0); g.S.battleSlots = slots0;
|
||
}
|
||
|
||
/// 편성 중 — 유닛을 고르면 덱 칸마다 화살표 · 넣고 나면 화살표 없음 · 편성 버튼 = 「완료」
|
||
static void EditCheck(GameApp app, string name, bool arrows)
|
||
{
|
||
var content = app.UnitRoot.Find("List/Viewport/Content"); int n = 0;
|
||
foreach (Transform ch in content.Find("DeckGrid")) if (ch.gameObject.activeSelf && ch.Find("Arrow") != null) n++;
|
||
var label = content.Find("CollHead/Edit/Label")?.GetComponent<Text>();
|
||
if (n != (arrows ? app.G.BC.slotMax : 0) || label == null || label.text != "완료") { fails++; notes.Add($"{name}: 편성 화면 이상 (화살표 {n} · 버튼 {label?.text})"); }
|
||
}
|
||
|
||
/// 아래 메뉴 = 연구 · 전투 · 유닛 · 유닛 화면 = 덱 칸 slotMax 개 · 목록 = 유닛 수 · 프리셋 버튼 = deckPresets 개
|
||
static void UnitCheck(GameApp app, string name)
|
||
{
|
||
var g = app.G; var root = app.UnitRoot; var bar = app.Root.Find("TabBar");
|
||
var content = root.Find("List/Viewport/Content");
|
||
int deck = content.Find("DeckGrid").childCount, coll = content.Find("CollGrid").childCount, presets = 0;
|
||
foreach (Transform ch in content.Find("DeckHead")) if (ch.name.StartsWith("P")) presets++;
|
||
string tabs = "";
|
||
if (bar != null) foreach (Transform ch in bar) { var t = ch.Find("Label")?.GetComponent<Text>(); if (t != null) tabs += t.text + "|"; }
|
||
// 미보유 = 목록 그림 중 보유 유닛이 쓰지 않는 것 · 「편성」 버튼 = 목록 머리줄 오른쪽
|
||
var ownedArts = new HashSet<int>(); foreach (var u in g.BC.units) { int a = UnitArt.ArtOf(u.id); if (a > 0) ownedArts.Add(a); }
|
||
int want = 0; foreach (var r in g.BC.roster) if (!ownedArts.Contains(r.art)) want++;
|
||
int locked = content.Find("LockGrid").childCount; bool edit = content.Find("CollHead/Edit") != null;
|
||
bool ok = root.gameObject.activeSelf && bar != null && bar.gameObject.activeSelf && tabs == "연구|전투|유닛|" && deck == g.BC.slotMax && coll == g.BC.units.Count && presets == g.DeckPresets && want > 0 && locked == want && edit;
|
||
if (!ok) { fails++; notes.Add($"{name}: 유닛 화면 이상 (메뉴 {tabs} · 덱 {deck} · 보유 {coll} · 미보유 {locked}/{want} · 프리셋 {presets} · 편성 {edit})"); }
|
||
}
|
||
|
||
static List<string> L(params string[] s) => new List<string>(s);
|
||
static int MaxMinions(Battle b) { int n = 0; foreach (var s in b.Summoners) n += s.def.max; return n; }
|
||
|
||
static void Scene(GameApp app, BattleSetup s, int seed)
|
||
{
|
||
app.BattleTest = s; app.NewBattleRound(seed);
|
||
app.B.Start(); app.B.MaxMana = 15; app.B.Mana = 15;
|
||
app.DeployCard(2); app.DeployCard(1); app.DeployCard(0); app.DeployCard(0); app.DeployCard(4);
|
||
}
|
||
|
||
static void CastleScene(GameApp app, string name, List<string> shots, List<string> gimmicks, Func<bool> moment)
|
||
{
|
||
Scene(app, new BattleSetup { mapType = 2, obstacles = 0, castleX = 9f, shots = shots, gimmicks = gimmicks }, 40);
|
||
bool ok = false;
|
||
for (int i = 0; i < 1200 && !ok; i++) { app.DebugTick(0.05f, 1); ok = moment(); }
|
||
if (!ok) { fails++; notes.Add(name + ": 발동 장면 못 잡음"); }
|
||
Shot(app, name);
|
||
}
|
||
}
|
||
}
|