using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace TKS
{
///
/// 보스 스테이지 시작 연출(2026-11-12 PD 「보스는 등장할 때 포커스 + 짤막한 대사(말풍선)」 · 「예시처럼 보스 등장 연출」 · 개발 요청서 C2).
/// ① 경고(위아래 검은 띠 + 붉은 가장자리 깜빡임) → ② 보스 포커스(카메라 이동·확대 · 다른 유닛 어둡게 · 발밑 붉은 원) →
/// ③ 컷인(위 「BOSS 등장!」 붉은 줄무늬 띠 → 보스 전신 + 아래 이름 띠) → ④ 말풍선 대사(타자 효과 · 2인 보스는 n_order 순) → ⑤ 아군 시야로 복귀.
/// 값 = ScenarioData.Cfg(boss_intro_config.csv) · 대사 = boss_intro.csv · 그림 = Resources/Scenario/*.png(띠·글자·말풍선 · 없으면 색 면) + 보스 유닛 그림.
/// 연출 동안 전투 단계 = Setup(아군 조작 잠금) · 끝나면 onDone(아군 차례). Sim.Active(적 AI 가상 실행) 중에는 아무것도 하지 않는다.
///
public class BossIntro : MonoBehaviour
{
static BossIntro current;
BattleManager battle;
CameraController cc;
Action onDone;
List bosses = new List();
List lines;
bool withCutin;
bool skip;
int taps;
Vector3 camStart;
float viewStart;
readonly Dictionary dimmed = new Dictionary();
readonly List rings = new List();
RectTransform root, fx;
Image dim, edge, barTop, barBottom;
Button skipBtn;
static float C(string k)
{
return ScenarioData.Cfg(k);
}
/// 이 스테이지에 연출을 틀지(보스 행 있음 · 가상 실행 아님 · 다시 출정 = replay_mode 2 면 안 틂).
public static bool Wants(BalanceConfig.StageDef stage, bool cleared)
{
if (Sim.Active || stage == null || stage.kind != StageKind.Boss) return false;
if (ScenarioData.Bosses(stage.chapter, stage.stage).Count == 0) return false;
return !(cleared && Mathf.RoundToInt(C("replay_mode")) == 2);
}
/// 연출 시작 — cleared = 이미 깬 스테이지(replay_mode 1 = 컷인 빼고 ②④⑤).
public static void Begin(BattleManager battle, BalanceConfig.StageDef stage, bool cleared, Action onDone)
{
Abort();
var cc = FindFirstObjectByType();
if (cc == null || cc.Cam == null)
{
onDone?.Invoke();
return;
}
var go = new GameObject("BossIntroUI");
var bi = go.AddComponent();
current = bi;
bi.battle = battle;
bi.cc = cc;
bi.onDone = onDone;
bi.lines = ScenarioData.Bosses(stage.chapter, stage.stage);
bi.withCutin = !(cleared && Mathf.RoundToInt(C("replay_mode")) == 1);
foreach (var l in bi.lines)
{
Unit hit = null;
foreach (var u in battle.AliveUnits(Faction.Enemy))
if (u.IsBoss && u.ArtId == l.id) { hit = u; break; }
if (hit == null)
foreach (var u in battle.AliveUnits(Faction.Enemy))
if (u.IsBoss && !bi.bosses.Contains(u)) { hit = u; break; }
bi.bosses.Add(hit);
}
bi.Build();
bi.StartCoroutine(bi.Run());
}
/// 진행 중 연출을 바로 치움(전투 재시작 · 월드맵 복귀) — 어둡게·원·카메라 상태를 되돌린다.
public static void Abort()
{
if (current == null) return;
current.Restore();
Destroy(current.gameObject);
current = null;
}
// ---------- 화면 ----------
static Sprite Spr(string name)
{
return Resources.Load(ScenarioData.Root + name);
}
static RectTransform Rt(string name, Transform parent, Vector2 aMin, Vector2 aMax, Vector2 pivot, Vector2 pos, Vector2 size)
{
var go = new GameObject(name, typeof(RectTransform));
go.transform.SetParent(parent, false);
var rt = (RectTransform)go.transform;
rt.anchorMin = aMin;
rt.anchorMax = aMax;
rt.pivot = pivot;
rt.anchoredPosition = pos;
rt.sizeDelta = size;
return rt;
}
static Image Img(string name, Transform parent, Sprite sp, Color col, Vector2 aMin, Vector2 aMax, Vector2 pivot, Vector2 pos, Vector2 size)
{
var img = Rt(name, parent, aMin, aMax, pivot, pos, size).gameObject.AddComponent();
img.sprite = sp;
img.color = col;
img.raycastTarget = false;
return img;
}
static Image Full(string name, Transform parent, Sprite sp, Color col)
{
return Img(name, parent, sp, col, Vector2.zero, Vector2.one, new Vector2(0.5f, 0.5f), Vector2.zero, Vector2.zero);
}
static Text Txt(Transform parent, string s, int size, Color col, TextAnchor anchor, Vector2 pos, Vector2 box, Font font)
{
var t = Rt("Text", parent, new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), pos, box).gameObject.AddComponent();
t.font = font;
t.fontSize = size;
t.color = col;
t.alignment = anchor;
t.text = s;
t.supportRichText = false;
t.raycastTarget = false;
t.horizontalOverflow = HorizontalWrapMode.Wrap;
t.verticalOverflow = VerticalWrapMode.Overflow;
return t;
}
static void Stroke(Text t, Color col, float d)
{
var o = t.gameObject.AddComponent();
o.effectColor = col;
o.effectDistance = new Vector2(d, -d);
var o2 = t.gameObject.AddComponent();
o2.effectColor = col;
o2.effectDistance = new Vector2(-d, d);
}
void Build()
{
UiLayer.Root(gameObject, cc.Cam, 460, 0.38f, UiLayer.Reference, UiRate.Event); // 아웃게임 위 덮개(재화 바 · 450)보다 위 · 연출 동안 화면 전체를 덮음
root = (RectTransform)transform;
// 누름 받이(탭 1 = 대사 전부 · 탭 2 = 닫기) — 전체 화면 · 투명
var catcher = Full("Tap", root, null, new Color(0f, 0f, 0f, 0f));
catcher.raycastTarget = true;
catcher.gameObject.AddComponent