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; } } }