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" , "투기장" , "적장 토벌" } ;
2026-11-09 03:24:04 +00:00
static readonly string [ ] Ribbons = { "ribbon_blue" , "ribbon_purple" , "ribbon_red" , "ribbon_orange" } ; // 카드 리본(설계서 §3⑧ · 테마 스킨 id)
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 ( )
{
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)
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 ;
2026-11-09 09:12:53 +00:00
if ( UISkin . Has ( "card4_bg" ) & & UISkin . Has ( "btn_l_yellow" ) )
{
CardsKit ( text , w , h ) ;
return ;
}
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 ) ) ;
2026-11-09 03:24:04 +00:00
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 ) ;
}
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 ) ;
}
2026-11-09 03:20:32 +00:00
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 ) ;
2026-11-05 15:22:54 +00:00
st . raycastTarget = false ;
UiKit . FillLabel ( st . transform , text [ i , 3 ] , 26 , UiKit . Red ) ;
}
}
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" } ,
} ;
2026-11-09 09:23:18 +00:00
static readonly string [ ] EntryIcons = { "cur_ticket" , "lobby_event" , "sword" , "fire" } ;
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 )
{
2026-11-09 09:23:18 +00:00
// 「이벤트」 = 카드 안 오른쪽 위 꼬리 칩(카드 밖 돌출 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 ) ;
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 ) ) ) ;
}
}
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 ) ;
2026-11-09 03:20:32 +00:00
box . GetComponent < Image > ( ) . color = open ? UiKit . Paper : UiKit . Slot ;
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 ) ;
}
}
}