using System.Collections.Generic;
namespace TKS
{
/// 스킬 수치가 레벨(lv_scale)·★5(패시브 배율)로 커지는 방식.
public enum SkillScale
{
/// 값 × 계수(비율·가산 · 기획서 §5 「수치 × (1 + lv_scale·(lv−1))」).
Value,
/// 배율(×k)은 1 을 넘는 몫만 키운다: 1 + (k − 1) × 계수 (×2 가 Lv5 에 ×2.8 이 되지 않게).
Mult,
/// 횟수 = 반올림(값 × 계수).
Count,
/// 켜고 끄는 값·턴·반경·확률(커지지 않음).
Fixed
}
///
/// effect_key 처리기 목록(2026-11-10 #1112 · PD 엑셀 「장수 목록」 새 기획 · 표 = skill_effects.csv 효과 1줄 = 키 · 대상 · 조건 · 지속 · p1~p3).
/// 이 표는 「처리기의 뜻 + 레벨 계수가 붙는 칸」이고 수치는 전부 skill_effects.csv(SkillInfoDb)에서 온다(C45). 표에 없는 키 = 임포트 오류.
/// 보물 진영 효과(range_mult · splash_radius_mult · side_crit_pct)도 같은 질의(SkillMods)를 탄다.
///
public static class SkillKeys
{
// ── 능력치(상시 · 대상·조건 칸)
public const string AtkPct = "atk_pct";
public const string AtkRatio = "atk_ratio";
public const string DmgPct = "dmg_pct";
public const string DmgTakenPct = "dmg_taken_pct";
public const string DmgTakenRangedPct = "dmg_taken_ranged_pct";
public const string DmgTakenFromArcherPct = "dmg_taken_from_archer_pct";
public const string CritChance = "crit_chance";
public const string CritDmgPct = "crit_dmg_pct";
public const string CritAlways = "crit_always";
public const string CounterTakenPct = "counter_taken_pct";
public const string CounterDmgPct = "counter_dmg_pct";
public const string CounterLifesteal = "counter_lifesteal";
public const string Lifesteal = "lifesteal";
public const string HpMaxPct = "hp_max_pct";
public const string SlingDistPct = "sling_dist_pct";
public const string RangeMult = "range_mult";
public const string SplashRadiusMult = "splash_radius_mult";
public const string SplashAllyRatio = "splash_ally_ratio";
public const string MeleePenaltyOff = "melee_penalty_off";
public const string MatchupFloor = "matchup_floor";
public const string KnockbackTakenPct = "knockback_taken_pct";
public const string PushMult = "push_mult";
public const string SelfBounceMult = "self_bounce_mult";
public const string KillStackAtk = "kill_stack_atk";
public const string AtkMissingHp = "atk_missing_hp";
public const string AtkPerAllyRange = "atk_per_ally_range";
public const string OnAllyDeathAtk = "on_ally_death_atk";
public const string DashDmgPct = "dash_dmg_pct";
public const string DmgPerHit = "dmg_per_hit";
public const string FirstHitBonus = "first_hit_bonus";
public const string SkillPowerLowHp = "skill_power_low_hp";
public const string SkillHpCost = "skill_hp_cost";
public const string LowHpTaken = "low_hp_taken";
public const string UnusedMoveDr = "unused_move_dr";
public const string RegenPct = "regen_pct";
public const string HealOnActionEnd = "heal_on_action_end";
public const string HealOnKill = "heal_on_kill";
public const string KillReaction = "kill_reaction";
public const string ReactionAtkStack = "reaction_atk_stack";
public const string MoveBank = "move_bank";
public const string TerrainImmune = "terrain_immune";
public const string Revive = "revive";
public const string NegateHits = "negate_hits";
public const string DrainAura = "drain_aura";
public const string Guard = "guard";
public const string GuardCounter = "guard_counter";
public const string GuardRadiusMult = "guard_radius_mult";
public const string GuardDr = "guard_dr";
public const string AxeAura = "axe_aura";
public const string ZoneSlow = "zone_slow";
public const string OnAttackedStun = "on_attacked_stun";
public const string OnAttackedConfuse = "on_attacked_confuse";
public const string MultiMaleStun = "multi_male_stun";
public const string TurnEndHeal = "turn_end_heal";
public const string TurnEndHealLowest = "turn_end_heal_lowest";
public const string TurnEndCleanse = "turn_end_cleanse";
public const string TurnStartHealRange = "turn_start_heal_range";
public const string DeathBanner = "death_banner";
public const string Hot = "hot";
public const string HealCleanse = "heal_cleanse";
public const string AllyHitHeal = "ally_hit_heal";
public const string AllyHitBuff = "ally_hit_buff";
public const string AllyHitWarp = "ally_hit_warp";
public const string AllyHitReaction = "ally_hit_reaction";
public const string ActionEndSacrifice = "action_end_sacrifice";
public const string PushContinue = "push_continue";
public const string PushedAllyAtkAdd = "pushed_ally_atk_add";
public const string SelfPushedMult = "self_pushed_mult";
public const string GimmickMult = "gimmick_mult";
public const string Zombie = "zombie";
public const string DoubleHitStun = "double_hit_stun";
public const string KillInRangeReward = "kill_in_range_reward";
public const string Invuln = "invuln";
public const string DamageToAtk = "damage_to_atk";
// ── 명중 시 상태 이상(공격자 효과 · 조건 칸 · p3 = 확률)
public const string HitStun = "hit_stun";
public const string HitBleed = "hit_bleed";
public const string HitPoison = "hit_poison";
public const string PoisonStack = "poison_stack";
public const string HitBurn = "hit_burn";
public const string HitConfuse = "hit_confuse";
public const string HitPush = "hit_push";
// ── 다음 행동(장착 중) 이동·공격 변형
public const string ExtraAction = "extra_action";
public const string ReactionOnHit = "reaction_on_hit";
public const string ReactionOnKill = "reaction_on_kill";
public const string ReactionOnTerrain = "reaction_on_terrain";
public const string PassAllies = "pass_allies";
public const string PierceThrough = "pierce_through";
public const string SwapOnHit = "swap_on_hit";
public const string DragBack = "drag_back";
public const string AimPush = "aim_push";
public const string ReturnOnHit = "return_on_hit";
public const string ReturnAlways = "return_always";
public const string ReturnAim = "return_aim";
public const string WallBounce = "wall_bounce";
public const string Rampage = "rampage";
public const string RepeatAction = "repeat_action";
// ── 원거리 특수 사격(장착 중)
public const string ArrowPassAllies = "arrow_pass_allies";
public const string ShotGiant = "shot_giant";
public const string ShotPierce = "shot_pierce";
public const string ShotBlast = "shot_blast";
public const string ShotFan = "shot_fan";
public const string Multishot = "multishot";
public const string ShotRicochet = "shot_ricochet";
public const string ShotSingle = "shot_single";
public const string ShotWhirl = "shot_whirl";
public const string ShotBarricade = "shot_barricade";
public const string ShotFirewall = "shot_firewall";
public const string ShotAsArcher = "shot_as_archer";
public const string FireZone = "fire_zone";
public const string ZoneBurn = "zone_burn";
public const string ZoneHitsAllies = "zone_hits_allies";
public const string PoisonZone = "poison_zone";
public const string SplashHeal = "splash_heal";
public const string SplashHealLowest = "splash_heal_lowest";
// ── 즉시 발동
public const string AoeStrike = "aoe_strike";
public const string AoeDrain = "aoe_drain";
public const string Volley = "volley";
public const string CharmPull = "charm_pull";
public const string HealAtk = "heal_atk";
public const string HealSelfPct = "heal_self_pct";
public const string Cleanse = "cleanse";
public const string Teleport = "teleport";
public const string GrantReaction = "grant_reaction";
public class Info
{
public SkillScale Mode;
/// 레벨·★ 계수가 붙는 칸(0 = p1 · 1 = p2 · 2 = p3).
public int Idx;
/// p2 도 같은 계수(이동 % · 공격 % 두 값).
public bool Both;
}
static Info I(SkillScale m, int idx = 0, bool both = false)
{
return new Info { Mode = m, Idx = idx, Both = both };
}
static readonly Info Fix = new Info { Mode = SkillScale.Fixed };
public static readonly Dictionary All = new Dictionary
{
{ AtkPct, I(SkillScale.Value) }, { AtkRatio, I(SkillScale.Mult) }, { DmgPct, I(SkillScale.Value) },
{ DmgTakenPct, I(SkillScale.Value) }, { DmgTakenRangedPct, I(SkillScale.Value) }, { DmgTakenFromArcherPct, I(SkillScale.Value) },
{ CritChance, I(SkillScale.Value) }, { CritDmgPct, I(SkillScale.Value) }, { CritAlways, Fix },
{ CounterTakenPct, I(SkillScale.Value) }, { CounterDmgPct, I(SkillScale.Value) }, { CounterLifesteal, I(SkillScale.Value) },
{ Lifesteal, I(SkillScale.Value) }, { HpMaxPct, I(SkillScale.Value) }, { SlingDistPct, I(SkillScale.Value) },
{ RangeMult, I(SkillScale.Mult) }, { SplashRadiusMult, I(SkillScale.Mult) }, { SplashAllyRatio, Fix },
{ MeleePenaltyOff, Fix }, { MatchupFloor, Fix }, { KnockbackTakenPct, Fix }, { PushMult, I(SkillScale.Mult) },
{ SelfBounceMult, Fix }, { KillStackAtk, I(SkillScale.Value) }, { AtkMissingHp, I(SkillScale.Value) },
{ AtkPerAllyRange, I(SkillScale.Value) }, { OnAllyDeathAtk, I(SkillScale.Value) }, { DashDmgPct, I(SkillScale.Value) },
{ DmgPerHit, I(SkillScale.Value) }, { FirstHitBonus, I(SkillScale.Value) }, { SkillPowerLowHp, I(SkillScale.Value) },
{ SkillHpCost, Fix }, { LowHpTaken, Fix }, { UnusedMoveDr, I(SkillScale.Value) }, { RegenPct, I(SkillScale.Value) },
{ HealOnActionEnd, I(SkillScale.Value) }, { HealOnKill, I(SkillScale.Value) }, { KillReaction, Fix },
{ ReactionAtkStack, I(SkillScale.Value) }, { MoveBank, Fix }, { TerrainImmune, Fix }, { Revive, Fix }, { NegateHits, Fix },
{ DrainAura, I(SkillScale.Value) }, { Guard, Fix }, { GuardCounter, Fix }, { GuardRadiusMult, I(SkillScale.Mult) },
{ GuardDr, I(SkillScale.Value) }, { AxeAura, I(SkillScale.Value, 1) }, { ZoneSlow, I(SkillScale.Value) },
{ OnAttackedStun, Fix }, { OnAttackedConfuse, Fix }, { MultiMaleStun, Fix }, { TurnEndHeal, I(SkillScale.Value, 1) },
{ TurnEndHealLowest, I(SkillScale.Value, 1) }, { TurnEndCleanse, Fix }, { TurnStartHealRange, I(SkillScale.Value) },
{ DeathBanner, Fix }, { Hot, I(SkillScale.Value) }, { HealCleanse, Fix }, { AllyHitHeal, I(SkillScale.Value) },
{ AllyHitBuff, I(SkillScale.Value, 0, true) }, { AllyHitWarp, Fix }, { AllyHitReaction, Fix }, { ActionEndSacrifice, Fix },
{ PushContinue, Fix }, { PushedAllyAtkAdd, I(SkillScale.Value) }, { SelfPushedMult, I(SkillScale.Mult) },
{ GimmickMult, I(SkillScale.Mult) }, { Zombie, Fix }, { DoubleHitStun, Fix }, { KillInRangeReward, Fix },
{ Invuln, Fix }, { DamageToAtk, Fix },
{ HitStun, Fix }, { HitBleed, I(SkillScale.Value, 1) }, { HitPoison, I(SkillScale.Value, 1) }, { PoisonStack, Fix },
{ HitBurn, I(SkillScale.Value, 1) }, { HitConfuse, Fix }, { HitPush, I(SkillScale.Value) },
{ ExtraAction, Fix }, { ReactionOnHit, Fix }, { ReactionOnKill, Fix }, { ReactionOnTerrain, Fix }, { PassAllies, Fix },
{ PierceThrough, Fix }, { SwapOnHit, Fix }, { DragBack, Fix }, { AimPush, Fix }, { ReturnOnHit, Fix }, { ReturnAlways, Fix },
{ ReturnAim, Fix }, { WallBounce, Fix }, { Rampage, Fix }, { RepeatAction, Fix },
{ ArrowPassAllies, Fix }, { ShotGiant, I(SkillScale.Mult, 1) }, { ShotPierce, I(SkillScale.Mult) }, { ShotBlast, I(SkillScale.Mult, 1) },
{ ShotFan, Fix }, { Multishot, Fix }, { ShotRicochet, I(SkillScale.Value, 1) }, { ShotSingle, Fix }, { ShotWhirl, Fix },
{ ShotBarricade, Fix }, { ShotFirewall, I(SkillScale.Mult, 2) }, { ShotAsArcher, Fix }, { FireZone, Fix }, { ZoneBurn, I(SkillScale.Value, 1) }, { ZoneHitsAllies, Fix }, { PoisonZone, Fix },
{ SplashHeal, I(SkillScale.Value) }, { SplashHealLowest, I(SkillScale.Value) },
{ AoeStrike, I(SkillScale.Mult, 1) }, { AoeDrain, I(SkillScale.Mult) }, { Volley, I(SkillScale.Mult) }, { CharmPull, Fix },
{ HealAtk, I(SkillScale.Value) }, { HealSelfPct, I(SkillScale.Value) }, { Cleanse, Fix }, { Teleport, Fix }, { GrantReaction, Fix },
};
public static bool Known(string key)
{
return key != null && All.ContainsKey(key);
}
/// 레벨·★ 계수 factor 를 적용한 값.
public static float Apply(SkillScale mode, float p, float factor)
{
switch (mode)
{
case SkillScale.Value: return p * factor;
case SkillScale.Mult: return p > 0f ? 1f + (p - 1f) * factor : p;
case SkillScale.Count: return UnityEngine.Mathf.Floor(p * factor + 0.5f);
}
return p;
}
}
}