2026-11-05 15:22:54 +00:00
using System.Collections.Generic ;
using UnityEngine ;
using UnityEngine.UI ;
namespace TKS.OutGame
{
/// <summary>
/// 내정(하단 탭 · 기획 S9~S11 · 목업 img13·05·14 · PD #1093 C) — 상단 탭 3(한 항목 = 한 페이지):
/// ① 훈련 = 병종 탭 5 · 랭크 바(현재 랭크 마커 + 랭크 보너스 · 다음 랭크 승급 골드 · 남은 레벨) · 카드 3(HP·공·방 Lv n/Max · 골드) · 3항목 Max → 랭크 승급
/// ② 순찰 = 기준 챕터 · 누적 n/최대 시간 · 시간당 수확 4줄 · 쌓인 보상 4칸 · 수확 · 광고 2배(스텁) · 즉시 수확(보옥)
/// ③ 개발 = 갈래 ◀ 상업·농업·공업 ▶ 한 페이지씩 · 세로 트리(선행 조건 깊이로 줄 · 노드 n/Max · 큰 노드) · 노드 탭 = 강화 팝업 · 도면·골드 · 트리 초기화.
/// </summary>
public class DomesticScreen : OutGameScreen
{
public static readonly string [ ] PageNames = { "훈련" , "순찰" , "개발" } ;
int page ;
int typeIdx ;
int branchIdx ;
readonly List < Button > tabs = new List < Button > ( ) ;
readonly List < RectTransform > pages = new List < RectTransform > ( ) ;
public int Page
{
get { return page ; }
}
/// <summary>병종 탭 순서 = training.csv 행 순서(보·창·기·궁·책).</summary>
public List < UnitType > TrainTypes
{
get
{
var l = new List < UnitType > ( ) ;
foreach ( var r in S . Db . Training . rows )
if ( ! l . Contains ( r . type ) ) l . Add ( r . type ) ;
return l ;
}
}
public UnitType TrainType
{
get
{
var l = TrainTypes ;
return l . Count > 0 ? l [ Mathf . Clamp ( typeIdx , 0 , l . Count - 1 ) ] : UnitType . Infantry ;
}
}
public string Branch
{
get
{
var b = S . Db . Dev . Branches ;
return b . Count > 0 ? b [ Mathf . Clamp ( branchIdx , 0 , b . Count - 1 ) ] : "" ;
}
}
/// <summary>개발 페이지에 그려진 노드 수(테스트).</summary>
public int NodeCount { get ; private set ; }
public override void Build ( )
{
Tab = 2 ;
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 ) ;
var body = Body ( ) ;
for ( int i = 0 ; i < PageNames . Length ; i + + )
{
int k = i ;
2026-11-09 09:12:53 +00:00
tabs . Add ( UISkin . On
? UiKit . SubTab ( body , "Tab" + i , PageNames [ i ] , 24f + i * 348f , 18f , 332f , 84f , ( ) = > SetPage ( k ) )
: UiKit . Btn ( "Tab" + i , body , PageNames [ i ] , 36 , UiKit . Paper , UiKit . Ink , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f + i * 348f , - 18f ) , new Vector2 ( 332f , 84f ) , ( ) = > SetPage ( k ) ) ) ;
2026-11-05 15:22:54 +00:00
pages . Add ( UiKit . Fill ( "Page" + i , body , 0f , 0f , 118f , 0f ) ) ;
}
}
public void SetPage ( int p )
{
page = Mathf . Clamp ( p , 0 , pages . Count - 1 ) ;
Refresh ( ) ;
}
public void SetTrainType ( UnitType t )
{
typeIdx = Mathf . Max ( 0 , TrainTypes . IndexOf ( t ) ) ;
Refresh ( ) ;
}
public void SetBranch ( int i )
{
int n = Mathf . Max ( 1 , S . Db . Dev . Branches . Count ) ;
branchIdx = ( ( i % n ) + n ) % n ;
Refresh ( ) ;
}
public override void Refresh ( )
{
for ( int i = 0 ; i < tabs . Count ; i + + )
{
bool on = i = = page ;
2026-11-09 09:12:53 +00:00
if ( UISkin . On ) UiKit . SetSubTab ( tabs [ i ] , on ) ;
else
{
tabs [ i ] . GetComponent < Image > ( ) . color = on ? UiKit . Red : UiKit . Paper ; // 선택 = 붉은 옻칠(다른 화면 탭과 같음)
UiKit . LabelOf ( tabs [ i ] ) . color = on ? UiKit . White : UiKit . Ink ;
}
2026-11-05 15:22:54 +00:00
pages [ i ] . gameObject . SetActive ( on ) ;
}
UiKit . Clear ( pages [ page ] ) ;
if ( page = = 0 ) BuildTraining ( pages [ 0 ] ) ;
2026-11-09 09:12:53 +00:00
else if ( page = = 1 )
{
if ( UISkin . Has ( "panel_field" ) & & UISkin . Has ( "btn_l_green" ) ) BuildPatrolKit ( pages [ 1 ] ) ;
else BuildPatrol ( pages [ 1 ] ) ;
}
2026-11-05 15:22:54 +00:00
else BuildDev ( pages [ 2 ] ) ;
}
// ---------- ① 훈련 ----------
void BuildTraining ( RectTransform p )
{
var db = S . Db ;
var d = S . Save ;
var t = TrainType ;
var types = TrainTypes ;
for ( int i = 0 ; i < types . Count ; i + + )
{
var ut = types [ i ] ;
bool on = ut = = t ;
UiKit . Btn ( "Type" + i , p , UnitTypeInfo . Mark ( ut ) , 40 , on ? UiKit . Red : UiKit . Paper , on ? UiKit . White : UiKit . Ink , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f + i * 208f , - 6f ) , new Vector2 ( 196f , 78f ) , ( ) = > SetTrainType ( ut ) ) ;
}
int rank = TrainingRules . Rank ( d , t ) ;
var stage = UiKit . TopBand ( "Unit" , p , 100f , 470f , 24f , 24f ) ;
UiKit . Paint ( stage , new Color ( 0.24f , 0.19f , 0.25f ) ) ;
UiKit . Label ( stage , TrainingRules . RankName ( db , rank ) + " <size=40>랭크 " + rank + "/" + db . Training . MaxRank + "</size>" , 60 , UiKit . White , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 20f ) , new Vector2 ( 900f , 90f ) ) ;
var halo = UiKit . Dot ( "Halo" , stage , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0f , - 40f ) , 300f , new Color ( 1f , 1f , 1f , 0.1f ) ) ;
halo . raycastTarget = false ;
var face = UiKit . Dot ( "Face" , stage , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0f , - 40f ) , 220f , UiKit . TypeColor ( t ) ) ;
UiKit . FillLabel ( face . transform , UnitTypeInfo . Mark ( t ) , 110 , UiKit . White ) ;
UiKit . Label ( stage , UnitTypeInfo . Korean ( t ) + " 영웅·병사 전원" , 30 , new Color ( 0.85f , 0.8f , 0.86f ) , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0f , 14f ) , new Vector2 ( 900f , 50f ) , false ) ;
// 랭크 바: 현재 랭크 마커 + 랭크 보너스 · 다음 랭크 승급 비용 · 남은 레벨
var bar = UiKit . TopBand ( "RankBar" , p , 586f , 230f , 24f , 24f ) ;
UiKit . Paint ( bar , new Color ( 0.17f , 0.14f , 0.19f ) ) ;
UiKit . Label ( bar , "랭크 진행" , 30 , new Color ( 0.85f , 0.8f , 0.86f ) , TextAnchor . MiddleLeft , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 12f ) , new Vector2 ( 300f , 50f ) ) ;
int left = TrainingRules . Remaining ( db , d , t ) ;
int total = db . Training . MaxLevel * TrainingTable . Stats . Length ;
var fill = UiKit . Bar ( "Progress" , bar , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 24f , 6f ) , new Vector2 ( 760f , 30f ) , new Color ( 0.32f , 0.28f , 0.35f ) , new Color ( 0.95f , 0.68f , 0.2f ) ) ;
float v = total > 0 ? ( total - left ) / ( float ) total : 0f ;
UiKit . SetBar ( fill , v ) ;
var marker = UiKit . Dot ( "Marker" , bar , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 24f + 760f * v , 6f ) , 70f , new Color ( 0.95f , 0.68f , 0.2f ) ) ;
UiKit . FillLabel ( marker . transform , "▲" , 34 , UiKit . Ink ) ;
var r = db . Training . RankOf ( rank ) ;
UiKit . Label ( bar , TrainingRules . RankName ( db , rank ) + " · 랭크 보너스 +" + ( r ! = null ? r . rankPct : 0f ) . ToString ( "0.#" ) + "%" , 32 , UiKit . White , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0f ) , new Vector2 ( 0f , 0f ) , new Vector2 ( 24f , 16f ) , new Vector2 ( 700f , 54f ) ) ;
string promo ;
if ( rank > = db . Training . MaxRank ) promo = "최종 랭크" ;
else promo = "다음 랭크 승급 비용\n<size=40>골드 × " + TrainingRules . PromoteCost ( db , d , t ) . ToString ( "N0" ) + "</size>" ;
UiKit . Label ( bar , promo , 26 , new Color ( 0.85f , 0.8f , 0.86f ) , TextAnchor . UpperRight , new Vector2 ( 1f , 1f ) , new Vector2 ( 1f , 1f ) , new Vector2 ( - 24f , - 12f ) , new Vector2 ( 300f , 110f ) , false ) ;
UiKit . Label ( bar , left + "\n<size=24>남은 레벨</size>" , 40 , UiKit . White , TextAnchor . LowerRight , new Vector2 ( 1f , 0f ) , new Vector2 ( 1f , 0f ) , new Vector2 ( - 24f , 12f ) , new Vector2 ( 200f , 90f ) ) ;
// 카드 3: HP · 공 · 방
string [ ] names = { "HP" , "공격력" , "방어력" } ;
string [ ] marks = { "H" , "공" , "방" } ;
float cw = 330f ;
for ( int i = 0 ; i < 3 ; i + + )
{
int k = i ;
int lv = TrainingRules . Lv ( d , t , i ) ;
bool max = lv > = db . Training . MaxLevel ;
2026-11-09 03:20:32 +00:00
var card = UiKit . Box ( "Card" + i , p , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f + i * ( cw + 21f ) , - 838f ) , new Vector2 ( cw , 360f ) , max ? UiKit . Purple : UiKit . Paper ) ;
2026-11-05 15:22:54 +00:00
UiKit . Border ( card , UiKit . Line , 3f ) ;
var head = UiKit . TopBand ( "Head" , card . transform , 0f , 72f ) ;
2026-11-09 03:20:32 +00:00
UiKit . Paint ( head , max ? UiKit . Purple : UiKit . Dark ) ;
2026-11-05 15:22:54 +00:00
UiKit . FillLabel ( head , max ? "Max" : "LV." + lv , 36 , max ? UiKit . White : UiKit . Ink ) ;
2026-11-09 03:20:32 +00:00
var dot = UiKit . Dot ( "Icon" , card . transform , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 86f ) , 96f , UiKit . Slot ) ;
2026-11-05 15:22:54 +00:00
UiKit . FillLabel ( dot . transform , marks [ i ] , 40 , UiKit . Ink ) ;
UiKit . Label ( card . transform , names [ i ] , 38 , UiKit . Ink , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 190f ) , new Vector2 ( 310f , 50f ) ) ;
UiKit . Label ( card . transform , "LV " + lv + "/" + db . Training . MaxLevel + " +" + TrainingRules . Bonus ( db , d , t , TrainingTable . Stats [ i ] ) . ToString ( "0.#" ) + "%" , 26 , UiKit . InkSoft , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 232f ) , new Vector2 ( 310f , 36f ) , false ) ;
if ( max ) UiKit . Label ( card . transform , "<color=#3a8a30>✔</color>" , 48 , UiKit . Ok , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0f , 20f ) , new Vector2 ( 300f , 70f ) ) ;
else
{
var b = UiKit . Btn ( "Up" , card . transform , TrainingRules . Cost ( db , d , t , i ) . ToString ( "N0" ) + " 골드" , 30 , UiKit . Coin , UiKit . Ink , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0f , 14f ) , new Vector2 ( 280f , 66f ) , ( ) = >
{
var why = S . Do ( ( ) = > TrainingRules . Level ( S . Db , S . Save , TrainType , k ) ) ;
if ( why ! = null ) Root . Toast ( why ) ;
} ) ;
b . interactable = TrainingRules . CanLevel ( db , d , t , i ) = = null ;
}
}
if ( left = = 0 & & rank < db . Training . MaxRank )
{
var pb = UiKit . Btn ( "Promote" , p , "랭크 승급 → " + TrainingRules . RankName ( db , rank + 1 ) + " · 골드 " + TrainingRules . PromoteCost ( db , d , t ) . ToString ( "N0" ) , 36 , UiKit . Red , UiKit . White , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0f , 90f ) , new Vector2 ( 900f , 110f ) , ( ) = >
{
var why = S . Do ( ( ) = > TrainingRules . Promote ( S . Db , S . Save , TrainType ) ) ;
Root . Toast ( why ? ? UnitTypeInfo . Korean ( TrainType ) + " 랭크 승급 → " + TrainingRules . RankName ( S . Db , TrainingRules . Rank ( S . Save , TrainType ) ) ) ;
} ) ;
pb . interactable = TrainingRules . CanPromote ( db , d , t ) = = null ;
}
UiKit . Label ( p , UnitTypeInfo . Korean ( t ) + " 전원 HP +" + TrainingRules . Bonus ( db , d , t , "hp" ) . ToString ( "0.#" ) + "% · 공 +" + TrainingRules . Bonus ( db , d , t , "atk" ) . ToString ( "0.#" ) + "% · 방 +" +
TrainingRules . Bonus ( db , d , t , "def" ) . ToString ( "0.#" ) + "% (3항목 Max → 랭크 승급 · 레벨 리셋 · 랭크 보너스 유지)" , 26 , UiKit . InkSoft , TextAnchor . MiddleCenter ,
new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0f , 20f ) , new Vector2 ( 1030f , 60f ) , false ) ;
}
// ---------- ② 순찰 ----------
void BuildPatrol ( RectTransform p )
{
var db = S . Db ;
var d = S . Save ;
var e = db . Economy ;
var row = PatrolRules . Row ( db , d ) ;
float cap = PatrolRules . CapHours ( db , d ) ;
float h = PatrolRules . Hours ( db , d , S . Now ) ;
var basis = Panel ( p , "Basis" , 0f , 110f ) ;
UiKit . Label ( basis , "기준: 마지막 클리어 챕터 " + ( row ! = null ? row . chapter : 0 ) + "장" , 38 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 8f ) , new Vector2 ( 900f , 54f ) ) ;
UiKit . Label ( basis , "제" + Mathf . Max ( 1 , d . chapterMax ) + "장 · 시간당 수확 · 오프라인 누적" , 28 , UiKit . InkSoft , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0f ) , new Vector2 ( 0f , 0f ) , new Vector2 ( 24f , 8f ) , new Vector2 ( 900f , 44f ) , false ) ;
var acc = Panel ( p , "Accum" , 126f , 170f ) ;
UiKit . Label ( acc , "누적 " + FormatHours ( h ) + " / 최대 " + FormatHours ( cap ) , 40 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 8f ) , new Vector2 ( 900f , 56f ) ) ;
2026-11-09 03:20:32 +00:00
var fill = UiKit . Bar ( "Bar" , acc , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0f , - 6f ) , new Vector2 ( 980f , 34f ) , UiKit . Slot , UiKit . Red ) ;
2026-11-05 15:22:54 +00:00
UiKit . SetBar ( fill , cap > 0f ? h / cap : 0f ) ;
UiKit . Label ( acc , "성장 패스 24시간(준비 중) · 농업 개발로 +" + Mathf . RoundToInt ( DevRules . Mod ( db , d , DevRules . PatrolCap ) ) + "분" , 26 , UiKit . Red , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0f ) , new Vector2 ( 0f , 0f ) , new Vector2 ( 24f , 8f ) , new Vector2 ( 900f , 44f ) , false ) ;
var rate = Panel ( p , "Rate" , 312f , 400f ) ;
UiKit . Label ( rate , "시간당 수확" , 38 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 8f ) , new Vector2 ( 600f , 56f ) ) ;
string [ ] rn = { "골드" , "병사 경험" , "승급 장비" , "보물 청동 상자" } ;
string [ ] rv =
{
PatrolRules . GoldPerHour ( db , d ) . ToString ( "N0" ) + " / 시간" ,
PatrolRules . ExpPerHour ( db , d ) . ToString ( "N0" ) + " / 시간(출전 편성)" ,
UiKit . GradeKo ( PatrolRules . GearGrade ( db , d ) ) + " · " + PatrolRules . GearPerHour ( db , d ) . ToString ( "0.##" ) + " / 시간" ,
PatrolRules . ChestPerHour ( db , d ) . ToString ( "0.##" ) + " / 시간" ,
} ;
Color [ ] rc = { UiKit . Coin , new Color ( 0.8f , 0.48f , 0.38f ) , UiKit . Ok , new Color ( 0.7f , 0.48f , 0.3f ) } ;
2026-11-06 14:20:57 +00:00
string [ ] rIcon = { "cur_gold" , "cur_exp" , "cur_gear" , "chest_bronze" } ;
2026-11-05 15:22:54 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
2026-11-09 03:20:32 +00:00
var line = UiKit . Box ( "Row" + i , rate , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 76f - i * 78f ) , new Vector2 ( 980f , 68f ) , UiKit . Dark ) ;
2026-11-06 14:20:57 +00:00
if ( UiKit . Icon ( line . transform , rIcon [ i ] , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 12f , 0f ) , 58f ) = = null )
UiKit . Dot ( "Dot" , line . transform , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 16f , 0f ) , 50f , rc [ i ] ) ;
2026-11-05 15:22:54 +00:00
UiKit . Label ( line . transform , rn [ i ] , 34 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 86f , 0f ) , new Vector2 ( 400f , 56f ) ) ;
UiKit . Label ( line . transform , rv [ i ] , 30 , UiKit . Ink , TextAnchor . MiddleRight , new Vector2 ( 1f , 0.5f ) , new Vector2 ( 1f , 0.5f ) , new Vector2 ( - 20f , 0f ) , new Vector2 ( 520f , 56f ) , false ) ;
}
var y = PatrolRules . Preview ( db , d , h , 1f ) ;
var got = Panel ( p , "Stored" , 728f , 220f ) ;
UiKit . Label ( got , "쌓인 보상" , 38 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 8f ) , new Vector2 ( 600f , 56f ) ) ;
string [ ] sv = { "골드\n" + y . gold . ToString ( "N0" ) , "병사 경험\n" + y . exp . ToString ( "N0" ) , "승급 장비\n" + y . gear , "청동 상자\n" + y . chests } ;
2026-11-09 03:24:04 +00:00
string [ ] sn = { y . gold . ToString ( "N0" ) , y . exp . ToString ( "N0" ) , y . gear . ToString ( ) , y . chests . ToString ( ) } ;
2026-11-05 15:22:54 +00:00
for ( int i = 0 ; i < 4 ; i + + )
{
2026-11-09 03:24:04 +00:00
if ( UISkin . Has ( "item_bg" ) & & UISkin . Icon ( rIcon [ i ] ) ! = null )
{
// 쌓인 보상 칸(설계서 §3⑦) = 팩 ItemFrame 5층 + 아이콘 + 수량
var cell = UiKit . Rect ( "Chip" + i , got , new Vector2 ( 0f , 0f ) , new Vector2 ( 0f , 0f ) , new Vector2 ( 24f + i * 250f , 16f ) , new Vector2 ( 232f , 130f ) ) ;
UiKit . ItemSlot ( cell , "gear_blue_" , true ) ;
UiKit . DropShadow ( UiKit . Icon ( cell , rIcon [ i ] , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 14f , 4f ) , 84f ) , 4f ) ;
var v = UiKit . Title ( UiKit . Label ( cell , sn [ i ] , 38 , UiKit . White , TextAnchor . MiddleRight , new Vector2 ( 1f , 0.5f ) , new Vector2 ( 1f , 0.5f ) , new Vector2 ( - 16f , 0f ) , new Vector2 ( 130f , 60f ) ) , true ) ;
v . horizontalOverflow = HorizontalWrapMode . Overflow ;
continue ;
}
2026-11-09 03:20:32 +00:00
var chip = UiKit . Box ( "Chip" + i , got , new Vector2 ( 0f , 0f ) , new Vector2 ( 0f , 0f ) , new Vector2 ( 24f + i * 250f , 16f ) , new Vector2 ( 232f , 130f ) , UiKit . Dark ) ;
2026-11-05 15:22:54 +00:00
UiKit . Border ( chip , rc [ i ] , 3f ) ;
UiKit . FillLabel ( chip . transform , sv [ i ] , 30 , UiKit . Ink ) ;
}
var hb = UiKit . Btn ( "Harvest" , p , "수확" , 64 , UiKit . Red , UiKit . White , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 966f ) , new Vector2 ( 1030f , 140f ) , ( ) = > DoHarvest ( false ) ) ;
hb . interactable = h > 0f ;
EconomyRules . RollDaily ( d , S . Now ) ;
var ad = UiKit . Btn ( "Ad" , p , "광고 2배 수확\n<size=26>" + d . adsUsed . patrol + "/" + e . PatrolAdDaily + "회/일 (시험)</size>" , 36 , UiKit . Dark , UiKit . White , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 1124f ) , new Vector2 ( 505f , 130f ) , ( ) = > DoHarvest ( true ) ) ;
ad . interactable = h > 0f & & d . adsUsed . patrol < e . PatrolAdDaily ;
UiKit . Btn ( "Instant" , p , "즉시 수확 " + FormatHours ( e . PatrolInstantHours ) + "분\n<size=26>보옥 " + e . PatrolInstantGem + "</size>" , 36 , UiKit . Jade , UiKit . White , new Vector2 ( 1f , 1f ) , new Vector2 ( 1f , 1f ) , new Vector2 ( - 24f , - 1124f ) , new Vector2 ( 505f , 130f ) , ( ) = >
{
string why ;
var r = S . InstantHarvest ( out why ) ;
if ( r = = null ) Root . Toast ( why ) ;
else ShowYield ( "즉시 수확" , r ) ;
} ) ;
}
2026-11-09 09:12:53 +00:00
/// <summary>
/// 순찰(Fable 제안서 §4-7 · #1104) — 섹션 「순찰」(기준 줄 + 레벨형 게이지 군량색 + 배지 「7h」) → 「시간당 수확」 스탯 칸 4(아이콘 64 · 라벨 · 값 Sen) →
/// 「쌓인 보상」 타일 4(ItemFrame 132 + 수량 칩) → 수확 Primary 녹 · 광고 2배 Secondary 하늘 + 광고 글리프 · 즉시 Secondary 보라 + 보옥 비용.
/// </summary>
void BuildPatrolKit ( RectTransform p )
{
var db = S . Db ;
var d = S . Save ;
var e = db . Economy ;
var row = PatrolRules . Row ( db , d ) ;
float cap = PatrolRules . CapHours ( db , d ) ;
float h = PatrolRules . Hours ( db , d , S . Now ) ;
var soft = UiKit . Col ( "text_soft" , UiKit . InkSoft ) ;
2026-11-09 09:23:18 +00:00
const float W = 1032f , Fw = W - 12f , GaugeBar = 800f ;
2026-11-09 09:12:53 +00:00
// ① 순찰
RectTransform ar ;
var a = UiKit . SectionPanel ( p , "Patrol" , 24f , 6f , W , 236f , false , false , out ar ) ;
UiKit . SectionTitle ( ar , "순찰" ) ;
var basis = UiKit . Label ( ar , "기준 = 마지막 클리어 " + ( row ! = null ? row . chapter : 0 ) + "장 · 오프라인 누적" , 26 , soft , TextAnchor . MiddleRight , new Vector2 ( 1f , 1f ) , new Vector2 ( 1f , 1f ) , new Vector2 ( - 24f , - 12f ) , new Vector2 ( 620f , 48f ) , false ) ;
basis . horizontalOverflow = HorizontalWrapMode . Overflow ;
Text badge ;
var fill = UiKit . Gauge ( a , "Gauge" , 16f , 66f , GaugeBar , "lv_badge" , "grain" , out badge ) ;
UiKit . SetGauge ( fill , cap > 0f ? h / cap : 0f , GaugeBar ) ;
badge . text = Mathf . FloorToInt ( h ) + "h" ;
badge . fontSize = 30 ;
2026-11-09 09:23:18 +00:00
var cur = UiKit . Title ( UiKit . Label ( a , FormatHours ( h ) + " / " + FormatHours ( cap ) , 30 , UiKit . White , TextAnchor . MiddleLeft , new Vector2 ( 0f , 1f ) , new Vector2 ( 0f , 1f ) , new Vector2 ( 24f , - 164f ) , new Vector2 ( 260f , 52f ) ) , true ) ;
2026-11-09 09:12:53 +00:00
cur . horizontalOverflow = HorizontalWrapMode . Overflow ;
2026-11-09 09:23:18 +00:00
UiKit . Label ( a , "성장 패스 24시간(준비 중) · 농업 개발 +" + Mathf . RoundToInt ( DevRules . Mod ( db , d , DevRules . PatrolCap ) ) + "분" , 24 , soft , TextAnchor . MiddleLeft , new Vector2 ( 1f , 1f ) , new Vector2 ( 1f , 1f ) , new Vector2 ( - 24f , - 168f ) , new Vector2 ( Fw - 320f , 44f ) , false ) . alignment = TextAnchor . MiddleRight ;
2026-11-09 09:12:53 +00:00
// ② 시간당 수확
RectTransform br ;
var b = UiKit . SectionPanel ( p , "Rate" , 24f , 258f , W , 448f , false , false , out br ) ;
UiKit . SectionTitle ( br , "시간당 수확" ) ;
string [ ] rn = { "골드" , "병사 경험" , "승급 장비" , "보물 청동 상자" } ;
string [ ] rv =
{
PatrolRules . GoldPerHour ( db , d ) . ToString ( "N0" ) ,
PatrolRules . ExpPerHour ( db , d ) . ToString ( "N0" ) ,
PatrolRules . GearPerHour ( db , d ) . ToString ( "0.##" ) ,
PatrolRules . ChestPerHour ( db , d ) . ToString ( "0.##" ) ,
} ;
string [ ] rs = { "/ 시간" , "/ 시간 · 출전 편성" , "/ 시간 · " + UiKit . GradeKo ( PatrolRules . GearGrade ( db , d ) ) + " 등급" , "/ 시간" } ;
string [ ] rIcon = { "cur_gold" , "cur_exp" , "cur_gear" , "chest_bronze" } ;
for ( int i = 0 ; i < 4 ; i + + )
{
var cell = UiKit . Skin ( UiKit . At ( b , "Row" + i , 14f , 76f + i * 90f , Fw - 28f , 82f ) , "panel12" , UiKit . Col ( "row_on_field" , UiKit . Paper ) ) . transform ;
var ic = UiKit . Icon ( cell , rIcon [ i ] , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 50f , 0f ) , 64f ) ;
if ( ic ! = null ) UiKit . DropShadow ( ic , 3f ) ;
UiKit . Label ( cell , rn [ i ] , 30 , soft , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 98f , 0f ) , new Vector2 ( 300f , 60f ) , false ) ;
var sub = UiKit . Label ( cell , rs [ i ] , 24 , soft , TextAnchor . MiddleRight , new Vector2 ( 1f , 0.5f ) , new Vector2 ( 1f , 0.5f ) , new Vector2 ( - 24f , - 2f ) , new Vector2 ( 300f , 50f ) , false ) ;
sub . horizontalOverflow = HorizontalWrapMode . Overflow ;
var val = UiKit . Title ( UiKit . Label ( cell , rv [ i ] , 44 , UiKit . White , TextAnchor . MiddleRight , new Vector2 ( 1f , 0.5f ) , new Vector2 ( 1f , 0.5f ) , new Vector2 ( - 24f - Mathf . Ceil ( sub . preferredWidth ) - 14f , 2f ) , new Vector2 ( 260f , 70f ) ) , true ) ;
val . horizontalOverflow = HorizontalWrapMode . Overflow ;
}
// ③ 쌓인 보상
var y = PatrolRules . Preview ( db , d , h , 1f ) ;
RectTransform cr ;
var c = UiKit . SectionPanel ( p , "Stored" , 24f , 722f , W , 262f , false , false , out cr ) ;
UiKit . SectionTitle ( cr , "쌓인 보상" ) ;
string [ ] sn = { y . gold . ToString ( "N0" ) , y . exp . ToString ( "N0" ) , y . gear . ToString ( ) , y . chests . ToString ( ) } ;
const float Tile = 132f , Gap = 64f ;
float tx0 = ( Fw - 4f * Tile - 3f * Gap ) * 0.5f ;
for ( int i = 0 ; i < 4 ; i + + )
UiKit . RewardTile ( c , "Tile" + i , tx0 + i * ( Tile + Gap ) , 90f , Tile , i = = 2 ? "gear_" + PatrolRules . GearGrade ( db , d ) + "_" : "gear_white_" , rIcon [ i ] , sn [ i ] , i = = 2 ) ;
// ④ 버튼
EconomyRules . RollDaily ( d , S . Now ) ;
var hb = UiKit . SkinBtn ( p , "Harvest" , h > 0f ? "btn_l_green" : "btn_gray" , UiKit . White , "수확" , 52 , UiKit . Col ( "text_on_green" , UiKit . White ) , ( 1080f - 600f ) * 0.5f , 1004f , 600f , 128f , ( ) = > DoHarvest ( false ) ) ;
hb . interactable = h > 0f ;
bool adOk = h > 0f & & d . adsUsed . patrol < e . PatrolAdDaily ;
var ad = UiKit . SkinBtn ( p , "Ad" , adOk ? "btn_sky" : "btn_gray" , UiKit . White , "광고 2배" , 38 , UiKit . White , 24f , 1152f , 505f , 112f , ( ) = > DoHarvest ( true ) ) ;
ad . interactable = adOk ;
UiKit . BtnCost ( ad , "ad" , d . adsUsed . patrol + "/" + e . PatrolAdDaily , 34f , 28 ) ;
var ins = UiKit . SkinBtn ( p , "Instant" , "btn_purple" , UiKit . White , "즉시 " + FormatHours ( e . PatrolInstantHours ) , 38 , UiKit . White , 1056f - 505f , 1152f , 505f , 112f , ( ) = >
{
string why ;
var r = S . InstantHarvest ( out why ) ;
if ( r = = null ) Root . Toast ( why ) ;
else ShowYield ( "즉시 수확" , r ) ;
} ) ;
UiKit . BtnCost ( ins , "cur_gem" , e . PatrolInstantGem . ToString ( ) , 40f , 32 ) ;
}
2026-11-05 15:22:54 +00:00
void DoHarvest ( bool ad )
{
string why ;
var r = S . Harvest ( ad , out why ) ;
if ( r = = null ) Root . Toast ( why ) ;
else ShowYield ( ad ? "광고 2배 수확" : "수확" , r ) ;
}
void ShowYield ( string title , PatrolYield y )
{
var sb = new System . Text . StringBuilder ( ) ;
sb . Append ( FormatHours ( y . hours ) ) . Append ( y . mult > 1f ? " × " + y . mult : "" ) . Append ( " 분량\n\n" ) ;
sb . Append ( "골드 +" ) . Append ( y . gold . ToString ( "N0" ) ) . Append ( "\n병사 경험 +" ) . Append ( y . exp ) . Append ( " (" ) . Append ( y . expTo . Count ) . Append ( "명)" ) ;
if ( y . gearIds . Count > 0 )
{
var names = new List < string > ( ) ;
foreach ( var g in y . gearIds ) names . Add ( S . Db . Gear . Get ( g ) . nameKo ) ;
sb . Append ( "\n장비 " ) . Append ( string . Join ( " · " , names . ToArray ( ) ) ) ;
}
if ( y . chestIds . Count > 0 ) sb . Append ( "\n" ) . Append ( S . Db . Chests . Get ( y . chestIds [ 0 ] ) . nameKo ) . Append ( " × " ) . Append ( y . chestIds . Count ) ;
Root . Modal ( title , sb . ToString ( ) , new OutGameRoot . ModalButton ( "확인" , UiKit . Dark , null ) ) ;
}
public static string FormatHours ( float h )
{
int min = Mathf . FloorToInt ( h * 60f + 1e-3f ) ;
return ( min / 60 ) + "시간" + ( min % 60 > 0 ? " " + ( min % 60 ) + "분" : "" ) ;
}
static RectTransform Panel ( RectTransform p , string name , float top , float h )
{
var band = UiKit . TopBand ( name , p , top , h , 24f , 24f ) ;
var img = UiKit . Paint ( band , UiKit . Paper ) ;
UiKit . Border ( img , UiKit . Line , 3f ) ;
return band ;
}
// ---------- ③ 개발 ----------
void BuildDev ( RectTransform p )
{
var db = S . Db ;
var d = S . Save ;
var branches = db . Dev . Branches ;
if ( branches . Count = = 0 ) return ;
int n = branches . Count ;
string br = Branch ;
UiKit . Btn ( "Prev" , p , "◀" , 44 , UiKit . Paper , UiKit . Ink , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( - 330f , - 6f ) , new Vector2 ( 100f , 90f ) , ( ) = > SetBranch ( branchIdx - 1 ) ) ;
UiKit . Btn ( "Next" , p , "▶" , 44 , UiKit . Paper , UiKit . Ink , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 330f , - 6f ) , new Vector2 ( 100f , 90f ) , ( ) = > SetBranch ( branchIdx + 1 ) ) ;
UiKit . Label ( p , BranchKo ( branches [ ( branchIdx + n - 1 ) % n ] ) , 24 , UiKit . InkSoft , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( - 330f , - 96f ) , new Vector2 ( 140f , 34f ) , false ) ;
UiKit . Label ( p , BranchKo ( branches [ ( branchIdx + 1 ) % n ] ) , 24 , UiKit . InkSoft , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 330f , - 96f ) , new Vector2 ( 140f , 34f ) , false ) ;
var title = UiKit . Box ( "Branch" , p , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 6f ) , new Vector2 ( 420f , 90f ) , UiKit . Red ) ;
UiKit . FillLabel ( title . transform , BranchKo ( br ) , 48 , UiKit . White ) ;
for ( int i = 0 ; i < n ; i + + ) UiKit . Dot ( "Page" + i , p , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( ( i - ( n - 1 ) / 2f ) * 30f , - 106f ) , 16f , i = = branchIdx ? UiKit . Red : UiKit . Slot ) ;
UiKit . Label ( p , BranchKo ( br ) + " — " + BranchGoal ( br ) , 30 , UiKit . InkSoft , TextAnchor . MiddleCenter , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0f , - 136f ) , new Vector2 ( 900f , 44f ) , false ) ;
// 깊이(선행 조건) = 줄 · 같은 줄 = 가로로
var nodes = db . Dev . InBranch ( br ) ;
var depth = new Dictionary < string , int > ( ) ;
int maxDepth = 0 ;
foreach ( var nd in nodes )
{
int dp = 0 ;
foreach ( var r in nd . requires )
{
int x ;
if ( depth . TryGetValue ( r . node , out x ) ) dp = Mathf . Max ( dp , x + 1 ) ;
}
depth [ nd . id ] = dp ;
maxDepth = Mathf . Max ( maxDepth , dp ) ;
}
var tree = UiKit . Fill ( "Tree" , p , 24f , 24f , 190f , 190f ) ;
float rowH = Mathf . Min ( 190f , 1050f / ( maxDepth + 1 ) ) ;
var pos = new Dictionary < string , Vector2 > ( ) ;
for ( int dp = 0 ; dp < = maxDepth ; dp + + )
{
var row = new List < DevTree . Node > ( ) ;
foreach ( var nd in nodes )
if ( depth [ nd . id ] = = dp ) row . Add ( nd ) ;
for ( int i = 0 ; i < row . Count ; i + + )
pos [ row [ i ] . id ] = new Vector2 ( ( i - ( row . Count - 1 ) / 2f ) * ( row . Count > 1 ? 840f / ( row . Count - 1 ) : 0f ) , - rowH * ( dp + 0.5f ) ) ;
}
foreach ( var nd in nodes )
foreach ( var r in nd . requires )
if ( pos . ContainsKey ( r . node ) ) Line ( tree , pos [ r . node ] , pos [ nd . id ] , DevRules . Level ( d , r . node ) > = r . lv ? new Color ( 0.45f , 0.36f , 0.3f ) : UiKit . Slot ) ;
NodeCount = 0 ;
foreach ( var nd in nodes )
{
var node = nd ;
int lv = DevRules . Level ( d , nd . id ) ;
bool open = DevRules . Unlocked ( d , nd ) ;
bool max = lv > = nd . lvMax ;
float size = nd . Big ? Mathf . Min ( 150f , rowH * 0.78f ) : Mathf . Min ( 110f , rowH * 0.6f ) ;
var c = UiKit . Btn ( "Node_" + nd . id , tree , "" , 30 , max ? UiKit . Coin : open ? UiKit . Paper : new Color ( 0.55f , 0.51f , 0.48f ) , UiKit . Ink , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 0.5f ) , pos [ nd . id ] , new Vector2 ( size , size ) , ( ) = > NodePopup ( node . id ) ) ;
var img = c . GetComponent < Image > ( ) ;
img . sprite = UiKit . Circle ;
img . type = Image . Type . Simple ;
UiKit . Border ( img , nd . Big ? new Color ( 0.78f , 0.62f , 0.25f ) : UiKit . Line , nd . Big ? 6f : 3f ) ;
UiKit . LabelOf ( c ) . text = open ? nd . nameKo . Substring ( 0 , 1 ) : "잠" ;
UiKit . LabelOf ( c ) . fontSize = nd . Big ? 60 : 44 ;
2026-11-09 03:20:32 +00:00
UiKit . LabelOf ( c ) . color = open ? ( max ? UiKit . White : UiKit . Ink ) : UiKit . InkSoft ;
2026-11-05 15:22:54 +00:00
var pill = UiKit . Box ( "Lv" , c . transform , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0.5f ) , new Vector2 ( 0f , 0f ) , new Vector2 ( 110f , 40f ) , UiKit . Dark ) ;
UiKit . FillLabel ( pill . transform , lv + "/" + nd . lvMax , 24 , UiKit . White ) ;
float side = pos [ nd . id ] . x < = 0f ? 1f : - 1f ;
var lab = UiKit . Label ( tree , nd . nameKo , 28 , open ? UiKit . Ink : UiKit . InkSoft , side > 0 ? TextAnchor . MiddleLeft : TextAnchor . MiddleRight , new Vector2 ( 0.5f , 1f ) ,
new Vector2 ( side > 0 ? 0f : 1f , 0.5f ) , pos [ nd . id ] + new Vector2 ( side * ( size / 2f + 10f ) , 0f ) , new Vector2 ( 240f , 50f ) ) ;
lab . horizontalOverflow = HorizontalWrapMode . Overflow ;
NodeCount + + ;
}
var foot = UiKit . Box ( "Foot" , p , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0.5f , 0f ) , new Vector2 ( 0f , 16f ) , new Vector2 ( 1032f , 150f ) , UiKit . Paper ) ;
UiKit . Border ( foot , UiKit . Line , 3f ) ;
2026-11-06 14:20:57 +00:00
UiKit . IconOrDot ( foot . transform , "cur_blueprint" , "도" , new Color ( 0.33f , 0.45f , 0.62f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 20f , 0f ) , 76f ) ;
2026-11-05 15:22:54 +00:00
UiKit . Label ( foot . transform , "도면 × " + d . inv . blueprint , 38 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 110f , 0f ) , new Vector2 ( 260f , 60f ) ) ;
2026-11-06 14:20:57 +00:00
UiKit . IconOrDot ( foot . transform , "cur_gold" , "골" , UiKit . Coin , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 380f , 0f ) , 76f ) ;
2026-11-05 15:22:54 +00:00
UiKit . Label ( foot . transform , d . gold . ToString ( "N0" ) , 38 , UiKit . Ink , TextAnchor . MiddleLeft , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 0f , 0.5f ) , new Vector2 ( 470f , 0f ) , new Vector2 ( 220f , 60f ) ) ;
UiKit . Btn ( "Reset" , foot . transform , "트리 초기화\n<size=24>보옥 " + db . Economy . DevResetGem + " · 도면 환급</size>" , 34 , UiKit . Dark , UiKit . White , new Vector2 ( 1f , 0.5f ) , new Vector2 ( 1f , 0.5f ) , new Vector2 ( - 16f , 0f ) , new Vector2 ( 320f , 120f ) , ( ) = >
{
Root . Modal ( "트리 초기화" , "모든 개발 노드를 0 으로 되돌립니다.\n보옥 " + db . Economy . DevResetGem + " 소모 · 쓴 도면 " + S . Save . dev . blueprintSpent + " 환급 · 골드 환급 없음" ,
new OutGameRoot . ModalButton ( "초기화" , UiKit . Red , ( ) = > Root . Toast ( S . Do ( ( ) = > DevRules . Reset ( S . Db , S . Save ) ) ? ? "개발 트리 초기화" ) ) ,
new OutGameRoot . ModalButton ( "취소" , UiKit . Dark , null ) ) ;
} ) ;
}
static void Line ( RectTransform parent , Vector2 a , Vector2 b , Color c )
{
var mid = ( a + b ) / 2f ;
var dir = b - a ;
var rt = UiKit . Rect ( "Link" , parent , new Vector2 ( 0.5f , 1f ) , new Vector2 ( 0.5f , 0.5f ) , mid , new Vector2 ( dir . magnitude , 8f ) ) ;
rt . localRotation = Quaternion . Euler ( 0f , 0f , Mathf . Atan2 ( dir . y , dir . x ) * Mathf . Rad2Deg ) ;
UiKit . Paint ( rt , c , false ) ;
}
public void NodePopup ( string id )
{
var db = S . Db ;
var d = S . Save ;
var n = db . Dev . Get ( id ) ;
if ( n = = null ) return ;
int lv = DevRules . Level ( d , id ) ;
var sb = new System . Text . StringBuilder ( ) ;
sb . Append ( BranchKo ( n . branch ) ) . Append ( n . Big ? " · 큰 노드" : "" ) . Append ( " Lv " ) . Append ( lv ) . Append ( " / " ) . Append ( n . lvMax ) . Append ( "\n\n" ) ;
foreach ( var k in n . EffectKeys )
{
string what ;
DevRules . Keys . TryGetValue ( k , out what ) ;
sb . Append ( what ) . Append ( " " ) . Append ( ( n . pctPerLv * lv ) . ToString ( "0.##" ) ) . Append ( " → " ) . Append ( ( n . pctPerLv * Mathf . Min ( n . lvMax , lv + 1 ) ) . ToString ( "0.##" ) ) . Append ( "\n" ) ;
}
if ( n . requires . Count > 0 )
{
sb . Append ( "\n선행: " ) ;
for ( int i = 0 ; i < n . requires . Count ; i + + )
{
var r = n . requires [ i ] ;
bool ok = DevRules . Level ( d , r . node ) > = r . lv ;
sb . Append ( i > 0 ? " · " : "" ) . Append ( ok ? "<color=#3a6a30>" : "<color=#a03a22>" ) . Append ( db . Dev . Get ( r . node ) . nameKo ) . Append ( " Lv " ) . Append ( r . lv ) . Append ( "</color>" ) ;
}
sb . Append ( "\n" ) ;
}
if ( lv < n . lvMax )
{
int gold , bp ;
DevRules . NextCost ( n , lv , out gold , out bp ) ;
sb . Append ( "\n다음 비용 골드 " ) . Append ( gold . ToString ( "N0" ) ) . Append ( bp > 0 ? " · 도면 " + bp : "" ) ;
}
var why = DevRules . CanUpgrade ( db , d , id ) ;
Root . Modal ( n . nameKo , sb . ToString ( ) ,
new OutGameRoot . ModalButton ( why = = null ? "개발 Lv+1" : why , why = = null ? UiKit . Red : UiKit . Disabled , ( ) = >
{
var w = S . Do ( ( ) = > DevRules . Upgrade ( S . Db , S . Save , id ) ) ;
if ( w = = null ) NodePopup ( id ) ;
else Root . Toast ( w ) ;
} , true ) ,
new OutGameRoot . ModalButton ( "닫기" , UiKit . Dark , null ) ) ;
}
public static string BranchKo ( string b )
{
switch ( b )
{
case "commerce" : return "상업" ;
case "agri" : return "농업" ;
case "industry" : return "공업" ;
}
return b ;
}
static string BranchGoal ( string b )
{
switch ( b )
{
case "commerce" : return "재화 획득 효율" ;
case "agri" : return "자원 획득 효율" ;
case "industry" : return "장비 획득" ;
}
return "" ;
}
public override bool OnBack ( )
{
return false ;
}
}
}