ThreeKingdomsSlingshot/Assets/Scripts/OutGame/Arena/ArenaScreen.cs

455 lines
29 KiB
C#
Raw Permalink Normal View History

아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
namespace TKS.OutGame
{
/// <summary>
/// 연무장(로비 출정 우측 · 기획 S13 · 목업 img09 · PD #1093 C) — 첫 페이지 = 모드 카드 4(규칙 · 보상 · 입장) → 카드 탭 = 모드 페이지:
/// ① 천하제일 탑(다음 층 · 적 템플릿 · 배율 · 보상 · 입장권 + 광고) ② 일일 던전 3(요일 · 남은 입장 · 보상) ③ 투기장(메달 · 주간 순위 · 샘플 상대 10 · 메달 상점)
/// ④ 적장 토벌(이번 주 보스 · 누적 피해 · 하루 1). 도전 = OutGameRoot.StartMode → BattleManager.StartMode(적 편성 · 배율 · 제한 턴) → 결과 패널 → 탭 = 이 페이지로.
/// </summary>
public class ArenaScreen : OutGameScreen
{
public static readonly string[] ModeNames = { "천하제일 탑", "일일 던전 3", "투기장", "적장 토벌" };
static readonly string[] Ribbons = { "ribbon_blue", "ribbon_purple", "ribbon_red", "ribbon_orange" }; // 카드 리본(설계서 §3⑧ · 테마 스킨 id)
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
int page;
RectTransform root;
/// <summary>0 = 카드 · 1 탑 · 2 던전 · 3 투기장 · 4 적장 토벌.</summary>
public int Page
{
get { return page; }
}
public override void Build()
{
보물 · 연무장 · 내정(순찰) · 전투 HUD 비주얼 개선 = Fable 제안서 §4-5~§4-8 구현(#1104 · PD 2026-11-09) - 보물: 목록 바탕 · 서브탭 · 보유 칸 = 보물 아이콘(납품 tre_* 없으면 기본 스탯별 팩 임시 tre_stat_* · 미보유 흰 25 % 실루엣) + 이름 띠 CardFrame01_Bottom · 등급 범례 칩 Round10 · 효과 줄 칩 상자 탭 = 포인트 띠 BannerFrame05 + 가는 주황 바 + 보상 상자 · 상자 카드 CardFrame03(상자 색 + 안쪽 + 광택) + 상자 300 + 뒤 금빛 · 등급 칩 + % 한 줄 · 화살표 아이콘 · 열기 Primary 노랑 + 보유 칩(없으면 회색) · 상자 행 ItemFrame 150 + 수량 칩 + 선택 FocusLine 깜빡 상자 색 = 코드 상수(ChestColor) → 테마 chest_<id> · 칸 색 chest_<id>_bg/deco/light/glow(C45) - 연무장: 목록 바탕 · 카드 = 일러 자리(픽토그램 256 흰 25 % · 임시) + 요약 한 줄 + 어두운 판 칩 2줄(입장 아이콘 + 값 · 보상 칸 76) + 상태 칩 · 규칙 문장 → ⓘ 팝업 · 적장 토벌 「이벤트」 뱃지 - 내정 순찰: 목록 바탕 · 서브탭 · 섹션 「순찰」(레벨형 게이지 군량색 + 배지 「7h」) · 「시간당 수확」 스탯 칸 4(아이콘 64 · 값 44 Sen) · 「쌓인 보상」 타일 4 + 수량 칩 · 수확 Primary 녹 · 광고 2배 Secondary 하늘 + 횟수 · 즉시 Secondary 보라 + 보옥 비용 - 전투 HUD: 유닛 판(초상 원 마스크 + 이름 · 병종 원 · 체력 바 색 단계 · 공/방 칩 · 슬링/사거리 칩 · 상태) · 수 칩 = 아군/적 아이콘 + 숫자 · 스킬 사용 가능 = 뒤 금빛 호흡 · 기본 조작 문장 삭제(상황 안내 유지) - 공통: Sen 에 「×」 글리프가 없어 수량은 「x」 · 회색 버튼 글자 흰 외곽선 · 금빛 틴트 α 1(Glow_Circle01 가운데 α 0.5 실측) - 테스트 117/117 · console error 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:12:53 +00:00
if (UISkin.On) UiKit.Paint(UiKit.Fill("ListBg", Rt), UiKit.Col("list_bg", UiKit.Bg), false); // 목록형 바탕(Fable §5-3)
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
Header("연무장", "탑 · 일일 던전 · 투기장 · 적장 토벌", true);
root = Body();
}
public void SetPage(int p)
{
page = Mathf.Clamp(p, 0, 4);
Refresh();
}
public override void OnShow()
{
S.Do(() =>
{
ArenaRules.RollDaily(S.Db, S.Save, S.Now);
ArenaRules.EnsureSeason(S.Db, S.Save, S.Now, S.Rng);
ArenaRules.RaidBoss(S.Db, S.Save, S.Now);
return null;
});
}
public override bool OnBack()
{
if (page == 0) return false;
SetPage(0);
return true;
}
public override void Refresh()
{
UiKit.Clear(root);
switch (page)
{
case 0: BuildCards(); break;
case 1: BuildTower(); break;
case 2: BuildDungeons(); break;
case 3: BuildArena(); break;
case 4: BuildRaid(); break;
}
}
// ---------- 카드 4 ----------
void BuildCards()
{
var db = S.Db;
var d = S.Save;
var e = db.Economy;
var now = S.Now;
string boss = db.Heroes.Get(ArenaRules.RaidBoss(db, d, now)) != null ? db.Heroes.Get(ArenaRules.RaidBoss(db, d, now)).nameKo : "";
string[,] text =
{
{ "100층 · 층마다 고정 편성 적 · 10층마다 보스", "보옥 · 도면 · 10층마다 보물 금 상자", "입장권 하루 " + e.TowerTicketsDaily + "(광고 +" + e.TowerAdTickets + ") · 진행 저장", "다음 " + ArenaRules.TowerNext(db, d) + "층 · 입장권 " + d.arena.towerTickets },
{ "군량고(골드) · 대장간(승급 장비·운철) · 사당(스킬서·영웅 조각) — 요일 순환", "던전별 재화", "각 하루 " + (db.Dungeons.dungeons.Count > 0 ? db.Dungeons.dungeons[0].entriesPerDay : 0) + "(패스 +1 준비 중)", "오늘 열림 " + OpenCount(now) + "곳" },
{ "비동기 5v5 · 상대 편성은 AI 가 조작(적 AI 재사용) · 주간 랭킹", "투기장 메달 → 영웅 조각 상점", "하루 " + db.Arena.EntriesPerDay, "메달 " + d.arena.medals + " · " + ArenaRules.Rank(d) + "위 · 남은 " + ArenaRules.ArenaLeft(db, d, now) },
{ "주간 보스(여포·조조 …) · 누적 피해", "보물 명장 상자", "하루 " + e.RaidDaily, "이번 주 " + boss + " · 누적 " + d.arena.raidDamage.ToString("N0") },
};
float w = 508f, h = 700f;
보물 · 연무장 · 내정(순찰) · 전투 HUD 비주얼 개선 = Fable 제안서 §4-5~§4-8 구현(#1104 · PD 2026-11-09) - 보물: 목록 바탕 · 서브탭 · 보유 칸 = 보물 아이콘(납품 tre_* 없으면 기본 스탯별 팩 임시 tre_stat_* · 미보유 흰 25 % 실루엣) + 이름 띠 CardFrame01_Bottom · 등급 범례 칩 Round10 · 효과 줄 칩 상자 탭 = 포인트 띠 BannerFrame05 + 가는 주황 바 + 보상 상자 · 상자 카드 CardFrame03(상자 색 + 안쪽 + 광택) + 상자 300 + 뒤 금빛 · 등급 칩 + % 한 줄 · 화살표 아이콘 · 열기 Primary 노랑 + 보유 칩(없으면 회색) · 상자 행 ItemFrame 150 + 수량 칩 + 선택 FocusLine 깜빡 상자 색 = 코드 상수(ChestColor) → 테마 chest_<id> · 칸 색 chest_<id>_bg/deco/light/glow(C45) - 연무장: 목록 바탕 · 카드 = 일러 자리(픽토그램 256 흰 25 % · 임시) + 요약 한 줄 + 어두운 판 칩 2줄(입장 아이콘 + 값 · 보상 칸 76) + 상태 칩 · 규칙 문장 → ⓘ 팝업 · 적장 토벌 「이벤트」 뱃지 - 내정 순찰: 목록 바탕 · 서브탭 · 섹션 「순찰」(레벨형 게이지 군량색 + 배지 「7h」) · 「시간당 수확」 스탯 칸 4(아이콘 64 · 값 44 Sen) · 「쌓인 보상」 타일 4 + 수량 칩 · 수확 Primary 녹 · 광고 2배 Secondary 하늘 + 횟수 · 즉시 Secondary 보라 + 보옥 비용 - 전투 HUD: 유닛 판(초상 원 마스크 + 이름 · 병종 원 · 체력 바 색 단계 · 공/방 칩 · 슬링/사거리 칩 · 상태) · 수 칩 = 아군/적 아이콘 + 숫자 · 스킬 사용 가능 = 뒤 금빛 호흡 · 기본 조작 문장 삭제(상황 안내 유지) - 공통: Sen 에 「×」 글리프가 없어 수량은 「x」 · 회색 버튼 글자 흰 외곽선 · 금빛 틴트 α 1(Glow_Circle01 가운데 α 0.5 실측) - 테스트 117/117 · console error 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:12:53 +00:00
if (UISkin.Has("card4_bg") && UISkin.Has("btn_l_yellow"))
{
CardsKit(text, w, h);
return;
}
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
for (int i = 0; i < 4; i++)
{
int k = i;
float x = 24f + (i % 2) * (w + 16f);
float y = -18f - (i / 2) * (h + 20f);
var card = UiKit.Btn("Card" + i, root, "", 30, UiKit.Paper, UiKit.Ink, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(x, y), new Vector2(w, h), () => SetPage(k + 1));
if (UISkin.Has("card4_bg") && UISkin.Has(Ribbons[i]))
{
// 카드(설계서 §3⑧) = 팩 CardFrame04 카드색 + 빛 + 테두리 · 글자 판 · 리본 제목
var ci = card.GetComponent<Image>();
UISkin.Apply(ci, "card4_bg");
ci.color = UiKit.Col("arena_" + i, UiKit.Paper);
UiKit.Layer(card.transform, "Glow", "card4_glow", UiKit.Col("arena_glow", UiKit.White), 0f, 0f, 0f, h * 0.5f);
UiKit.Layer(card.transform, "Inner", "panel12", UiKit.Col("arena_inner", UiKit.Paper), 14f, 112f, 14f, 100f);
UiKit.Layer(card.transform, "Border", "card4_border", UiKit.Col("card_border", UiKit.Line));
var rib = UiKit.Skin(UiKit.Rect("Ribbon", card.transform, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 4f), new Vector2(w - 40f, 96f)), Ribbons[i], UiKit.White);
var rt = UiKit.Title(UiKit.FillLabel(rib.transform, ModeNames[i] + (i == 3 ? " <size=24>(이벤트)</size>" : ""), 42, UiKit.White, TextAnchor.MiddleCenter, true, 40f), true);
rt.rectTransform.offsetMin = new Vector2(rt.rectTransform.offsetMin.x, 16f);
}
else
{
UiKit.Border(card.GetComponent<Image>(), UiKit.Line, 3f);
var head = UiKit.TopBand("Head", card.transform, 0f, 110f);
UiKit.Paint(head, UiKit.Red);
UiKit.FillLabel(head, ModeNames[i] + (i == 3 ? "\n<size=26>(이벤트)</size>" : ""), 44, UiKit.White);
}
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
string[] tags = { "규칙", "보상", "입장" };
for (int t = 0; t < 3; t++)
{
float ty = -128f - t * 160f;
var tag = UiKit.Box("Tag" + t, card.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, ty), new Vector2(96f, 44f), UiKit.Dark);
tag.raycastTarget = false;
UiKit.FillLabel(tag.transform, tags[t], 24, UiKit.White);
UiKit.Label(card.transform, text[i, t], 28, UiKit.Ink, TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, ty - 52f), new Vector2(w - 40f, 104f), false);
}
var st = UiKit.Box("Status", card.transform, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 16f), new Vector2(w - 32f, 70f), UiKit.Dark);
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
st.raycastTarget = false;
UiKit.FillLabel(st.transform, text[i, 3], 26, UiKit.Red);
}
}
보물 · 연무장 · 내정(순찰) · 전투 HUD 비주얼 개선 = Fable 제안서 §4-5~§4-8 구현(#1104 · PD 2026-11-09) - 보물: 목록 바탕 · 서브탭 · 보유 칸 = 보물 아이콘(납품 tre_* 없으면 기본 스탯별 팩 임시 tre_stat_* · 미보유 흰 25 % 실루엣) + 이름 띠 CardFrame01_Bottom · 등급 범례 칩 Round10 · 효과 줄 칩 상자 탭 = 포인트 띠 BannerFrame05 + 가는 주황 바 + 보상 상자 · 상자 카드 CardFrame03(상자 색 + 안쪽 + 광택) + 상자 300 + 뒤 금빛 · 등급 칩 + % 한 줄 · 화살표 아이콘 · 열기 Primary 노랑 + 보유 칩(없으면 회색) · 상자 행 ItemFrame 150 + 수량 칩 + 선택 FocusLine 깜빡 상자 색 = 코드 상수(ChestColor) → 테마 chest_<id> · 칸 색 chest_<id>_bg/deco/light/glow(C45) - 연무장: 목록 바탕 · 카드 = 일러 자리(픽토그램 256 흰 25 % · 임시) + 요약 한 줄 + 어두운 판 칩 2줄(입장 아이콘 + 값 · 보상 칸 76) + 상태 칩 · 규칙 문장 → ⓘ 팝업 · 적장 토벌 「이벤트」 뱃지 - 내정 순찰: 목록 바탕 · 서브탭 · 섹션 「순찰」(레벨형 게이지 군량색 + 배지 「7h」) · 「시간당 수확」 스탯 칸 4(아이콘 64 · 값 44 Sen) · 「쌓인 보상」 타일 4 + 수량 칩 · 수확 Primary 녹 · 광고 2배 Secondary 하늘 + 횟수 · 즉시 Secondary 보라 + 보옥 비용 - 전투 HUD: 유닛 판(초상 원 마스크 + 이름 · 병종 원 · 체력 바 색 단계 · 공/방 칩 · 슬링/사거리 칩 · 상태) · 수 칩 = 아군/적 아이콘 + 숫자 · 스킬 사용 가능 = 뒤 금빛 호흡 · 기본 조작 문장 삭제(상황 안내 유지) - 공통: Sen 에 「×」 글리프가 없어 수량은 「x」 · 회색 버튼 글자 흰 외곽선 · 금빛 틴트 α 1(Glow_Circle01 가운데 α 0.5 실측) - 테스트 117/117 · console error 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:12:53 +00:00
// 카드 그림 · 요약 · 보상 아이콘(테마 id · Fable §4-6 · 일러 N4 전까지 픽토그램 임시)
static readonly string[] Arts = { "arena_art_0", "arena_art_1", "arena_art_2", "arena_art_3" };
static readonly string[] Briefs = { "층마다 고정 편성 · 10층마다 보스", "요일 순환 재화 던전", "비동기 5v5 · 주간 랭킹", "주간 보스 · 누적 피해" };
static readonly string[][] RewardIcons =
{
new[] { "cur_gem", "cur_blueprint", "chest_gold" },
new[] { "cur_gold", "cur_iron", "cur_skillbook" },
new[] { "gear_flag", "cur_shard" },
new[] { "chest_general" },
};
static readonly string[] EntryIcons = { "cur_ticket", "lobby_event", "sword", "fire" };
보물 · 연무장 · 내정(순찰) · 전투 HUD 비주얼 개선 = Fable 제안서 §4-5~§4-8 구현(#1104 · PD 2026-11-09) - 보물: 목록 바탕 · 서브탭 · 보유 칸 = 보물 아이콘(납품 tre_* 없으면 기본 스탯별 팩 임시 tre_stat_* · 미보유 흰 25 % 실루엣) + 이름 띠 CardFrame01_Bottom · 등급 범례 칩 Round10 · 효과 줄 칩 상자 탭 = 포인트 띠 BannerFrame05 + 가는 주황 바 + 보상 상자 · 상자 카드 CardFrame03(상자 색 + 안쪽 + 광택) + 상자 300 + 뒤 금빛 · 등급 칩 + % 한 줄 · 화살표 아이콘 · 열기 Primary 노랑 + 보유 칩(없으면 회색) · 상자 행 ItemFrame 150 + 수량 칩 + 선택 FocusLine 깜빡 상자 색 = 코드 상수(ChestColor) → 테마 chest_<id> · 칸 색 chest_<id>_bg/deco/light/glow(C45) - 연무장: 목록 바탕 · 카드 = 일러 자리(픽토그램 256 흰 25 % · 임시) + 요약 한 줄 + 어두운 판 칩 2줄(입장 아이콘 + 값 · 보상 칸 76) + 상태 칩 · 규칙 문장 → ⓘ 팝업 · 적장 토벌 「이벤트」 뱃지 - 내정 순찰: 목록 바탕 · 서브탭 · 섹션 「순찰」(레벨형 게이지 군량색 + 배지 「7h」) · 「시간당 수확」 스탯 칸 4(아이콘 64 · 값 44 Sen) · 「쌓인 보상」 타일 4 + 수량 칩 · 수확 Primary 녹 · 광고 2배 Secondary 하늘 + 횟수 · 즉시 Secondary 보라 + 보옥 비용 - 전투 HUD: 유닛 판(초상 원 마스크 + 이름 · 병종 원 · 체력 바 색 단계 · 공/방 칩 · 슬링/사거리 칩 · 상태) · 수 칩 = 아군/적 아이콘 + 숫자 · 스킬 사용 가능 = 뒤 금빛 호흡 · 기본 조작 문장 삭제(상황 안내 유지) - 공통: Sen 에 「×」 글리프가 없어 수량은 「x」 · 회색 버튼 글자 흰 외곽선 · 금빛 틴트 α 1(Glow_Circle01 가운데 α 0.5 실측) - 테스트 117/117 · console error 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:12:53 +00:00
/// <summary>
/// 모드 카드(Fable 제안서 §4-6 · #1104) = CardFrame04 카드색 + 리본 제목(유지) · 위 일러 자리(픽토그램 220 흰 25 %) + 요약 한 줄 ·
/// 아래 어두운 판에 칩 2줄(입장 = 아이콘 + 값 · 보상 = 아이템 칸 72) · 상태 칩 · 규칙 문장 → ⓘ 팝업 · 이벤트 = 오른쪽 위 뱃지.
/// </summary>
void CardsKit(string[,] text, float w, float h)
{
var d = S.Save;
var db = S.Db;
var now = S.Now;
string[] entry =
{
"입장권 " + d.arena.towerTickets + " / " + db.Economy.TowerTicketsDaily,
"오늘 " + OpenCount(now) + "곳 · 각 " + (db.Dungeons.dungeons.Count > 0 ? db.Dungeons.dungeons[0].entriesPerDay : 0) + "회",
"남은 " + ArenaRules.ArenaLeft(db, d, now) + " / " + db.Arena.EntriesPerDay,
"하루 " + db.Economy.RaidDaily + "회",
};
var soft = UiKit.Col("text_soft", UiKit.InkSoft);
for (int i = 0; i < 4; i++)
{
int k = i;
float x = 24f + (i % 2) * (w + 16f);
float y = 18f + (i / 2) * (h + 20f);
var ci = UiKit.Skin(UiKit.At(root, "Card" + i, x, y, w, h), "card4_bg", UiKit.Col("arena_" + i, UiKit.Paper), true);
var card = ci.transform;
card.gameObject.AddComponent<Button>().onClick.AddListener(() => SetPage(k + 1));
UiKit.Layer(card, "Glow", "card4_glow", UiKit.Col("arena_glow", UiKit.White), 0f, 0f, 0f, h * 0.5f);
// 일러 자리 = 픽토그램(흰 25 %)
var art = UiKit.Icon(card, Arts[i], new Vector2(0.5f, 1f), new Vector2(0.5f, 0.5f), new Vector2(0f, -232f), 220f);
if (art != null) art.color = UiKit.Col("arena_art", new Color(1f, 1f, 1f, 0.25f));
var brief = UiKit.Title(UiKit.Label(card, Briefs[i], 28, UiKit.White, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -356f), new Vector2(w - 40f, 44f)), true);
brief.horizontalOverflow = HorizontalWrapMode.Overflow;
// 칩 2줄 판
var board = UiKit.Skin(UiKit.At(card, "Inner", 14f, 414f, w - 28f, 182f), "panel12", UiKit.Col("arena_inner", UiKit.Paper)).transform;
UiKit.Label(board, "입장", 26, soft, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, -14f), new Vector2(80f, 56f));
UiKit.InfoChip(board, "Entry", 96f, 16f, w - 28f - 96f - 18f, 52f, EntryIcons[i], entry[i], 26);
UiKit.Label(board, "보상", 26, soft, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(20f, -98f), new Vector2(80f, 72f));
for (int r = 0; r < RewardIcons[i].Length; r++)
UiKit.RewardTile(board, "Rw" + r, 96f + r * 86f, 92f, 76f, "gear_white_", RewardIcons[i][r], null, false);
// 상태 칩
var st = UiKit.Skin(UiKit.At(card, "Status", 16f, h - 86f, w - 32f, 66f), "chip", UiKit.Col("chip_dark", UiKit.Dark));
var stt = UiKit.Title(UiKit.FillLabel(st.transform, text[i, 3], 26, UiKit.Col("lv_fill", UiKit.Coin), TextAnchor.MiddleCenter, true, 16f), true);
stt.horizontalOverflow = HorizontalWrapMode.Overflow;
// 리본 제목(맨 위) · 이벤트 뱃지 · ⓘ
var rib = UiKit.Skin(UiKit.Rect("Ribbon", card, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, 4f), new Vector2(w - 40f, 96f)), Ribbons[i], UiKit.White);
var rt = UiKit.Title(UiKit.FillLabel(rib.transform, ModeNames[i], 42, UiKit.White, TextAnchor.MiddleCenter, true, 40f), true);
rt.rectTransform.offsetMin = new Vector2(rt.rectTransform.offsetMin.x, 16f);
if (i == 3)
{
// 「이벤트」 = 카드 안 오른쪽 위 꼬리 칩(카드 밖 돌출 0 · 검수2 결함 7)
var ev = UiKit.Skin(UiKit.Rect("Event", card, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-20f, -102f), new Vector2(120f, 44f)), "tail", UiKit.Col("tail_red", UiKit.Red));
UiKit.Title(UiKit.FillLabel(ev.transform, "이벤트", 22, UiKit.White), true);
보물 · 연무장 · 내정(순찰) · 전투 HUD 비주얼 개선 = Fable 제안서 §4-5~§4-8 구현(#1104 · PD 2026-11-09) - 보물: 목록 바탕 · 서브탭 · 보유 칸 = 보물 아이콘(납품 tre_* 없으면 기본 스탯별 팩 임시 tre_stat_* · 미보유 흰 25 % 실루엣) + 이름 띠 CardFrame01_Bottom · 등급 범례 칩 Round10 · 효과 줄 칩 상자 탭 = 포인트 띠 BannerFrame05 + 가는 주황 바 + 보상 상자 · 상자 카드 CardFrame03(상자 색 + 안쪽 + 광택) + 상자 300 + 뒤 금빛 · 등급 칩 + % 한 줄 · 화살표 아이콘 · 열기 Primary 노랑 + 보유 칩(없으면 회색) · 상자 행 ItemFrame 150 + 수량 칩 + 선택 FocusLine 깜빡 상자 색 = 코드 상수(ChestColor) → 테마 chest_<id> · 칸 색 chest_<id>_bg/deco/light/glow(C45) - 연무장: 목록 바탕 · 카드 = 일러 자리(픽토그램 256 흰 25 % · 임시) + 요약 한 줄 + 어두운 판 칩 2줄(입장 아이콘 + 값 · 보상 칸 76) + 상태 칩 · 규칙 문장 → ⓘ 팝업 · 적장 토벌 「이벤트」 뱃지 - 내정 순찰: 목록 바탕 · 서브탭 · 섹션 「순찰」(레벨형 게이지 군량색 + 배지 「7h」) · 「시간당 수확」 스탯 칸 4(아이콘 64 · 값 44 Sen) · 「쌓인 보상」 타일 4 + 수량 칩 · 수확 Primary 녹 · 광고 2배 Secondary 하늘 + 횟수 · 즉시 Secondary 보라 + 보옥 비용 - 전투 HUD: 유닛 판(초상 원 마스크 + 이름 · 병종 원 · 체력 바 색 단계 · 공/방 칩 · 슬링/사거리 칩 · 상태) · 수 칩 = 아군/적 아이콘 + 숫자 · 스킬 사용 가능 = 뒤 금빛 호흡 · 기본 조작 문장 삭제(상황 안내 유지) - 공통: Sen 에 「×」 글리프가 없어 수량은 「x」 · 회색 버튼 글자 흰 외곽선 · 금빛 틴트 α 1(Glow_Circle01 가운데 α 0.5 실측) - 테스트 117/117 · console error 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-09 09:12:53 +00:00
}
var info = UiKit.Rect("Info", card, new Vector2(0f, 1f), new Vector2(0.5f, 0.5f), new Vector2(46f, -122f), new Vector2(60f, 60f));
UiKit.Skin(info, "badge_circle", UiKit.Col("chip_dark", UiKit.Dark), true);
if (UiKit.Icon(info, "info", new Vector2(0.5f, 0.5f), new Vector2(0.5f, 0.5f), Vector2.zero, 32f) == null) UiKit.Title(UiKit.FillLabel(info, "i", 30, UiKit.White), true);
string rules = "<b>규칙</b> " + text[i, 0] + "\n\n<b>보상</b> " + text[i, 1] + "\n\n<b>입장</b> " + text[i, 2];
info.gameObject.AddComponent<Button>().onClick.AddListener(() => Root.Modal(ModeNames[k], rules, new OutGameRoot.ModalButton("확인", UiKit.Dark, null)));
}
}
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
int OpenCount(System.DateTime now)
{
int n = 0;
foreach (var dg in S.Db.Dungeons.dungeons)
if (ArenaRules.OpenToday(dg, now)) n++;
return n;
}
RectTransform Panel(string name, float top, float h)
{
var band = UiKit.TopBand(name, root, top, h, 24f, 24f);
var img = UiKit.Paint(band, UiKit.Paper);
UiKit.Border(img, UiKit.Line, 3f);
return band;
}
void Title(string t)
{
var band = UiKit.TopBand("Title", root, 12f, 96f, 24f, 24f);
UiKit.Paint(band, UiKit.Red);
UiKit.FillLabel(band, t, 48, UiKit.White);
}
// ---------- ① 천하제일 탑 ----------
void BuildTower()
{
var db = S.Db;
var d = S.Save;
Title("천하제일 탑");
int next = ArenaRules.TowerNext(db, d);
var f = db.Tower.Get(next);
var stage = ArenaRules.TowerStage(db, Root.Battle.Config, next);
var info = Panel("Floor", 124f, 520f);
UiKit.Label(info, next + "층", 110, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -20f), new Vector2(600f, 140f));
var sb = new System.Text.StringBuilder();
sb.Append("클리어 최고 ").Append(d.arena.towerFloor).Append("층 / ").Append(db.Tower.Top).Append("층\n");
if (f != null)
{
sb.Append("적 편성 = ").Append(f.templateStage).Append(stage != null ? " 「" + stage.subtitle + "」 · " + stage.enemies.Count + "기" : " (템플릿 없음)").Append("\n");
sb.Append("적 HP ×").Append(f.hpMult.ToString("0.00")).Append(" · 공격 ×").Append(f.atkMult.ToString("0.00")).Append("\n");
sb.Append("보상 보옥 ").Append(f.rewardGem).Append(" · 도면 ").Append(f.rewardBlueprint);
if (f.chest.Length > 0) sb.Append(" · ").Append(db.Chests.Get(f.chest).nameKo);
}
UiKit.Label(info, sb.ToString(), 34, UiKit.Ink, TextAnchor.UpperCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -180f), new Vector2(980f, 320f), false).lineSpacing = 1.2f;
var tk = Panel("Tickets", 664f, 150f);
UiKit.Label(tk, "입장권 " + d.arena.towerTickets + " <size=28>(매일 " + db.Economy.TowerTicketsDaily + "장까지 채움 · 입장 = 1장)</size>", 40, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(24f, 0f), new Vector2(720f, 80f));
var ad = UiKit.Btn("Ad", tk, "광고 +" + db.Economy.TowerAdTickets + "\n<size=24>(시험 · " + d.dailyC.towerAd + "/1)</size>", 32, UiKit.Grain, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-16f, 0f), new Vector2(240f, 110f), () =>
Root.Toast(S.Do(() => ArenaRules.TowerAd(S.Db, S.Save, S.Now)) ?? "입장권 +" + S.Db.Economy.TowerAdTickets + " (광고 SDK 미연동 · 시험 지급)"));
ad.interactable = d.dailyC.towerAd < 1;
var why = ArenaRules.CanTower(db, d, S.Now);
if (stage == null && why == null) why = "템플릿 스테이지 없음";
var go = UiKit.Btn("Go", root, why == null ? "도전 — " + next + "층" : why, 48, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -840f), new Vector2(1032f, 150f), () =>
{
var w = S.Do(() => ArenaRules.EnterTower(S.Db, S.Save, S.Now));
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Tower, "", next, ArenaRules.TowerStage(S.Db, Root.Battle.Config, next), 0, 1);
});
go.interactable = why == null;
UiKit.Label(root, "진행 저장 · 패배해도 층 유지(입장권만 소모)", 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -1010f), new Vector2(1000f, 50f), false);
}
// ---------- ② 일일 던전 ----------
void BuildDungeons()
{
var db = S.Db;
var d = S.Save;
var now = S.Now;
Title("일일 던전");
var today = DungeonDb.Weekdays[(int)now.ToLocalTime().DayOfWeek];
UiKit.Label(root, "오늘 = " + DayKo(today) + "요일 · 요일 순환 · 승리 시 입장 1회 소모", 30, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -116f), new Vector2(1000f, 50f), false);
for (int i = 0; i < db.Dungeons.dungeons.Count; i++)
{
var dg = db.Dungeons.dungeons[i];
bool open = ArenaRules.OpenToday(dg, now);
int left = ArenaRules.DungeonLeft(db, d, dg, now);
var box = Panel("Dungeon_" + dg.id, 180f + i * 330f, 310f);
box.GetComponent<Image>().color = open ? UiKit.Paper : UiKit.Slot;
아웃게임 2차(PD #1093 세션 C) — 내정(훈련·순찰·개발) · 보물(수집품·세트·상자) · 연무장(탑·일일 던전·투기장·적장 토벌) · 거래 상자 탭 + 영웅 56명 확장(PD #1096 · 신규 effect_key 4) - 데이터: C 표 10장(training · training_ranks · patrol · dev_tree · treasures · treasure_sets · chests(+iron/buy 열) · tower · dungeons(+reward_amount) · arena(+상점 키)) + economy.csv C 키 24 → 임포트 18 자산(OutGameTables.C.cs 검증: 행·연속 Lv/랭크/층·참조·확률 합 1·선행 노드·효과 키 처리기·카운터 키 · 오류 시 자산 불변) - 영웅 56명: heroes 71 · skills 112 · hero_gear 1344 · gacha_pool 71 재복사 · 신규 키 4(team_dmg_pct_turn · cooldown_add · charm · heal_self_pct) + 기존 키 조건 열(받는 피해 p2 HP 임계/보스/근접 · p3 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) · 대장군 키는 표 미사용(처리기 유지) - 내정: 훈련(병종 5 · 3항목 Lv · cost_mult · 랭크 승급 · train_bonus → §3-2 항목별) · 순찰(챕터 행 × 시간 · 오프라인 · 상한 12h + 농업 · 소수점 이월 · 광고 2배 스텁 · 즉시 수확 보옥) · 개발(갈래 3 · 선행 · 골드·도면 비용 · 효과 키 → 군량·보상·상점·순찰·상자 적용 지점 · 초기화 보옥 + 도면 환급) - 보물: 보유 중(4열 · 등급 색 · 상세·강화) · 세트(단계 효과) · 상자(포인트 → 명장 · 확률 · 중복 조각) · 효과 hp/atk/def → HeroRules · crit/range/splash → BattleManager.SideEffectsProvider → SkillMods · 조건 카운터 8종 = Runtime/BattleEvents(전투 코드 Raise 1줄씩 · 아군만) · 스테이지 청동 상자 10 % - 연무장: BattleManager.StartMode(BattleMode = 적 편성·제한 턴·onExit) · 탑 100층(입장권·층 배율·보상·10층 상자) · 던전 3(요일·2/일·보상) · 투기장(샘플 10 · EnemyAI 방어 · 메달·주간 순위·조각 상점) · 적장 토벌(주간 순환·hp ×20·5턴·누적 피해·명장 상자·천기) · 결과 패널 → 탭 = 연무장 - 거래: 상자 탭(구매 = 골드/보옥/포인트 · 거래 할인) · 패스·월정액·챕터 팩·첫 결제 팩 = 준비 중 스텁 · 스킬서 임시 판매 제거(사당 던전) - 테스트 87/87(기존 73 유지 · 갱신 3 = 로비 탭 · 표 키 목록 · 상점 스킬서 칸 / 신규 14 = OutGameCTests 12 + 확장 키 2) · console error 0 · 캡처 = DataImporter.CaptureC Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-05 15:22:54 +00:00
UiKit.Label(box, dg.nameKo, 52, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -16f), new Vector2(400f, 76f));
var days = new List<string>();
foreach (var w in dg.weekdays) days.Add(w == today ? "<color=#b0301e>" + DayKo(w) + "</color>" : DayKo(w));
UiKit.Label(box, "요일 " + string.Join(" · ", days.ToArray()), 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -100f), new Vector2(600f, 50f), false);
var rw = new List<string>();
foreach (var k in dg.rewardKinds) rw.Add(RewardKo(k) + " " + dg.Amount(k));
UiKit.Label(box, "보상 " + string.Join(" · ", rw.ToArray()), 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -152f), new Vector2(640f, 50f), false);
UiKit.Label(box, "적 = " + dg.templateStage + " 편성 · HP ×" + dg.hpMult.ToString("0.0#"), 28, UiKit.InkSoft, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(28f, -204f), new Vector2(640f, 50f), false);
string id = dg.id;
var why = ArenaRules.CanDungeon(db, d, id, now);
var b = UiKit.Btn("Go", box, open ? "입장\n<size=26>남은 " + left + "/" + dg.entriesPerDay + "</size>" : "닫힘", 40, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-24f, 0f), new Vector2(300f, 170f), () =>
{
var w = ArenaRules.CanDungeon(S.Db, S.Save, id, S.Now);
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Dungeon, id, 0, ArenaRules.DungeonStage(S.Db, Root.Battle.Config, id), 0, 2);
});
b.interactable = why == null;
}
}
static string DayKo(string w)
{
switch (w)
{
case "sun": return "일";
case "mon": return "월";
case "tue": return "화";
case "wed": return "수";
case "thu": return "목";
case "fri": return "금";
case "sat": return "토";
}
return w;
}
static string RewardKo(string k)
{
switch (k)
{
case "gold": return "골드";
case "gear": return "승급 장비";
case "iron": return "운철";
case "skill_book": return "스킬서";
case "shards": return "영웅 조각";
}
return k;
}
// ---------- ③ 투기장 ----------
void BuildArena()
{
var db = S.Db;
var d = S.Save;
var now = S.Now;
Title("투기장");
var me = Panel("Me", 124f, 200f);
int left = ArenaRules.ArenaLeft(db, d, now);
UiKit.Label(me, "메달 " + d.arena.medals + " · 주간 점수 " + d.arena.seasonScore + " · " + ArenaRules.Rank(d) + "위 / " + (d.arena.samples.Count + 1), 36, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -14f), new Vector2(980f, 56f));
var start = SaveStore.ParseIso(d.arena.seasonStart, now);
int daysLeft = Mathf.Max(0, db.Arena.SeasonDays - (int)(now - start).TotalDays);
UiKit.Label(me, "전적 " + d.arena.wins + "승 " + d.arena.losses + "패 · 오늘 남은 도전 " + left + "/" + db.Arena.EntriesPerDay + " · 시즌 " + daysLeft + "일 남음 · 승 +" + db.Arena.MedalWin + " / 패 +" + db.Arena.MedalLose + "\n" +
"<size=26>순위 보상 1~10위 보옥 " + db.Arena.RewardTop10Gem + " · 그 밖 " + db.Arena.RewardTop100Gem + (d.arena.lastSeason.Length > 0 ? " · " + d.arena.lastSeason : "") + "</size>",
30, UiKit.InkSoft, TextAnchor.UpperLeft, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(24f, -76f), new Vector2(980f, 110f), false);
UiKit.Btn("Shop", me, "메달 상점", 30, UiKit.Jade, UiKit.White, new Vector2(1f, 1f), new Vector2(1f, 1f), new Vector2(-16f, -12f), new Vector2(200f, 60f), MedalShop);
ScrollRect sr;
var list = UiKit.ScrollGrid("Opponents", root, 24f, 24f, 344f, 16f, new Vector2(1032f, 130f), new Vector2(0f, 12f), 1, out sr);
for (int i = 0; i < d.arena.samples.Count; i++)
{
var s = d.arena.samples[i];
int k = i;
var cell = UiKit.Cell(list, "Opp" + i, UiKit.Paper, null);
UiKit.Border(cell, UiKit.Line, 2f);
int power = 0;
for (int j = 0; j < s.heroes.Count; j++)
{
var h = db.Heroes.Get(s.heroes[j]);
if (h == null) continue;
power += HeroRules.Compute(db, Root.Battle.Config, h, new HeroState { id = h.id, lv = s.lv, grade = db.Promotion.FirstGrade, star = Mathf.Clamp(h.rarity, 1, db.Evolve.MaxStar) }).power;
UiKit.Portrait("H" + j, cell.transform, h, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(16f + j * 92f, 0f), new Vector2(84f, 104f), false, 1.8f, new Color(0.9f, 0.86f, 0.8f));
}
UiKit.Label(cell.transform, s.name + "\n<size=26>전투력 " + power.ToString("N0") + " · 점수 " + s.score + "</size>", 30, UiKit.Ink, TextAnchor.MiddleLeft, new Vector2(0f, 0.5f), new Vector2(0f, 0.5f), new Vector2(490f, 0f), new Vector2(330f, 110f));
var why = ArenaRules.CanArena(db, d, k, now);
var b = UiKit.Btn("Go", cell.transform, "도전", 34, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White, new Vector2(1f, 0.5f), new Vector2(1f, 0.5f), new Vector2(-16f, 0f), new Vector2(180f, 96f), () =>
{
var w = ArenaRules.CanArena(S.Db, S.Save, k, S.Now);
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Arena, "", k, ArenaRules.ArenaStage(S.Db, Root.Battle.Config, S.Save.arena.samples[k]), 0, 3);
});
b.interactable = why == null;
}
}
public void MedalShop()
{
var db = S.Db;
var d = S.Save;
var content = Root.ModalCustom("메달 상점 — 영웅 조각", 1500f, new OutGameRoot.ModalButton("닫기", UiKit.Dark, null));
UiKit.Label(content, "메달 " + d.arena.medals + " · " + db.Arena.ShopPrice + "메달 = 조각 " + db.Arena.ShopShards + " · 오늘 " + d.dailyC.arenaShop + "/" + db.Arena.ShopDaily, 30, UiKit.Ink, TextAnchor.MiddleCenter,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), Vector2.zero, new Vector2(860f, 50f));
ScrollRect sr;
var grid = UiKit.ScrollGrid("Heroes", content, 0f, 0f, 60f, 0f, new Vector2(200f, 260f), new Vector2(12f, 12f), 4, out sr);
foreach (var hs in d.heroes)
{
var h = db.Heroes.Get(hs.id);
if (h == null) continue;
string id = h.id;
var cell = UiKit.Cell(grid, "Buy_" + id, UiKit.Paper, () =>
{
var w = S.Do(() => ArenaRules.BuyShards(S.Db, S.Save, id, S.Now));
Root.Toast(w ?? h.nameKo + " 조각 +" + S.Db.Arena.ShopShards);
if (w == null) MedalShop();
});
UiKit.Portrait("Face", cell.transform, h, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -8f), new Vector2(150f, 150f), false, 1.8f, new Color(0.9f, 0.86f, 0.8f));
UiKit.Label(cell.transform, h.nameKo + "\n<size=22>조각 " + hs.shards + " · ★" + hs.star + "</size>", 26, UiKit.Ink, TextAnchor.MiddleCenter, new Vector2(0.5f, 0f), new Vector2(0.5f, 0f), new Vector2(0f, 8f), new Vector2(190f, 90f));
}
}
// ---------- ④ 적장 토벌 ----------
void BuildRaid()
{
var db = S.Db;
var d = S.Save;
var now = S.Now;
var e = db.Economy;
Title("적장 토벌 (이벤트)");
string bossId = ArenaRules.RaidBoss(db, d, now);
var h = db.Heroes.Get(bossId);
var stage = ArenaRules.RaidStage(db, Root.Battle.Config, bossId);
var box = Panel("Boss", 124f, 640f);
UiKit.Portrait("Boss", box, h, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -20f), new Vector2(320f, 320f), false, 1f, new Color(0.85f, 0.8f, 0.75f));
var sb = new System.Text.StringBuilder();
sb.Append("<size=52><b>이번 주 보스 — ").Append(h != null ? h.nameKo : bossId).Append("</b></size>\n");
sb.Append("보스 HP ×").Append(e.RaidHpMult.ToString("0.#")).Append(" · ").Append(e.RaidTurns).Append("턴 동안 피해를 누적").Append(stage != null ? " · 적 " + stage.enemies.Count + "기" : "").Append("\n");
sb.Append("이번 주 누적 피해 ").Append(d.arena.raidDamage.ToString("N0")).Append(" (").Append(d.arena.raidWeek).Append(" 주)\n");
sb.Append("보상 ").Append(db.Chests.Get(e.RaidChest).nameKo).Append(" · 천기 보물 확률 ").Append((e.RaidArcanaPct * 100f).ToString("0.#")).Append("%");
UiKit.Label(box, sb.ToString(), 32, UiKit.Ink, TextAnchor.UpperCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -360f), new Vector2(980f, 270f), false).lineSpacing = 1.15f;
var why = ArenaRules.CanRaid(db, d, now);
if (stage == null && why == null) why = "보스 편성 없음";
var go = UiKit.Btn("Go", root, why == null ? "토벌 — 남은 " + ArenaRules.RaidLeft(db, d, now) + "/" + e.RaidDaily : why, 48, why == null ? UiKit.Red : UiKit.Disabled, UiKit.White,
new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -790f), new Vector2(1032f, 150f), () =>
{
var w = ArenaRules.CanRaid(S.Db, S.Save, S.Now);
if (w != null)
{
Root.Toast(w);
return;
}
Root.StartMode(ArenaRules.Raid, bossId, 0, ArenaRules.RaidStage(S.Db, Root.Battle.Config, bossId), S.Db.Economy.RaidTurns, 4);
});
go.interactable = why == null;
UiKit.Label(root, "주간 보스 = 여포·조조 순환 · 주가 바뀌면 누적 초기화 · 천기 = 적장 토벌 전용", 28, UiKit.InkSoft, TextAnchor.MiddleCenter, new Vector2(0.5f, 1f), new Vector2(0.5f, 1f), new Vector2(0f, -960f), new Vector2(1000f, 50f), false);
}
}
}