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

174 lines
7.7 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System.Collections.Generic;
using UnityEngine;
namespace TKS.OutGame
{
/// <summary>
/// 내정 개발 트리 규칙(기획서 §7 · S11 · PD #1093 C) — 노드 레벨 · 선행 조건 · 비용(골드 = gold_base × gold_growth^lv · 도면 = blueprint_from_lv 이상 Lv 마다 blueprint_per_lv) ·
/// 초기화(보옥 dev_reset_gem · 쓴 도면 환급 · 골드 환급 없음) · 효과 = Mod(키) = Σ pct_per_lv × Lv 를 각 계산 지점이 읽는다(아래 Keys = 키 → 적용 지점).
/// </summary>
public static class DevRules
{
public const string GoldGain = "gold_gain_pct";
public const string GemBonus = "gem_bonus_pct";
public const string TradeDiscount = "trade_discount_pct";
public const string ChestPoint = "chest_point_pct";
public const string TradeFreeRefresh = "trade_free_refresh";
public const string PatrolGold = "patrol_gold_pct";
public const string SellPrice = "sell_price_pct";
public const string FirstClearGem = "first_clear_gem_pct";
public const string StarReward = "star_reward_pct";
public const string FoodCap = "food_cap_add";
public const string FoodRegen = "food_regen_cut_sec";
public const string PatrolYield = "patrol_yield_pct";
public const string PatrolCap = "patrol_cap_min";
public const string PatrolGearGrade = "patrol_gear_grade_up";
public const string ExpGain = "exp_gain_pct";
public const string FoodCost = "food_cost_cut";
public const string AdFood = "ad_food_add";
public const string PatrolGearChance = "patrol_gear_chance_pct";
public const string GearDrop = "gear_drop_pct";
public const string ChestHigh = "chest_high_grade_pct";
public const string IronGain = "iron_gain_pct";
public const string CraftCost = "craft_cost_cut_pct";
public const string CraftBulk = "craft_bulk";
public const string CraftBlueprint = "craft_blueprint_cut";
public const string GearSell = "gear_sell_pct";
public const string BlueprintGain = "blueprint_gain_pct";
public const string DungeonGear = "dungeon_gear_pct";
public const string ChestTier = "chest_tier_up_pct";
/// <summary>효과 키 → 적용 지점(화면 설명 · 「미구현」 = 판매·합성·별 보상이 아직 없어 적용 지점 없음 · PD 확인 대상).</summary>
public static readonly Dictionary<string, string> Keys = new Dictionary<string, string>
{
{ GoldGain, "스테이지·군량고 골드 +%" },
{ GemBonus, "스테이지·탑 보옥 +%" },
{ TradeDiscount, "상점 장비·상자 가격 −%" },
{ ChestPoint, "상자 포인트 +%" },
{ TradeFreeRefresh, "상점 무료 새로고침 +회/일" },
{ PatrolGold, "순찰 골드 +%" },
{ SellPrice, "판매가 +% (판매 미구현)" },
{ FirstClearGem, "첫 클리어 보옥 +%" },
{ StarReward, "별 보상 +% (별 보상 미구현)" },
{ FoodCap, "군량 상한 +" },
{ FoodRegen, "군량 회복 −초" },
{ PatrolYield, "순찰 수확 +%" },
{ PatrolCap, "순찰 상한 +분" },
{ PatrolGearGrade, "순찰 장비 등급 +" },
{ ExpGain, "스테이지·순찰 경험 +%" },
{ FoodCost, "스테이지 군량 −(1 단위 내림)" },
{ AdFood, "광고 군량 +" },
{ PatrolGearChance, "순찰 장비 +%" },
{ GearDrop, "스테이지 장비 드랍 +%p" },
{ ChestHigh, "상자 상위 등급 확률 +%" },
{ IronGain, "운철 +%" },
{ CraftCost, "합성 비용 −% (합성 미구현)" },
{ CraftBulk, "합성 일괄 (합성 미구현)" },
{ CraftBlueprint, "합성 도면 − (합성 미구현)" },
{ GearSell, "장비 판매 +% (판매 미구현)" },
{ BlueprintGain, "도면 획득 +%" },
{ DungeonGear, "대장간 장비 +%" },
{ ChestTier, "상자 한 단계 위 확률 +%" },
};
public static bool Known(string key)
{
return key != null && Keys.ContainsKey(key);
}
public static int Level(SaveData d, string node)
{
return Counts.Get(d.dev.nodes, node);
}
/// <summary>효과 합 = Σ(키를 가진 노드) pct_per_lv × Lv(단위는 키마다 · Keys).</summary>
public static float Mod(OutGameData db, SaveData d, string key)
{
if (db == null || db.Dev == null || d == null) return 0f;
float s = 0f;
foreach (var n in db.Dev.nodes)
{
int lv = Level(d, n.id);
if (lv <= 0) continue;
foreach (var k in n.EffectKeys)
if (k == key) s += n.pctPerLv * lv;
}
return s;
}
/// <summary>% 효과의 배율 = 1 + Mod/100.</summary>
public static float Mult(OutGameData db, SaveData d, string key)
{
return 1f + Mod(db, d, key) / 100f;
}
/// <summary>Lv → Lv+1 비용(골드 · 도면).</summary>
public static void NextCost(DevTree.Node n, int lv, out int gold, out int blueprint)
{
gold = Mathf.RoundToInt(n.goldBase * Mathf.Pow(n.goldGrowth, lv));
blueprint = lv + 1 >= n.blueprintFromLv ? n.blueprintPerLv : 0;
}
public static bool Unlocked(SaveData d, DevTree.Node n)
{
foreach (var r in n.requires)
if (Level(d, r.node) < r.lv) return false;
return true;
}
public static string CanUpgrade(OutGameData db, SaveData d, string id)
{
var n = db.Dev.Get(id);
if (n == null) return "없는 노드";
int lv = Level(d, id);
if (lv >= n.lvMax) return "최고 레벨";
if (!Unlocked(d, n))
{
var sb = new List<string>();
foreach (var r in n.requires)
if (Level(d, r.node) < r.lv) sb.Add(db.Dev.Get(r.node).nameKo + " Lv " + r.lv);
return "선행: " + string.Join(" · ", sb.ToArray());
}
int gold, bp;
NextCost(n, lv, out gold, out bp);
if (d.gold < gold) return "골드 부족 " + d.gold.ToString("N0") + " / " + gold.ToString("N0");
if (d.inv.blueprint < bp) return "도면 부족 " + d.inv.blueprint + " / " + bp;
return null;
}
public static string Upgrade(OutGameData db, SaveData d, string id)
{
var why = CanUpgrade(db, d, id);
if (why != null) return why;
var n = db.Dev.Get(id);
int gold, bp;
NextCost(n, Level(d, id), out gold, out bp);
d.gold -= gold;
d.inv.blueprint -= bp;
d.dev.blueprintSpent += bp;
Counts.Add(d.dev.nodes, id, 1);
return null;
}
public static int TotalLevels(SaveData d)
{
int n = 0;
foreach (var c in d.dev.nodes) n += c.n;
return n;
}
/// <summary>트리 초기화 = 보옥 dev_reset_gem · 쓴 도면 환급 · 골드 환급 없음.</summary>
public static string Reset(OutGameData db, SaveData d)
{
if (TotalLevels(d) == 0) return "초기화할 노드 없음";
int cost = db.Economy.DevResetGem;
if (d.gem < cost) return "보옥 부족 " + d.gem + " / " + cost;
d.gem -= cost;
d.inv.blueprint += d.dev.blueprintSpent;
d.dev.blueprintSpent = 0;
d.dev.nodes.Clear();
return null;
}
}
}