2026-11-05 16:20:07 +00:00
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace TKS
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// 적 AI 점수 가중치(PD #1098 · 전술규칙_적AI_기획서 §2-3 · SOT = Assets/Data/AI/ai_weights.csv = 기획 tables/ 사본) →
|
|
|
|
|
/// Resources/AI/AiWeights(메뉴 TKS/Import Gimmicks 가 함께 임포트). 코드는 키 이름만 안다(C45).
|
|
|
|
|
/// </summary>
|
|
|
|
|
public class AiWeights : ScriptableObject
|
|
|
|
|
{
|
|
|
|
|
public const string CsvPath = "Data/AI/ai_weights.csv"; // Assets/ 아래
|
|
|
|
|
public const string ResourcePath = "AI/AiWeights";
|
|
|
|
|
|
|
|
|
|
/// <summary>코드가 읽는 키(표에 없으면 임포트 오류).</summary>
|
|
|
|
|
public static readonly string[] Keys =
|
|
|
|
|
{
|
2026-11-10 08:25:47 +00:00
|
|
|
"dmg_player", "kill_base", "kill_hp_ratio", "dmg_own", "gimmick_dest_bonus", "chain", "support_reach", "pierce_hit",
|
2026-11-05 16:20:07 +00:00
|
|
|
"archer_cover", "high_ground", "cav_line", "splash_hit", "route_open", "zone_end", "banner", "skill_waste", "approach", "boss_accuracy", "mode_accuracy",
|
|
|
|
|
"noise_power", "top_n", "dirs", "dirs_many", "many_units", "power_1", "power_2", "power_3", "budget_ms", "boss_active", "active_max_chapter"
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
[Serializable]
|
|
|
|
|
public class Entry
|
|
|
|
|
{
|
|
|
|
|
public string key;
|
|
|
|
|
public float value;
|
|
|
|
|
public string desc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public List<Entry> entries = new List<Entry>();
|
|
|
|
|
Dictionary<string, float> index;
|
|
|
|
|
|
|
|
|
|
public float this[string key]
|
|
|
|
|
{
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
if (index == null)
|
|
|
|
|
{
|
|
|
|
|
index = new Dictionary<string, float>();
|
|
|
|
|
foreach (var e in entries) index[e.key] = e.value;
|
|
|
|
|
}
|
|
|
|
|
float v;
|
|
|
|
|
if (index.TryGetValue(key, out v)) return v;
|
|
|
|
|
Debug.LogError("[TKS] ai_weights.csv 에 키 없음: " + key);
|
|
|
|
|
return 0f;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static AiWeights cached;
|
|
|
|
|
|
|
|
|
|
public static AiWeights Load()
|
|
|
|
|
{
|
|
|
|
|
if (cached == null) cached = Resources.Load<AiWeights>(ResourcePath);
|
|
|
|
|
return cached;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void Invalidate()
|
|
|
|
|
{
|
|
|
|
|
cached = null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>CSV(key,value,desc_ko) 해석 · 키 누락·숫자 아님 = errors.</summary>
|
|
|
|
|
public static List<Entry> Parse(string text, List<string> errors)
|
|
|
|
|
{
|
|
|
|
|
var list = new List<Entry>();
|
|
|
|
|
var seen = new HashSet<string>();
|
|
|
|
|
foreach (var r in StageTable.ReadCsv(text, "ai_weights.csv", errors))
|
|
|
|
|
{
|
|
|
|
|
string k, v, d;
|
|
|
|
|
r.TryGetValue("key", out k);
|
|
|
|
|
r.TryGetValue("value", out v);
|
|
|
|
|
r.TryGetValue("desc_ko", out d);
|
|
|
|
|
float f;
|
|
|
|
|
if (string.IsNullOrEmpty(k) || !float.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out f))
|
|
|
|
|
{
|
|
|
|
|
errors.Add("ai_weights.csv 줄 " + r["#"] + ": key·value 형식(「" + k + "」 · 「" + v + "」)");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!seen.Add(k)) errors.Add("ai_weights.csv 줄 " + r["#"] + ": key 중복 「" + k + "」");
|
|
|
|
|
list.Add(new Entry { key = k, value = f, desc = d ?? "" });
|
|
|
|
|
}
|
|
|
|
|
foreach (var k in Keys)
|
|
|
|
|
{
|
|
|
|
|
if (!seen.Contains(k)) errors.Add("ai_weights.csv: 키 없음 「" + k + "」");
|
|
|
|
|
}
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|