ThreeKingdomsSlingshot/Assets/Scripts/OutGame/Domestic/TrainingRules.cs

117 lines
4.5 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 UnityEngine;
namespace TKS.OutGame
{
/// <summary>
/// 내정 훈련 규칙(기획서 §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 공식(그 병종 영웅·병사 전원).
/// </summary>
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;
}
/// <summary>훈련 보너스 % = 랭크 보너스 + 항목 Lv 누적 %.</summary>
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);
}
/// <summary>다음 Lv 비용(최고 Lv = 0).</summary>
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;
}
/// <summary>이 랭크에서 남은 레벨 수(3항목 × Max − 합).</summary>
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;
}
/// <summary>랭크 +1 · 3항목 Lv 리셋(랭크 보너스는 새 랭크 값).</summary>
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 : "";
}
}
}