2026-11-05 14:31:27 +00:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
|
|
|
|
|
|
namespace TKS
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>스킬 수치가 레벨(lv_scale)·★5(패시브 배율)로 커지는 방식.</summary>
|
|
|
|
|
|
public enum SkillScale
|
|
|
|
|
|
{
|
2026-11-10 10:00:18 +00:00
|
|
|
|
/// <summary>값 × 계수(비율·가산 · 기획서 §5 「수치 × (1 + lv_scale·(lv−1))」).</summary>
|
2026-11-05 14:31:27 +00:00
|
|
|
|
Value,
|
|
|
|
|
|
/// <summary>배율(×k)은 1 을 넘는 몫만 키운다: 1 + (k − 1) × 계수 (×2 가 Lv5 에 ×2.8 이 되지 않게).</summary>
|
|
|
|
|
|
Mult,
|
|
|
|
|
|
/// <summary>횟수 = 반올림(값 × 계수).</summary>
|
|
|
|
|
|
Count,
|
2026-11-10 10:00:18 +00:00
|
|
|
|
/// <summary>켜고 끄는 값·턴·반경·확률(커지지 않음).</summary>
|
2026-11-05 14:31:27 +00:00
|
|
|
|
Fixed
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-11-10 10:00:18 +00:00
|
|
|
|
/// 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)를 탄다.
|
2026-11-05 14:31:27 +00:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
public static class SkillKeys
|
|
|
|
|
|
{
|
2026-11-10 10:00:18 +00:00
|
|
|
|
// ── 능력치(상시 · 대상·조건 칸)
|
|
|
|
|
|
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";
|
2026-11-05 14:31:27 +00:00
|
|
|
|
public const string CounterTakenPct = "counter_taken_pct";
|
2026-11-10 10:00:18 +00:00
|
|
|
|
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";
|
2026-11-05 14:31:27 +00:00
|
|
|
|
public const string RangeMult = "range_mult";
|
|
|
|
|
|
public const string SplashRadiusMult = "splash_radius_mult";
|
|
|
|
|
|
public const string SplashAllyRatio = "splash_ally_ratio";
|
2026-11-10 10:00:18 +00:00
|
|
|
|
public const string MeleePenaltyOff = "melee_penalty_off";
|
2026-11-05 14:31:27 +00:00
|
|
|
|
public const string MatchupFloor = "matchup_floor";
|
|
|
|
|
|
public const string KnockbackTakenPct = "knockback_taken_pct";
|
2026-11-10 10:00:18 +00:00
|
|
|
|
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";
|
2026-11-05 14:31:27 +00:00
|
|
|
|
public const string OnAllyDeathAtk = "on_ally_death_atk";
|
2026-11-10 10:00:18 +00:00
|
|
|
|
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";
|
2026-11-10 11:36:58 +00:00
|
|
|
|
public const string ZoneBurn = "zone_burn";
|
|
|
|
|
|
public const string ZoneHitsAllies = "zone_hits_allies";
|
2026-11-10 10:00:18 +00:00
|
|
|
|
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";
|
2026-11-05 15:22:54 +00:00
|
|
|
|
public const string HealSelfPct = "heal_self_pct";
|
2026-11-10 10:00:18 +00:00
|
|
|
|
public const string Cleanse = "cleanse";
|
|
|
|
|
|
public const string Teleport = "teleport";
|
|
|
|
|
|
public const string GrantReaction = "grant_reaction";
|
2026-11-05 14:31:27 +00:00
|
|
|
|
|
|
|
|
|
|
public class Info
|
|
|
|
|
|
{
|
2026-11-10 10:00:18 +00:00
|
|
|
|
public SkillScale Mode;
|
|
|
|
|
|
/// <summary>레벨·★ 계수가 붙는 칸(0 = p1 · 1 = p2 · 2 = p3).</summary>
|
|
|
|
|
|
public int Idx;
|
|
|
|
|
|
/// <summary>p2 도 같은 계수(이동 % · 공격 % 두 값).</summary>
|
|
|
|
|
|
public bool Both;
|
2026-11-05 14:31:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-10 10:00:18 +00:00
|
|
|
|
static Info I(SkillScale m, int idx = 0, bool both = false)
|
2026-11-05 14:31:27 +00:00
|
|
|
|
{
|
2026-11-10 10:00:18 +00:00
|
|
|
|
return new Info { Mode = m, Idx = idx, Both = both };
|
2026-11-05 14:31:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-11-10 10:00:18 +00:00
|
|
|
|
static readonly Info Fix = new Info { Mode = SkillScale.Fixed };
|
|
|
|
|
|
|
2026-11-05 14:31:27 +00:00
|
|
|
|
public static readonly Dictionary<string, Info> All = new Dictionary<string, Info>
|
|
|
|
|
|
{
|
2026-11-10 10:00:18 +00:00
|
|
|
|
{ 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 },
|
2026-11-10 11:36:58 +00:00
|
|
|
|
{ ShotBarricade, Fix }, { ShotFirewall, I(SkillScale.Mult, 2) }, { ShotAsArcher, Fix }, { FireZone, Fix }, { ZoneBurn, I(SkillScale.Value, 1) }, { ZoneHitsAllies, Fix }, { PoisonZone, Fix },
|
2026-11-10 10:00:18 +00:00
|
|
|
|
{ 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 },
|
2026-11-05 14:31:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
public static bool Known(string key)
|
|
|
|
|
|
{
|
|
|
|
|
|
return key != null && All.ContainsKey(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>레벨·★ 계수 factor 를 적용한 값.</summary>
|
|
|
|
|
|
public static float Apply(SkillScale mode, float p, float factor)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (mode)
|
|
|
|
|
|
{
|
|
|
|
|
|
case SkillScale.Value: return p * factor;
|
2026-11-10 10:00:18 +00:00
|
|
|
|
case SkillScale.Mult: return p > 0f ? 1f + (p - 1f) * factor : p;
|
2026-11-05 14:31:27 +00:00
|
|
|
|
case SkillScale.Count: return UnityEngine.Mathf.Floor(p * factor + 0.5f);
|
|
|
|
|
|
}
|
|
|
|
|
|
return p;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|