324 lines
13 KiB
C#
324 lines
13 KiB
C#
|
|
using System.Collections.Generic;
|
||
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.EventSystems;
|
||
|
|
using UnityEngine.UI;
|
||
|
|
|
||
|
|
namespace TKS.OutGame
|
||
|
|
{
|
||
|
|
/// <summary>
|
||
|
|
/// 편성(img08 · 기획 S12 v3) — 프리셋 3 · 전열 3(윗줄)·후열 2(아랫줄) 원형 자리 · 하단 보유 목록(장수|병사 · 2줄 카드 · 출전 중) ·
|
||
|
|
/// 탭(자리 고르기 → 카드 탭 = 넣기 · 같은 자리 두 번 = 빼기 · 다른 자리 = 맞바꾸기) · 끌기(자리 ↔ 자리 · 카드 → 자리) · 저장 = 그 프리셋을 출전 편성으로.
|
||
|
|
/// 빈 칸 허용 · 최소 1 · 편성 제약 없음(D4).
|
||
|
|
/// </summary>
|
||
|
|
public class FormationScreen : OutGameScreen
|
||
|
|
{
|
||
|
|
int preset;
|
||
|
|
readonly List<string> work = new List<string>();
|
||
|
|
bool dirty;
|
||
|
|
int selected = -1;
|
||
|
|
bool soldiers;
|
||
|
|
|
||
|
|
readonly List<Button> presetBtns = new List<Button>();
|
||
|
|
readonly List<RectTransform> slots = new List<RectTransform>();
|
||
|
|
Button tabHero;
|
||
|
|
Button tabSoldier;
|
||
|
|
RectTransform list;
|
||
|
|
ScrollRect scroll;
|
||
|
|
RectTransform ghost;
|
||
|
|
|
||
|
|
public IList<string> Working
|
||
|
|
{
|
||
|
|
get { return work; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public int Preset
|
||
|
|
{
|
||
|
|
get { return preset; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public bool Dirty
|
||
|
|
{
|
||
|
|
get { return dirty; }
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void Build()
|
||
|
|
{
|
||
|
|
int n = Root.Battle.Config.playerSpawnPoints.Length;
|
||
|
|
int front = Mathf.Clamp(S.Db.Economy.FormationFront, 0, n);
|
||
|
|
Header("편성", "출정 좌측 · 출전 " + n + "칸", true);
|
||
|
|
var body = Body();
|
||
|
|
|
||
|
|
for (int i = 0; i < S.Save.presets.Count; i++)
|
||
|
|
{
|
||
|
|
int k = i;
|
||
|
|
presetBtns.Add(UiKit.Btn("Preset" + i, body, "프리셋 " + (i + 1), 34, UiKit.Paper, UiKit.Ink, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f + i * 226f, -18f), new Vector2(210f, 84f), () => LoadPreset(k)));
|
||
|
|
}
|
||
|
|
var chip = UiKit.Box("Free", body, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-24f, -24f), new Vector2(270f, 72f), UiKit.Dark);
|
||
|
|
UiKit.FillLabel(chip.transform, "편성 제약 없음", 30, UiKit.White);
|
||
|
|
|
||
|
|
var board = UiKit.TopBand("Board", body, 122f, 600f, 24f, 24f);
|
||
|
|
var bimg = UiKit.Paint(board, UiKit.Paper);
|
||
|
|
UiKit.Border(bimg, UiKit.Line, 3f);
|
||
|
|
Pill(board, "전열", UiKit.Red, -116f);
|
||
|
|
Pill(board, "후열", UiKit.Dark, -380f);
|
||
|
|
UiKit.Label(board, "탭·드래그 = 자리 교체 · 같은 자리 두 번 탭 = 빼기", 24, UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 0f), new Vector2(0f, 0f), new Vector2(24f, 8f), new Vector2(900f, 40f), false);
|
||
|
|
for (int i = 0; i < n; i++)
|
||
|
|
{
|
||
|
|
bool f = i < front;
|
||
|
|
int col = f ? i : i - front;
|
||
|
|
int cnt = f ? front : n - front;
|
||
|
|
float gap = 264f;
|
||
|
|
float x = 70f + (col - (cnt - 1) / 2f) * gap;
|
||
|
|
float y = f ? -30f : -290f;
|
||
|
|
var s = UiKit.Rect("Slot" + i, board, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(x, y), new Vector2(230f, 250f));
|
||
|
|
var hit = s.gameObject.AddComponent<Image>();
|
||
|
|
hit.color = new Color(1f, 1f, 1f, 0f);
|
||
|
|
hit.raycastTarget = true;
|
||
|
|
var td = s.gameObject.AddComponent<TapDrag>();
|
||
|
|
td.AnyDirection = true;
|
||
|
|
int k = i;
|
||
|
|
td.OnTap = () => TapSlot(k);
|
||
|
|
td.OnDragStart = e => StartGhost(work[k], e);
|
||
|
|
td.OnDragMove = MoveGhost;
|
||
|
|
td.OnDragEnd = e => DropFromSlot(k, e);
|
||
|
|
slots.Add(s);
|
||
|
|
}
|
||
|
|
|
||
|
|
var row = UiKit.TopBand("ListHead", body, 738f, 84f, 24f, 24f);
|
||
|
|
UiKit.Label(row, "보유 영웅", 42, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), Vector2.zero, new Vector2(300f, 70f));
|
||
|
|
tabHero = UiKit.Btn("TabHero", row, "장수", 32, UiKit.Red, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-440f, 0f), new Vector2(190f, 76f), () => { soldiers = false; Refresh(); });
|
||
|
|
tabSoldier = UiKit.Btn("TabSoldier", row, "병사", 32, UiKit.Paper, UiKit.Ink, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-240f, 0f), new Vector2(190f, 76f), () => { soldiers = true; Refresh(); });
|
||
|
|
UiKit.Btn("Save", row, "저장", 34, UiKit.Red, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), Vector2.zero, new Vector2(220f, 76f), SaveNow);
|
||
|
|
list = UiKit.ScrollGrid("List", body, 24f, 24f, 836f, 12f, new Vector2(506f, 150f), new Vector2(20f, 18f), 2, out scroll);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void Pill(RectTransform board, string label, Color c, float y)
|
||
|
|
{
|
||
|
|
var p = UiKit.Box("Row_" + label, board, new Vector2(0f, 1f), new Vector2(0f, 0.5f), new Vector2(20f, y), new Vector2(140f, 62f), c);
|
||
|
|
UiKit.FillLabel(p.transform, label, 32, UiKit.White);
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void OnShow()
|
||
|
|
{
|
||
|
|
LoadPreset(S.Save.presetIdx, true);
|
||
|
|
}
|
||
|
|
|
||
|
|
public override bool OnBack()
|
||
|
|
{
|
||
|
|
if (dirty) Root.Toast("저장하지 않은 편성 변경은 취소됩니다");
|
||
|
|
dirty = false;
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void LoadPreset(int idx, bool quiet = false)
|
||
|
|
{
|
||
|
|
if (dirty && !quiet) Root.Toast("저장하지 않은 변경 취소");
|
||
|
|
preset = Mathf.Clamp(idx, 0, S.Save.presets.Count - 1);
|
||
|
|
work.Clear();
|
||
|
|
work.AddRange(S.Save.presets[preset].slots);
|
||
|
|
dirty = false;
|
||
|
|
selected = -1;
|
||
|
|
Refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
int Filled()
|
||
|
|
{
|
||
|
|
int n = 0;
|
||
|
|
foreach (var id in work)
|
||
|
|
if (!string.IsNullOrEmpty(id)) n++;
|
||
|
|
return n;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void TapSlot(int i)
|
||
|
|
{
|
||
|
|
if (selected < 0)
|
||
|
|
{
|
||
|
|
selected = i;
|
||
|
|
}
|
||
|
|
else if (selected == i)
|
||
|
|
{
|
||
|
|
if (!string.IsNullOrEmpty(work[i]))
|
||
|
|
{
|
||
|
|
if (Filled() > 1)
|
||
|
|
{
|
||
|
|
work[i] = "";
|
||
|
|
dirty = true;
|
||
|
|
}
|
||
|
|
else Root.Toast("최소 1명은 출전해야 합니다");
|
||
|
|
}
|
||
|
|
selected = -1;
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
Swap(selected, i);
|
||
|
|
selected = -1;
|
||
|
|
}
|
||
|
|
Refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void TapCard(string id)
|
||
|
|
{
|
||
|
|
int at = work.IndexOf(id);
|
||
|
|
if (at >= 0)
|
||
|
|
{
|
||
|
|
if (Filled() > 1)
|
||
|
|
{
|
||
|
|
work[at] = "";
|
||
|
|
dirty = true;
|
||
|
|
}
|
||
|
|
else Root.Toast("최소 1명은 출전해야 합니다");
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
int target = selected >= 0 ? selected : work.IndexOf("");
|
||
|
|
if (target < 0) Root.Toast("빈 자리가 없습니다 — 자리를 먼저 탭해 고른 뒤 카드를 탭(교체)");
|
||
|
|
else
|
||
|
|
{
|
||
|
|
work[target] = id;
|
||
|
|
dirty = true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
selected = -1;
|
||
|
|
Refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
/// <summary>id 를 자리 slot 에 넣는다(이미 다른 자리에 있으면 맞바꿈).</summary>
|
||
|
|
public void Place(string id, int slot)
|
||
|
|
{
|
||
|
|
if (slot < 0 || slot >= work.Count || string.IsNullOrEmpty(id)) return;
|
||
|
|
int at = work.IndexOf(id);
|
||
|
|
if (at >= 0) Swap(at, slot);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
work[slot] = id;
|
||
|
|
dirty = true;
|
||
|
|
}
|
||
|
|
Refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
public void Swap(int a, int b)
|
||
|
|
{
|
||
|
|
if (a == b) return;
|
||
|
|
var t = work[a];
|
||
|
|
work[a] = work[b];
|
||
|
|
work[b] = t;
|
||
|
|
dirty = true;
|
||
|
|
}
|
||
|
|
|
||
|
|
public void SaveNow()
|
||
|
|
{
|
||
|
|
var why = S.SavePreset(preset, work);
|
||
|
|
if (why != null)
|
||
|
|
{
|
||
|
|
Root.Toast(why);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
dirty = false;
|
||
|
|
Root.Toast("프리셋 " + (preset + 1) + " 저장 · 출전 편성으로 지정");
|
||
|
|
Refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- 끌기 ----------
|
||
|
|
|
||
|
|
void StartGhost(string id, PointerEventData e)
|
||
|
|
{
|
||
|
|
if (string.IsNullOrEmpty(id)) return;
|
||
|
|
if (ghost != null) Object.Destroy(ghost.gameObject);
|
||
|
|
ghost = UiKit.Portrait("Ghost", Root.TopRoot, S.Def(id), new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, new Vector2(170f, 170f), true, 2.1f, UiKit.Red);
|
||
|
|
ghost.gameObject.name = "Ghost_" + id;
|
||
|
|
MoveGhost(e);
|
||
|
|
}
|
||
|
|
|
||
|
|
void MoveGhost(PointerEventData e)
|
||
|
|
{
|
||
|
|
if (ghost == null) return;
|
||
|
|
Vector2 lp;
|
||
|
|
if (RectTransformUtility.ScreenPointToLocalPointInRectangle(Root.TopRoot, e.position, Root.Cam, out lp)) ghost.anchoredPosition = lp;
|
||
|
|
}
|
||
|
|
|
||
|
|
int SlotAt(Vector2 screen)
|
||
|
|
{
|
||
|
|
for (int i = 0; i < slots.Count; i++)
|
||
|
|
if (RectTransformUtility.RectangleContainsScreenPoint(slots[i], screen, Root.Cam)) return i;
|
||
|
|
return -1;
|
||
|
|
}
|
||
|
|
|
||
|
|
void DropFromSlot(int from, PointerEventData e)
|
||
|
|
{
|
||
|
|
EndGhost();
|
||
|
|
int to = SlotAt(e.position);
|
||
|
|
if (to < 0 || to == from) return;
|
||
|
|
Swap(from, to);
|
||
|
|
selected = -1;
|
||
|
|
Refresh();
|
||
|
|
}
|
||
|
|
|
||
|
|
void DropCard(string id, PointerEventData e)
|
||
|
|
{
|
||
|
|
EndGhost();
|
||
|
|
int to = SlotAt(e.position);
|
||
|
|
if (to >= 0) Place(id, to);
|
||
|
|
}
|
||
|
|
|
||
|
|
void EndGhost()
|
||
|
|
{
|
||
|
|
if (ghost == null) return;
|
||
|
|
ghost.gameObject.SetActive(false);
|
||
|
|
Object.Destroy(ghost.gameObject);
|
||
|
|
ghost = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------- 그리기 ----------
|
||
|
|
|
||
|
|
public override void Refresh()
|
||
|
|
{
|
||
|
|
for (int i = 0; i < presetBtns.Count; i++)
|
||
|
|
{
|
||
|
|
bool on = i == preset;
|
||
|
|
presetBtns[i].GetComponent<Image>().color = on ? UiKit.Red : UiKit.Paper;
|
||
|
|
var t = UiKit.LabelOf(presetBtns[i]);
|
||
|
|
t.color = on ? UiKit.White : UiKit.Ink;
|
||
|
|
t.text = "프리셋 " + (i + 1) + (i == S.Save.presetIdx ? " <size=22>출전</size>" : "") + (on && dirty ? "*" : "");
|
||
|
|
}
|
||
|
|
int front = S.Db.Economy.FormationFront;
|
||
|
|
for (int i = 0; i < slots.Count; i++)
|
||
|
|
{
|
||
|
|
var s = slots[i];
|
||
|
|
UiKit.Clear(s);
|
||
|
|
var id = i < work.Count ? work[i] : "";
|
||
|
|
var h = S.Def(id);
|
||
|
|
if (i == selected) UiKit.Dot("Select", s, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 12f), 236f, UiKit.Star);
|
||
|
|
Color ring = i < front ? UiKit.Red : UiKit.Dark;
|
||
|
|
UiKit.Dot("Ring", s, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 0f), 212f, ring);
|
||
|
|
if (h != null)
|
||
|
|
{
|
||
|
|
UiKit.Portrait("Face", s, h, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -10f), new Vector2(192f, 192f), true, 2.1f, new Color(0.94f, 0.91f, 0.85f));
|
||
|
|
var tag = UiKit.Box("Name", s, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 4f), new Vector2(170f, 50f), UiKit.Dark);
|
||
|
|
UiKit.FillLabel(tag.transform, h.nameKo, 28, UiKit.White);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
UiKit.Dot("Empty", s, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -10f), 192f, new Color(0.9f, 0.87f, 0.82f));
|
||
|
|
UiKit.Label(s, "+\n<size=26>빈 칸</size>", 60, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -40f), new Vector2(200f, 140f));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
tabHero.GetComponent<Image>().color = soldiers ? UiKit.Paper : UiKit.Red;
|
||
|
|
UiKit.LabelOf(tabHero).color = soldiers ? UiKit.Ink : UiKit.White;
|
||
|
|
tabSoldier.GetComponent<Image>().color = soldiers ? UiKit.Red : UiKit.Paper;
|
||
|
|
UiKit.LabelOf(tabSoldier).color = soldiers ? UiKit.White : UiKit.Ink;
|
||
|
|
UiKit.Clear(list);
|
||
|
|
foreach (var h in S.Owned(soldiers))
|
||
|
|
{
|
||
|
|
var id = h.id;
|
||
|
|
var card = HeroCards.Card(list, S, h, work.Contains(id), true, null);
|
||
|
|
var td = card.gameObject.AddComponent<TapDrag>();
|
||
|
|
td.OnTap = () => TapCard(id);
|
||
|
|
td.OnDragStart = e => StartGhost(id, e);
|
||
|
|
td.OnDragMove = MoveGhost;
|
||
|
|
td.OnDragEnd = e => DropCard(id, e);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|