using UnityEngine; namespace TKS.OutGame { /// /// 내정 훈련 규칙(기획서 §7 · S9 · PD #1093 C) — 병종마다 HP·공·방 3항목 골드 레벨업(항목당 Lv max) · 비용 = gold_cost(Lv) × cost_mult(랭크) · /// 3항목 Max → 랭크 승급(promote_gold · 레벨 리셋 · 랭크 보너스 유지) · train_bonus(병종, 항목) = rank_pct(랭크) + stat_pct(Lv) → §3-2 공식(그 병종 영웅·병사 전원). /// public static class TrainingRules { public static int StatIndex(string stat) { return System.Array.IndexOf(TrainingTable.Stats, stat); } static TrainState Find(SaveData d, UnitType t) { if (d == null || d.training == null) return null; var key = UnitTypeInfo.Key(t); foreach (var s in d.training) if (s.type == key) return s; return null; } public static int Rank(SaveData d, UnitType t) { var s = Find(d, t); return s != null ? Mathf.Max(1, s.rank) : 1; } public static int Lv(SaveData d, UnitType t, int stat) { var s = Find(d, t); return s != null && s.lv != null && stat >= 0 && stat < s.lv.Length ? s.lv[stat] : 0; } /// 훈련 보너스 % = 랭크 보너스 + 항목 Lv 누적 %. public static float Bonus(OutGameData db, SaveData d, UnitType t, string stat) { if (db == null || db.Training == null || d == null) return 0f; var rank = db.Training.RankOf(Rank(d, t)); int lv = Lv(d, t, StatIndex(stat)); var row = lv > 0 ? db.Training.Get(t, stat, lv) : null; return (rank != null ? rank.rankPct : 0f) + (row != null ? row.statPct : 0f); } /// 다음 Lv 비용(최고 Lv = 0). public static int Cost(OutGameData db, SaveData d, UnitType t, int stat) { int lv = Lv(d, t, stat); var row = db.Training.Get(t, TrainingTable.Stats[stat], lv + 1); var rank = db.Training.RankOf(Rank(d, t)); if (row == null) return 0; return Mathf.RoundToInt(row.goldCost * (rank != null ? rank.costMult : 1f)); } public static string CanLevel(OutGameData db, SaveData d, UnitType t, int stat) { if (Lv(d, t, stat) >= db.Training.MaxLevel) return "Max"; int cost = Cost(db, d, t, stat); if (d.gold < cost) return "골드 부족 " + d.gold.ToString("N0") + " / " + cost.ToString("N0"); return null; } public static string Level(OutGameData db, SaveData d, UnitType t, int stat) { var why = CanLevel(db, d, t, stat); if (why != null) return why; d.gold -= Cost(db, d, t, stat); d.Train(t).lv[stat]++; return null; } /// 이 랭크에서 남은 레벨 수(3항목 × Max − 합). public static int Remaining(OutGameData db, SaveData d, UnitType t) { int left = 0; for (int i = 0; i < TrainingTable.Stats.Length; i++) left += db.Training.MaxLevel - Lv(d, t, i); return left; } public static int PromoteCost(OutGameData db, SaveData d, UnitType t) { var r = db.Training.RankOf(Rank(d, t)); return r != null ? r.promoteGold : 0; } public static string CanPromote(OutGameData db, SaveData d, UnitType t) { if (Rank(d, t) >= db.Training.MaxRank) return "최종 랭크"; int left = Remaining(db, d, t); if (left > 0) return "남은 레벨 " + left; int cost = PromoteCost(db, d, t); if (d.gold < cost) return "골드 부족 " + d.gold.ToString("N0") + " / " + cost.ToString("N0"); return null; } /// 랭크 +1 · 3항목 Lv 리셋(랭크 보너스는 새 랭크 값). public static string Promote(OutGameData db, SaveData d, UnitType t) { var why = CanPromote(db, d, t); if (why != null) return why; d.gold -= PromoteCost(db, d, t); var s = d.Train(t); s.rank++; s.lv = new int[TrainingTable.Stats.Length]; return null; } public static string RankName(OutGameData db, int rank) { var r = db.Training.RankOf(rank); return r != null ? r.nameKo : ""; } } }