장수 스킬 144개 새 기획 구현(#1112 · PD 2026-11-10 엑셀 「장수 목록」) — 효과 표 skill_effects.csv(효과 1줄 · 한글 주석 열) · 이름 56개 새로
- 수치 SOT = Assets/Data/OutGame/skill_effects.csv(skill_id·key·target·cond·dur·p1~p3 + effect_ko·p1_ko~p3_ko 주석) · skills.csv effect_key = 키 목록 · heroes.csv gender(m|f) - 처리기 121종(SkillKeys) · 대상 칸(자신·같은 편·진영·반경·행동 반경) · 조건 칸(치명타·상대 상태 이상·병종·성별·턴·장착 중) · 지속 라운드 - 상태 이상 = 혼란·출혈(이동 거리 비례)·중독(중첩)·화상 + 기절(기믹 행동 불가 재사용) · 정화 · 회복·흡혈·부활·무적·무효화·호위(반격) - 재행동(유비·위연·여몽·손견·곽가·우금·맹획·감녕·고순·장합) · 치명타 확률·피해·무조건 - 이동 변형: 관통(손견·안량·손책 아군 통과) · 핀볼 튕김(손책·동탁) · 복귀(조운·손책·여포·문추·공손찬·순우경 · 하후돈 탭 지점·쓸어 감) · 위치 교환(위연) · 끌어오기(장료) · 아군 통과+탭 방향 밀치기(조조) · 반복(정보) · 난동(동탁) - 특수 사격: 거대 화살 · 직선 관통 · 폭발(화염구·독구름·운석·화살비) · 부채꼴 · 멀티샷 · 도탄 · 회오리 · 목책 · 불 장벽·불꽃·독 지대 · 궁병식 비도 · 광역 회복 - 즉시형: 광역 타격·밀기 · 흡수 · 일제 사격 · 미인계 끌어오기 · 범위 회복·정화 · 재행동 부여 · 변진 - 가상 실행(적 AI) 스킬 상태 사본(TakeState/RestoreState) · 옛 견제 스킬 확장 제거 - 테스트(고친 부분 클래스만): SkillTests 19(새로) · BattleFlowTests 42 · TacticalAITests 7 · GimmickTests 19 · OutGameTests 15 · OutGameCTests 12 전부 통과 · console error 0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b383e355c8
commit
0b2f56f271
|
|
@ -1,88 +1,88 @@
|
||||||
id,name_ko,hanja,faction,unit_type,rarity,hp_mult,atk_mult,def_mult,art,skill_active,skill_passive,is_soldier,first_stage
|
id,name_ko,hanja,faction,unit_type,rarity,hp_mult,atk_mult,def_mult,art,skill_active,skill_passive,is_soldier,first_stage,gender
|
||||||
liubei,유비,劉備,shu,infantry,3,1.1,1.0,1.0,liubei,liubei_a,liubei_p,0,1-1
|
liubei,유비,劉備,shu,infantry,3,1.1,1.0,1.0,liubei,liubei_a,liubei_p,0,1-1,m
|
||||||
guanyu,관우,關羽,shu,spear,3,1.1,1.25,1.0,guanyu,guanyu_a,guanyu_p,0,1-1
|
guanyu,관우,關羽,shu,spear,3,1.1,1.25,1.0,guanyu,guanyu_a,guanyu_p,0,1-1,m
|
||||||
zhangfei,장비,張飛,shu,cavalry,3,1.0,1.2,1.0,zhangfei,zhangfei_a,zhangfei_p,0,1-1
|
zhangfei,장비,張飛,shu,cavalry,3,1.0,1.2,1.0,zhangfei,zhangfei_a,zhangfei_p,0,1-1,m
|
||||||
zhaoyun,조운,趙雲,shu,cavalry,3,1.1,1.1,1.0,zhaoyun,zhaoyun_a,zhaoyun_p,0,3-3
|
zhaoyun,조운,趙雲,shu,cavalry,3,1.1,1.1,1.0,zhaoyun,zhaoyun_a,zhaoyun_p,0,3-3,m
|
||||||
huangzhong,황충,黃忠,shu,archer,3,1.0,1.25,1.0,huangzhong,huangzhong_a,huangzhong_p,0,
|
huangzhong,황충,黃忠,shu,archer,3,1.0,1.25,1.0,huangzhong,huangzhong_a,huangzhong_p,0,,m
|
||||||
zhugeliang,제갈량,諸葛亮,shu,strategist,3,1.15,1.15,1.1,zhugeliang,zhugeliang_a,zhugeliang_p,0,8-13
|
zhugeliang,제갈량,諸葛亮,shu,strategist,3,1.15,1.15,1.1,zhugeliang,zhugeliang_a,zhugeliang_p,0,8-13,m
|
||||||
zhoucang,주창,周倉,shu,infantry,2,1.05,1.05,1.05,zhoucang,zhoucang_a,zhoucang_p,0,7-2
|
zhoucang,주창,周倉,shu,infantry,2,1.05,1.05,1.05,zhoucang,zhoucang_a,zhoucang_p,0,7-2,m
|
||||||
guanping,관평,關平,shu,spear,2,1.05,1.05,1.05,guanping,guanping_a,guanping_p,0,7-7
|
guanping,관평,關平,shu,spear,2,1.05,1.05,1.05,guanping,guanping_a,guanping_p,0,7-7,m
|
||||||
xushu,서서,徐庶,shu,strategist,3,1.15,1.15,1.1,xushu,xushu_a,xushu_p,0,8-6
|
xushu,서서,徐庶,shu,strategist,3,1.15,1.15,1.1,xushu,xushu_a,xushu_p,0,8-6,m
|
||||||
taishici,태사자,太史慈,shu,archer,3,1.15,1.15,1.1,taishici,taishici_a,taishici_p,0,3-8
|
taishici,태사자,太史慈,shu,archer,3,1.15,1.15,1.1,taishici,taishici_a,taishici_p,0,3-8,m
|
||||||
machao,마초,馬超,shu,cavalry,3,1.15,1.15,1.1,machao,machao_a,machao_p,0,
|
machao,마초,馬超,shu,cavalry,3,1.15,1.15,1.1,machao,machao_a,machao_p,0,,m
|
||||||
weiyan,위연,魏延,shu,infantry,3,1.15,1.15,1.1,weiyan,weiyan_a,weiyan_p,0,
|
weiyan,위연,魏延,shu,infantry,3,1.15,1.15,1.1,weiyan,weiyan_a,weiyan_p,0,,m
|
||||||
pangtong,방통,龐統,shu,strategist,3,1.15,1.15,1.1,pangtong,pangtong_a,pangtong_p,0,
|
pangtong,방통,龐統,shu,strategist,3,1.15,1.15,1.1,pangtong,pangtong_a,pangtong_p,0,,m
|
||||||
jiangwei,강유,姜維,shu,spear,3,1.15,1.15,1.1,jiangwei,jiangwei_a,jiangwei_p,0,
|
jiangwei,강유,姜維,shu,spear,3,1.15,1.15,1.1,jiangwei,jiangwei_a,jiangwei_p,0,,m
|
||||||
liufeng,유봉,劉封,shu,cavalry,2,1.05,1.05,1.05,liufeng,liufeng_a,liufeng_p,0,8-11
|
liufeng,유봉,劉封,shu,cavalry,2,1.05,1.05,1.05,liufeng,liufeng_a,liufeng_p,0,8-11,m
|
||||||
jianyong,간옹,簡雍,shu,strategist,2,1.05,1.05,1.05,jianyong,jianyong_a,jianyong_p,0,3-12
|
jianyong,간옹,簡雍,shu,strategist,2,1.05,1.05,1.05,jianyong,jianyong_a,jianyong_p,0,3-12,m
|
||||||
sunqian,손건,孫乾,shu,strategist,2,1.05,1.05,1.05,sunqian,sunqian_a,sunqian_p,0,3-12
|
sunqian,손건,孫乾,shu,strategist,2,1.05,1.05,1.05,sunqian,sunqian_a,sunqian_p,0,3-12,m
|
||||||
mizhu,미축,糜竺,shu,archer,2,1.05,1.05,1.05,mizhu,mizhu_a,mizhu_p,0,3-12
|
mizhu,미축,糜竺,shu,archer,2,1.05,1.05,1.05,mizhu,mizhu_a,mizhu_p,0,3-12,m
|
||||||
caocao,조조,曹操,wei,infantry,3,1.15,1.15,1.1,cc_boss_caocao,caocao_a,caocao_p,0,1-8
|
caocao,조조,曹操,wei,infantry,3,1.15,1.15,1.1,cc_boss_caocao,caocao_a,caocao_p,0,1-8,m
|
||||||
xiahoudun,하후돈,夏侯惇,wei,cavalry,3,1.15,1.15,1.1,cc_boss_xiahoudun,xiahoudun_a,xiahoudun_p,0,5-14
|
xiahoudun,하후돈,夏侯惇,wei,cavalry,3,1.15,1.15,1.1,cc_boss_xiahoudun,xiahoudun_a,xiahoudun_p,0,5-14,m
|
||||||
xuchu,허저,許褚,wei,infantry,3,1.15,1.15,1.1,cc_boss_xuchu,xuchu_a,xuchu_p,0,5-15
|
xuchu,허저,許褚,wei,infantry,3,1.15,1.15,1.1,cc_boss_xuchu,xuchu_a,xuchu_p,0,5-15,m
|
||||||
zhangliao,장료,張遼,wei,cavalry,3,1.15,1.15,1.1,dz_boss_zhangliao,zhangliao_a,zhangliao_p,0,4-14
|
zhangliao,장료,張遼,wei,cavalry,3,1.15,1.15,1.1,dz_boss_zhangliao,zhangliao_a,zhangliao_p,0,4-14,m
|
||||||
xuhuang,서황,徐晃,wei,infantry,3,1.15,1.15,1.1,cc_boss_xuhuang,xuhuang_a,xuhuang_p,0,5-15
|
xuhuang,서황,徐晃,wei,infantry,3,1.15,1.15,1.1,cc_boss_xuhuang,xuhuang_a,xuhuang_p,0,5-15,m
|
||||||
zhanghe,장합,張郃,wei,spear,3,1.15,1.15,1.1,cc_boss_zhanghe,zhanghe_a,zhanghe_p,0,7-9
|
zhanghe,장합,張郃,wei,spear,3,1.15,1.15,1.1,cc_boss_zhanghe,zhanghe_a,zhanghe_p,0,7-9,m
|
||||||
caoren,조인,曹仁,wei,infantry,3,1.15,1.15,1.1,cc_boss_caoren,caoren_a,caoren_p,0,8-10
|
caoren,조인,曹仁,wei,infantry,3,1.15,1.15,1.1,cc_boss_caoren,caoren_a,caoren_p,0,8-10,m
|
||||||
yujin,우금,于禁,wei,infantry,2,1.05,1.05,1.05,cc_boss_yujin,yujin_a,yujin_p,0,3-15
|
yujin,우금,于禁,wei,infantry,2,1.05,1.05,1.05,cc_boss_yujin,yujin_a,yujin_p,0,3-15,m
|
||||||
simayi,사마의,司馬懿,wei,strategist,3,1.15,1.15,1.1,wei_boss_simayi,simayi_a,simayi_p,0,
|
simayi,사마의,司馬懿,wei,strategist,3,1.15,1.15,1.1,wei_boss_simayi,simayi_a,simayi_p,0,,m
|
||||||
xiahouyuan,하후연,夏侯淵,wei,archer,3,1.15,1.15,1.1,xiahouyuan,xiahouyuan_a,xiahouyuan_p,0,
|
xiahouyuan,하후연,夏侯淵,wei,archer,3,1.15,1.15,1.1,xiahouyuan,xiahouyuan_a,xiahouyuan_p,0,,m
|
||||||
guojia,곽가,郭嘉,wei,strategist,3,1.15,1.15,1.1,guojia,guojia_a,guojia_p,0,
|
guojia,곽가,郭嘉,wei,strategist,3,1.15,1.15,1.1,guojia,guojia_a,guojia_p,0,,m
|
||||||
jiaxu,가후,賈詡,wei,strategist,3,1.15,1.15,1.1,jiaxu,jiaxu_a,jiaxu_p,0,
|
jiaxu,가후,賈詡,wei,strategist,3,1.15,1.15,1.1,jiaxu,jiaxu_a,jiaxu_p,0,,m
|
||||||
dianwei,전위,典韋,wei,infantry,3,1.15,1.15,1.1,dianwei,dianwei_a,dianwei_p,0,
|
dianwei,전위,典韋,wei,infantry,3,1.15,1.15,1.1,dianwei,dianwei_a,dianwei_p,0,,m
|
||||||
xunyu,순욱,荀彧,wei,strategist,3,1.15,1.15,1.1,xunyu,xunyu_a,xunyu_p,0,
|
xunyu,순욱,荀彧,wei,strategist,3,1.15,1.15,1.1,xunyu,xunyu_a,xunyu_p,0,,m
|
||||||
xiahoumao,하후무,夏侯楙,wei,cavalry,1,1.0,1.0,1.0,xiahoumao,xiahoumao_a,xiahoumao_p,0,
|
xiahoumao,하후무,夏侯楙,wei,cavalry,1,1.0,1.0,1.0,xiahoumao,xiahoumao_a,xiahoumao_p,0,,m
|
||||||
wanglang,왕랑,王朗,wei,strategist,1,1.0,1.0,1.0,wanglang,wanglang_a,wanglang_p,0,
|
wanglang,왕랑,王朗,wei,strategist,1,1.0,1.0,1.0,wanglang,wanglang_a,wanglang_p,0,,m
|
||||||
xiahouen,하후은,夏侯恩,wei,cavalry,1,1.0,1.0,1.0,cc_boss_xiahouen,xiahouen_a,xiahouen_p,0,9-8
|
xiahouen,하후은,夏侯恩,wei,cavalry,1,1.0,1.0,1.0,cc_boss_xiahouen,xiahouen_a,xiahouen_p,0,9-8,m
|
||||||
sunjian,손견,孫堅,wu,infantry,3,1.15,1.15,1.1,wu_sunjian,sunjian_a,sunjian_p,0,2-3
|
sunjian,손견,孫堅,wu,infantry,3,1.15,1.15,1.1,wu_sunjian,sunjian_a,sunjian_p,0,2-3,m
|
||||||
zhouyu,주유,周瑜,wu,strategist,3,1.15,1.15,1.1,wu_zhouyu,zhouyu_a,zhouyu_p,0,10-3
|
zhouyu,주유,周瑜,wu,strategist,3,1.15,1.15,1.1,wu_zhouyu,zhouyu_a,zhouyu_p,0,10-3,m
|
||||||
huanggai,황개,黃蓋,wu,infantry,3,1.15,1.15,1.1,wu_huanggai,huanggai_a,huanggai_p,0,10-7
|
huanggai,황개,黃蓋,wu,infantry,3,1.15,1.15,1.1,wu_huanggai,huanggai_a,huanggai_p,0,10-7,m
|
||||||
ganning,감녕,甘寧,wu,archer,3,1.15,1.15,1.1,wu_ganning,ganning_a,ganning_p,0,10-3
|
ganning,감녕,甘寧,wu,archer,3,1.15,1.15,1.1,wu_ganning,ganning_a,ganning_p,0,10-3,m
|
||||||
chengpu,정보,程普,wu,spear,2,1.05,1.05,1.05,wu_chengpu,chengpu_a,chengpu_p,0,2-3
|
chengpu,정보,程普,wu,spear,2,1.05,1.05,1.05,wu_chengpu,chengpu_a,chengpu_p,0,2-3,m
|
||||||
sunce,손책,孫策,wu,cavalry,3,1.15,1.15,1.1,sunce,sunce_a,sunce_p,0,
|
sunce,손책,孫策,wu,cavalry,3,1.15,1.15,1.1,sunce,sunce_a,sunce_p,0,,m
|
||||||
sunquan,손권,孫權,wu,archer,3,1.15,1.15,1.1,sunquan,sunquan_a,sunquan_p,0,
|
sunquan,손권,孫權,wu,archer,3,1.15,1.15,1.1,sunquan,sunquan_a,sunquan_p,0,,m
|
||||||
lusu,노숙,魯肅,wu,strategist,3,1.15,1.15,1.1,lusu,lusu_a,lusu_p,0,
|
lusu,노숙,魯肅,wu,strategist,3,1.15,1.15,1.1,lusu,lusu_a,lusu_p,0,,m
|
||||||
lvmeng,여몽,呂蒙,wu,infantry,3,1.15,1.15,1.1,lvmeng,lvmeng_a,lvmeng_p,0,
|
lvmeng,여몽,呂蒙,wu,infantry,3,1.15,1.15,1.1,lvmeng,lvmeng_a,lvmeng_p,0,,m
|
||||||
luxun,육손,陸遜,wu,strategist,3,1.15,1.15,1.1,luxun,luxun_a,luxun_p,0,
|
luxun,육손,陸遜,wu,strategist,3,1.15,1.15,1.1,luxun,luxun_a,luxun_p,0,,m
|
||||||
sunshangxiang,손상향,孫尙香,wu,archer,3,1.15,1.15,1.1,sunshangxiang,sunshangxiang_a,sunshangxiang_p,0,
|
sunshangxiang,손상향,孫尙香,wu,archer,3,1.15,1.15,1.1,sunshangxiang,sunshangxiang_a,sunshangxiang_p,0,,f
|
||||||
xiaoqiao,소교,小喬,wu,strategist,3,1.15,1.15,1.1,xiaoqiao,xiaoqiao_a,xiaoqiao_p,0,
|
xiaoqiao,소교,小喬,wu,strategist,3,1.15,1.15,1.1,xiaoqiao,xiaoqiao_a,xiaoqiao_p,0,,f
|
||||||
lingtong,능통,凌統,wu,spear,2,1.05,1.05,1.05,lingtong,lingtong_a,lingtong_p,0,
|
lingtong,능통,凌統,wu,spear,2,1.05,1.05,1.05,lingtong,lingtong_a,lingtong_p,0,,m
|
||||||
zhoutai,주태,周泰,wu,infantry,2,1.05,1.05,1.05,zhoutai,zhoutai_a,zhoutai_p,0,
|
zhoutai,주태,周泰,wu,infantry,2,1.05,1.05,1.05,zhoutai,zhoutai_a,zhoutai_p,0,,m
|
||||||
zhugejin,제갈근,諸葛瑾,wu,strategist,2,1.05,1.05,1.05,zhugejin,zhugejin_a,zhugejin_p,0,
|
zhugejin,제갈근,諸葛瑾,wu,strategist,2,1.05,1.05,1.05,zhugejin,zhugejin_a,zhugejin_p,0,,m
|
||||||
dongxi,동습,董襲,wu,spear,1,1.0,1.0,1.0,dongxi,dongxi_a,dongxi_p,0,
|
dongxi,동습,董襲,wu,spear,1,1.0,1.0,1.0,dongxi,dongxi_a,dongxi_p,0,,m
|
||||||
lvbu,여포,呂布,other,cavalry,3,1.15,1.15,1.1,dz_boss_lvbu,lvbu_a,lvbu_p,0,2-10
|
lvbu,여포,呂布,other,cavalry,3,1.15,1.15,1.1,dz_boss_lvbu,lvbu_a,lvbu_p,0,2-10,m
|
||||||
zhangjiao,장각,張角,other,infantry,2,1.05,1.05,1.05,hgj_boss_zhangjiao,zhangjiao_a,zhangjiao_p,0,1-10
|
zhangjiao,장각,張角,other,infantry,2,1.05,1.05,1.05,hgj_boss_zhangjiao,zhangjiao_a,zhangjiao_p,0,1-10,m
|
||||||
huaxiong,화웅,華雄,other,infantry,2,1.05,1.05,1.05,dz_boss_huaxiong,huaxiong_a,huaxiong_p,0,2-5
|
huaxiong,화웅,華雄,other,infantry,2,1.05,1.05,1.05,dz_boss_huaxiong,huaxiong_a,huaxiong_p,0,2-5,m
|
||||||
yanliang,안량,顏良,other,spear,2,1.05,1.05,1.05,ys_boss_yanliang,yanliang_a,yanliang_p,0,6-5
|
yanliang,안량,顏良,other,spear,2,1.05,1.05,1.05,ys_boss_yanliang,yanliang_a,yanliang_p,0,6-5,m
|
||||||
wenchou,문추,文醜,other,cavalry,2,1.05,1.05,1.05,ys_boss_wenchou,wenchou_a,wenchou_p,0,3-3
|
wenchou,문추,文醜,other,cavalry,2,1.05,1.05,1.05,ys_boss_wenchou,wenchou_a,wenchou_p,0,3-3,m
|
||||||
gongsunzan,공손찬,公孫瓚,other,cavalry,2,1.05,1.05,1.05,ys_gongsunzan,gongsunzan_a,gongsunzan_p,0,2-1
|
gongsunzan,공손찬,公孫瓚,other,cavalry,2,1.05,1.05,1.05,ys_gongsunzan,gongsunzan_a,gongsunzan_p,0,2-1,m
|
||||||
dongzhuo,동탁,董卓,other,infantry,3,1.15,1.15,1.1,dongzhuo,dongzhuo_a,dongzhuo_p,0,
|
dongzhuo,동탁,董卓,other,infantry,3,1.15,1.15,1.1,dongzhuo,dongzhuo_a,dongzhuo_p,0,,m
|
||||||
lijue,이각,李傕,other,cavalry,2,1.05,1.05,1.05,lijue,lijue_a,lijue_p,0,
|
lijue,이각,李傕,other,cavalry,2,1.05,1.05,1.05,lijue,lijue_a,lijue_p,0,,m
|
||||||
yuanshao,원소,袁紹,other,archer,3,1.15,1.15,1.1,yuanshao,yuanshao_a,yuanshao_p,0,
|
yuanshao,원소,袁紹,other,archer,3,1.15,1.15,1.1,yuanshao,yuanshao_a,yuanshao_p,0,,m
|
||||||
yuanshu,원술,袁術,other,infantry,2,1.05,1.05,1.05,yuanshu,yuanshu_a,yuanshu_p,0,
|
yuanshu,원술,袁術,other,infantry,2,1.05,1.05,1.05,yuanshu,yuanshu_a,yuanshu_p,0,,m
|
||||||
diaochan,초선,貂蟬,other,strategist,3,1.15,1.15,1.1,diaochan,diaochan_a,diaochan_p,0,
|
diaochan,초선,貂蟬,other,strategist,3,1.15,1.15,1.1,diaochan,diaochan_a,diaochan_p,0,,f
|
||||||
zhurong,축융,祝融,other,spear,3,1.15,1.15,1.1,zhurong,zhurong_a,zhurong_p,0,
|
zhurong,축융,祝融,other,spear,3,1.15,1.15,1.1,zhurong,zhurong_a,zhurong_p,0,,f
|
||||||
menghuo,맹획,孟獲,other,infantry,3,1.15,1.15,1.1,menghuo,menghuo_a,menghuo_p,0,
|
menghuo,맹획,孟獲,other,infantry,3,1.15,1.15,1.1,menghuo,menghuo_a,menghuo_p,0,,m
|
||||||
gaoshun,고순,高順,other,infantry,2,1.05,1.05,1.05,dz_boss_gaoshun,gaoshun_a,gaoshun_p,0,4-10
|
gaoshun,고순,高順,other,infantry,2,1.05,1.05,1.05,dz_boss_gaoshun,gaoshun_a,gaoshun_p,0,4-10,m
|
||||||
zhanglu,장로,張魯,other,strategist,2,1.05,1.05,1.05,zhanglu,zhanglu_a,zhanglu_p,0,
|
zhanglu,장로,張魯,other,strategist,2,1.05,1.05,1.05,zhanglu,zhanglu_a,zhanglu_p,0,,m
|
||||||
yanbaihu,엄백호,嚴白虎,other,infantry,1,1.0,1.0,1.0,yanbaihu,yanbaihu_a,yanbaihu_p,0,
|
yanbaihu,엄백호,嚴白虎,other,infantry,1,1.0,1.0,1.0,yanbaihu,yanbaihu_a,yanbaihu_p,0,,m
|
||||||
hanxuan,한현,韓玄,other,spear,1,1.0,1.0,1.0,hanxuan,hanxuan_a,hanxuan_p,0,
|
hanxuan,한현,韓玄,other,spear,1,1.0,1.0,1.0,hanxuan,hanxuan_a,hanxuan_p,0,,m
|
||||||
jinxuan,금선,金旋,other,archer,1,1.0,1.0,1.0,jinxuan,jinxuan_a,jinxuan_p,0,
|
jinxuan,금선,金旋,other,archer,1,1.0,1.0,1.0,jinxuan,jinxuan_a,jinxuan_p,0,,m
|
||||||
zhaofan,조범,趙範,other,strategist,1,1.0,1.0,1.0,zhaofan,zhaofan_a,zhaofan_p,0,
|
zhaofan,조범,趙範,other,strategist,1,1.0,1.0,1.0,zhaofan,zhaofan_a,zhaofan_p,0,,m
|
||||||
zhangren,장임,張任,other,archer,1,1.0,1.0,1.0,zhangren,zhangren_a,zhangren_p,0,
|
zhangren,장임,張任,other,archer,1,1.0,1.0,1.0,zhangren,zhangren_a,zhangren_p,0,,m
|
||||||
chunyuqiong,순우경,淳于瓊,other,cavalry,1,1.0,1.0,1.0,chunyuqiong,chunyuqiong_a,chunyuqiong_p,0,
|
chunyuqiong,순우경,淳于瓊,other,cavalry,1,1.0,1.0,1.0,chunyuqiong,chunyuqiong_a,chunyuqiong_p,0,,m
|
||||||
soldier_infantry_1,정예 보병,步兵,shu,infantry,1,0.85,0.85,0.85,shu_infantry,,,1,
|
soldier_infantry_1,정예 보병,步兵,shu,infantry,1,0.85,0.85,0.85,shu_infantry,,,1,,m
|
||||||
soldier_infantry_2,영웅 보병,步兵,shu,infantry,2,0.95,0.95,0.95,shu_infantry,,,1,
|
soldier_infantry_2,영웅 보병,步兵,shu,infantry,2,0.95,0.95,0.95,shu_infantry,,,1,,m
|
||||||
soldier_infantry_3,전설 보병,步兵,shu,infantry,3,1.05,1.05,1.05,shu_infantry,,,1,
|
soldier_infantry_3,전설 보병,步兵,shu,infantry,3,1.05,1.05,1.05,shu_infantry,,,1,,m
|
||||||
soldier_spear_1,정예 창병,槍兵,shu,spear,1,0.85,0.85,0.85,shu_spear,,,1,
|
soldier_spear_1,정예 창병,槍兵,shu,spear,1,0.85,0.85,0.85,shu_spear,,,1,,m
|
||||||
soldier_spear_2,영웅 창병,槍兵,shu,spear,2,0.95,0.95,0.95,shu_spear,,,1,
|
soldier_spear_2,영웅 창병,槍兵,shu,spear,2,0.95,0.95,0.95,shu_spear,,,1,,m
|
||||||
soldier_spear_3,전설 창병,槍兵,shu,spear,3,1.05,1.05,1.05,shu_spear,,,1,
|
soldier_spear_3,전설 창병,槍兵,shu,spear,3,1.05,1.05,1.05,shu_spear,,,1,,m
|
||||||
soldier_cavalry_1,정예 기병,騎兵,shu,cavalry,1,0.85,0.85,0.85,shu_cavalry,,,1,
|
soldier_cavalry_1,정예 기병,騎兵,shu,cavalry,1,0.85,0.85,0.85,shu_cavalry,,,1,,m
|
||||||
soldier_cavalry_2,영웅 기병,騎兵,shu,cavalry,2,0.95,0.95,0.95,shu_cavalry,,,1,
|
soldier_cavalry_2,영웅 기병,騎兵,shu,cavalry,2,0.95,0.95,0.95,shu_cavalry,,,1,,m
|
||||||
soldier_cavalry_3,전설 기병,騎兵,shu,cavalry,3,1.05,1.05,1.05,shu_cavalry,,,1,
|
soldier_cavalry_3,전설 기병,騎兵,shu,cavalry,3,1.05,1.05,1.05,shu_cavalry,,,1,,m
|
||||||
soldier_archer_1,정예 궁병,弓兵,shu,archer,1,0.85,0.85,0.85,shu_archer,,,1,
|
soldier_archer_1,정예 궁병,弓兵,shu,archer,1,0.85,0.85,0.85,shu_archer,,,1,,m
|
||||||
soldier_archer_2,영웅 궁병,弓兵,shu,archer,2,0.95,0.95,0.95,shu_archer,,,1,
|
soldier_archer_2,영웅 궁병,弓兵,shu,archer,2,0.95,0.95,0.95,shu_archer,,,1,,m
|
||||||
soldier_archer_3,전설 궁병,弓兵,shu,archer,3,1.05,1.05,1.05,shu_archer,,,1,
|
soldier_archer_3,전설 궁병,弓兵,shu,archer,3,1.05,1.05,1.05,shu_archer,,,1,,m
|
||||||
soldier_strategist_1,정예 책사,策士,shu,strategist,1,0.85,0.85,0.85,shu_strategist,,,1,
|
soldier_strategist_1,정예 책사,策士,shu,strategist,1,0.85,0.85,0.85,shu_strategist,,,1,,m
|
||||||
soldier_strategist_2,영웅 책사,策士,shu,strategist,2,0.95,0.95,0.95,shu_strategist,,,1,
|
soldier_strategist_2,영웅 책사,策士,shu,strategist,2,0.95,0.95,0.95,shu_strategist,,,1,,m
|
||||||
soldier_strategist_3,전설 책사,策士,shu,strategist,3,1.05,1.05,1.05,shu_strategist,,,1,
|
soldier_strategist_3,전설 책사,策士,shu,strategist,3,1.05,1.05,1.05,shu_strategist,,,1,,m
|
||||||
|
|
|
||||||
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
skill_id,key,target,cond,dur,p1,p2,p3,note,effect_ko,p1_ko,p2_ko,p3_ko
|
||||||
|
liubei_a,extra_action,,,0,1,,,행동 뒤 재행동 1회,행동 뒤 한 번 더 행동(재행동),재행동 횟수,,
|
||||||
|
liubei_a,counter_taken_pct,,,1,-1,,,1턴 반격 안 받음,받는 반격 피해 증감 · 1턴 지속,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
liubei_p,atk_pct,camp:shu,,0,0.2,,,,공격력 증가 · 대상: 촉 진영 유닛,공격력 +비율(0.2 = +20 %),,
|
||||||
|
liubei_p,revive,,,0,1.0,1,0,HP 비율·횟수(전투당)·조건 HP 비율,쓰러지면 부활,부활 HP 비율(1.0 = 100 %),전투당 횟수,맞기 전 HP 가 이 비율 이상일 때만(0 = 조건 없음)
|
||||||
|
guanyu_a,aoe_strike,,,0,3,2.5,1,반경·공격 배율·밀기,자신 주위 적 전원 공격·밀침,반경,피해 = 공격력 × 배수(0 = 피해 없음),바깥으로 밀기 거리
|
||||||
|
guanyu_a,hit_bleed,,,0,2,0.5,1,턴(기본값)·이동 비례·확률,명중한 적 출혈(움직인 거리에 비례해 생명력 감소),출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
guanyu_p,counter_dmg_pct,camp:shu,,0,0.5,,,,주는 반격 피해 증가 · 대상: 촉 진영 유닛,반격 피해 +비율(0.5 = +50 % · 1.0 = 2배),,
|
||||||
|
guanyu_p,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
guanyu_p,kill_stack_atk,,,0,0.1,1.0,,처치당·최대,적 처치마다 공격력 누적(전투 동안),처치 1기당 공격 +비율,최대 누적 비율,
|
||||||
|
zhangfei_a,aoe_strike,,,0,4,0,1,반경·피해 없음·밀기,자신 주위 적 전원 공격·밀침,반경,피해 = 공격력 × 배수(0 = 피해 없음),바깥으로 밀기 거리
|
||||||
|
zhangfei_a,hit_stun,,,0,1,,,,명중한 적 기절(다음 자기 턴 행동 불가),기절 턴,,
|
||||||
|
zhangfei_p,sling_dist_pct,camp:shu,,0,0.3,,,,이동(슬링) 거리 증가 · 대상: 촉 진영 유닛,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
zhangfei_p,dash_dmg_pct,,,0,1.0,,,슬링 거리 비례 최대,돌진(슬링) 거리에 비례해 공격력 증가,슬링 최대 거리로 당겼을 때 공격 +비율(최대),,
|
||||||
|
zhangfei_p,double_hit_stun,,,0,2,1,,연속 피해 수·기절 턴,같은 적에게 연속 행동으로 피해를 주면 기절,연속 피해 횟수,기절 턴,
|
||||||
|
zhangfei_p,lifesteal,,vs_stunned,0,0.5,,,,입힌 피해만큼 회복(흡혈) · 조건: 상대가 기절일 때,입힌 피해 × 비율 회복,,
|
||||||
|
zhaoyun_a,sling_dist_pct,,,0,1.0,,,,이동(슬링) 거리 증가,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
zhaoyun_a,dmg_pct,,,0,0.5,,,,주는 피해 증가,피해 +비율(0.5 = +50 %),,
|
||||||
|
zhaoyun_p,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
zhaoyun_p,return_on_hit,,,0,1,0,,복귀 중 피해·쓸어 감,피해를 입히면 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,1 = 복귀 경로의 유닛을 함께 쓸어 감,
|
||||||
|
huangzhong_a,shot_giant,,,0,0.5,5.0,,화살 반지름(기본값)·공격 배율 · 사거리 무관,사거리 무관 거대 화살 — 첫 적에게 큰 피해,화살 반지름,피해 = 공격력 × 배수,
|
||||||
|
huangzhong_a,hit_stun,,,0,1,,,,명중한 적 기절(다음 자기 턴 행동 불가),기절 턴,,
|
||||||
|
huangzhong_p,arrow_pass_allies,,,0,1,1,,p2 1 = 장애물도 통과,화살이 아군을 통과,1 = 켬,1 = 장애물도 통과,
|
||||||
|
huangzhong_p,crit_chance,,,0,0.2,,,,치명타 확률 증가,확률 +(0.2 = +20 %p),,
|
||||||
|
huangzhong_p,crit_dmg_pct,,,0,0.5,,,,치명타 피해량 증가,치명타 피해 +비율(0.5 = +50 %),,
|
||||||
|
zhugeliang_a,heal_atk,range,,0,1.0,,,,회복 · 대상: 시전 순간 행동 반경 안 아군,회복 = 공격력 × 배수,,
|
||||||
|
zhugeliang_a,cleanse,range,,0,1,,,,상태 이상 제거(혼란·출혈·중독·화상·기절) · 대상: 시전 순간 행동 반경 안 아군,1 = 켬,,
|
||||||
|
zhugeliang_p,counter_taken_pct,camp:shu,,0,-0.5,,,,받는 반격 피해 증감 · 대상: 촉 진영 유닛,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
zhugeliang_p,turn_end_heal_lowest,,,0,3,0.5,,반경·공격 배율,턴 종료 시 주위에서 생명력이 가장 낮은 아군 회복,반경,회복 = 공격력 × 배수,
|
||||||
|
zhoucang_a,atk_ratio,,,0,1.8,,,,이 공격의 피해 = 공격력의 배수,공격력 배수(1.8 = 180 %),,
|
||||||
|
zhoucang_a,hit_bleed,,,0,2,0.5,1,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소),출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
zhoucang_a,push_mult,,,0,2,,,,밀치는 거리 배율,밀기 ×배율(2 = 2배),,
|
||||||
|
zhoucang_p,atk_pct,,vs_bleed,0,0.5,,,,공격력 증가 · 조건: 상대가 출혈일 때,공격력 +비율(0.2 = +20 %),,
|
||||||
|
guanping_a,guard_radius_mult,,,2,2,,,,호위·반격 반경 배율 · 2턴 지속,반경 ×배율,,
|
||||||
|
guanping_a,guard_dr,,,2,-0.5,,,,호위로 대신 받는 피해 감소 · 2턴 지속,받는 비율(−0.5 = 반감),,
|
||||||
|
guanping_p,counter_dmg_pct,,,0,0.5,,,,주는 반격 피해 증가,반격 피해 +비율(0.5 = +50 % · 1.0 = 2배),,
|
||||||
|
guanping_p,guard,,,0,2,1.0,,반경·대신 받는 비율,호위 — 반경 안 아군 피해를 대신 받음,호위 반경,대신 받는 비율(1.0 = 전부),
|
||||||
|
xushu_a,shot_blast,,,0,1.5,2.5,,폭발 반경(기본값 = 책사 광역)·공격 배율,투사체가 첫 적 자리에서 폭발 — 반경 안 적 전원 피해,폭발 반경,피해 = 공격력 × 배수,
|
||||||
|
xushu_a,hit_burn,,,0,3,0.05,1,,명중한 적 화상(불태우기 · 턴 시작마다 생명력 감소),화상 턴,1회 최대 HP × 비율,확률
|
||||||
|
xushu_p,range_mult,,,0,1.5,,,,공격 사거리 배율,사거리 ×배율(1.5 = +50 %),,
|
||||||
|
xushu_p,splash_ally_ratio,,,0,0,,,,자신의 광역이 아군에게 주는 피해 비율,아군 피해 비율(0 = 아군 피해 없음),,
|
||||||
|
taishici_a,shot_pierce,,,0,3.0,0,0,공격 배율·아군(0 통과)·밀기 · 장애물 무시,직선 관통 사격 — 경로 위 유닛 전원(장애물 무시),피해 = 공격력 × 배수,아군 처리(0 통과 · 1 피해 · 2 회복),밀기 거리(0 = 없음)
|
||||||
|
taishici_p,melee_penalty_off,,,0,1,,,,궁병·책사 근접 전투 패널티 없음,1 = 켬,,
|
||||||
|
taishici_p,atk_pct,,,0,0.5,,,,공격력 증가,공격력 +비율(0.2 = +20 %),,
|
||||||
|
taishici_p,crit_dmg_pct,,,0,0.5,,,,치명타 피해량 증가,치명타 피해 +비율(0.5 = +50 %),,
|
||||||
|
machao_a,atk_pct,,,0,0.5,,,,공격력 증가,공격력 +비율(0.2 = +20 %),,
|
||||||
|
machao_a,crit_always,,,0,1,,,,무조건 치명타,1 = 켬,,
|
||||||
|
machao_p,lifesteal,,,0,0.2,,,,입힌 피해만큼 회복(흡혈),입힌 피해 × 비율 회복,,
|
||||||
|
machao_p,dmg_per_hit,,,0,0.25,,,이번 행동 앞서 맞힌 적 1기당,한 행동에서 앞서 맞힌 적 1기당 피해 증가,맞힌 적 1기당 피해 +비율,,
|
||||||
|
weiyan_a,swap_on_hit,,,0,1,,,명중 대상 ↔ 출발 위치,명중한 대상을 자신의 출발 위치로 보내고 자신은 그 자리로(위치 교환),1 = 켬,,
|
||||||
|
weiyan_a,extra_action,,,0,1,,,,행동 뒤 한 번 더 행동(재행동),재행동 횟수,,
|
||||||
|
weiyan_p,matchup_floor,,,0,1.0,,,불리 상성 무시,상성 무시(불리한 상성만 무시 · 유리 상성은 유지),상성 배율 하한(1.0 = 불리 상성 없음),,
|
||||||
|
weiyan_p,atk_missing_hp,,,0,1.0,1.0,,잃은 HP 비율 × p1 · 최대,잃은 생명력 비율만큼 공격력 증가,잃은 HP 비율 × 계수(1.0 = HP 1 % 감소당 공격 1 %),최대 증가 비율(빈칸 = 제한 없음),
|
||||||
|
pangtong_a,heal_atk,range,,0,0.5,,,,회복 · 대상: 시전 순간 행동 반경 안 아군,회복 = 공격력 × 배수,,
|
||||||
|
pangtong_a,hot,range,,3,0.1,,,매 턴 시작 공격 배율,지속 회복 — 매 자기 턴 시작마다 · 대상: 시전 순간 행동 반경 안 아군 · 3턴 지속,회복 = 시전자 공격력 × 배수,,
|
||||||
|
pangtong_p,dmg_taken_ranged_pct,camp:shu,,0,-0.5,,,,받는 원거리(사격·광역) 피해 증감 · 대상: 촉 진영 유닛,받는 원거리 피해 비율(−0.5 = 반감),,
|
||||||
|
pangtong_p,turn_end_cleanse,,,0,3,1,,반경·인원,턴 종료 시 주위 아군 상태 이상 제거,반경,인원,
|
||||||
|
jiangwei_a,heal_self_pct,,,0,0.5,,,,자신 생명력 회복,최대 HP × 비율,,
|
||||||
|
jiangwei_a,guard_radius_mult,,,3,1.5,,,,호위·반격 반경 배율 · 3턴 지속,반경 ×배율,,
|
||||||
|
jiangwei_p,counter_dmg_pct,,,0,0.5,,,,주는 반격 피해 증가,반격 피해 +비율(0.5 = +50 % · 1.0 = 2배),,
|
||||||
|
jiangwei_p,guard_counter,,,0,2,1.0,,반격 범위(기본값 = 관평 호위 2)·대신 받는 비율,호위 반격 — 반경 안 아군 피해를 대신 받고 공격자에게 반격,반격(호위) 반경,대신 받는 비율,
|
||||||
|
liufeng_a,atk_ratio,,,0,1.85,,,,이 공격의 피해 = 공격력의 배수,공격력 배수(1.8 = 180 %),,
|
||||||
|
liufeng_a,hit_confuse,,,0,1,0.5,1,턴·이동 배율·확률,명중한 적 혼란(이동 거리 감소),혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
liufeng_p,counter_taken_pct,,vs_confused,0,-1,,,,받는 반격 피해 증감 · 조건: 상대가 혼란일 때,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
liufeng_p,hit_stun,,vs_confused,0,1,,,,명중한 적 기절(다음 자기 턴 행동 불가) · 조건: 상대가 혼란일 때,기절 턴,,
|
||||||
|
jianyong_a,ally_hit_heal,,,0,0.8,,,,자신의 공격이 아군에 맞으면 피해 대신 회복,회복 = 공격력 × 배수,,
|
||||||
|
jianyong_a,hit_stun,,,0,1,,,,명중한 적 기절(다음 자기 턴 행동 불가),기절 턴,,
|
||||||
|
jianyong_p,heal_cleanse,,,0,1,,,,자신이 회복시킨 대상의 상태 이상 제거,1 = 켬,,
|
||||||
|
jianyong_p,on_attacked_stun,,,0,1,3,,기절 턴·재충전 턴,자신을 공격한 대상 기절(재충전),기절 턴,재충전 턴,
|
||||||
|
sunqian_a,splash_heal,,,0,0.6,,,명중 대상 주위 광역 = 회복(적 포함),책사 광역이 피해 대신 회복(적 포함),회복 = 공격력 × 배수,,
|
||||||
|
sunqian_p,sling_dist_pct,radius:3,,0,0.3,,,,이동(슬링) 거리 증가 · 대상: 자신 주위 3 안 아군,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
mizhu_a,sling_dist_pct,range,,1,0.5,,,,이동(슬링) 거리 증가 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
mizhu_a,counter_taken_pct,range,,1,-0.5,,,,받는 반격 피해 증감 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
mizhu_p,ally_hit_buff,,,1,0.2,0.2,,이동 %·공격 %,자신의 공격이 아군에 맞으면 버프(피해 없음) · 1턴 지속,이동 거리 +비율,공격력 +비율,
|
||||||
|
caocao_a,pass_allies,,,0,1,,,,아군을 통과해 돌진,1 = 켬,,
|
||||||
|
caocao_a,aim_push,,,0,1,,,명중 적을 탭한 방향으로 밀침,명중한 적을 탭한 방향으로 밀침(자신은 밀리지 않음),1 = 켬,,
|
||||||
|
caocao_p,atk_pct,camp:wei,,0,0.2,,,,공격력 증가 · 대상: 위 진영 유닛,공격력 +비율(0.2 = +20 %),,
|
||||||
|
caocao_p,matchup_floor,,,0,1.0,,,,상성 무시(불리한 상성만 무시 · 유리 상성은 유지),상성 배율 하한(1.0 = 불리 상성 없음),,
|
||||||
|
caocao_p,on_ally_death_atk,allies,,0,0.1,,,,아군이 쓰러질 때마다 공격력 증가 · 대상: 같은 편 전원,아군 1기 전사당 공격 +비율,,
|
||||||
|
xiahoudun_a,sling_dist_pct,,,0,0.5,,,,이동(슬링) 거리 증가,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
xiahoudun_a,return_aim,,,0,1,,,탭한 곳으로 복귀,탭한 지점으로 복귀(거리 무관),1 = 켬,,
|
||||||
|
xiahoudun_p,dmg_pct,camp:wei:cavalry,,0,0.5,,,,주는 피해 증가 · 대상: 위 진영 기병 유닛,피해 +비율(0.5 = +50 %),,
|
||||||
|
xiahoudun_p,return_on_hit,,,0,0,1,,복귀 피해 없음·경로 유닛 쓸어 감,피해를 입히면 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,1 = 복귀 경로의 유닛을 함께 쓸어 감,
|
||||||
|
xiahoudun_p,matchup_floor,,,0,1.0,,,,상성 무시(불리한 상성만 무시 · 유리 상성은 유지),상성 배율 하한(1.0 = 불리 상성 없음),,
|
||||||
|
xuchu_a,self_bounce_mult,,,0,2,,,,부딪힌 뒤 자신이 튕기는 거리 배율,튕김 ×배율(0 = 튕기지 않음 · 2 = 2배),,
|
||||||
|
xuchu_a,hit_stun,,,0,1,,,,명중한 적 기절(다음 자기 턴 행동 불가),기절 턴,,
|
||||||
|
xuchu_p,hp_max_pct,,,0,0.5,,,,최대 생명력 증가,최대 HP +비율,,
|
||||||
|
xuchu_p,knockback_taken_pct,,,0,-1,,,,밀리는 거리 증감,밀림 비율(−1 = 밀리지 않음),,
|
||||||
|
xuchu_p,regen_pct,,,0,0.05,,,,자기 턴 시작마다 생명력 회복,최대 HP × 비율 회복,,
|
||||||
|
zhangliao_a,drag_back,,,0,1,,,명중 적을 출발 위치로 끌고 옴,관통으로 맞힌 적을 자신의 출발 위치로 끌고 옴,1 = 켬,,
|
||||||
|
zhangliao_a,hit_confuse,,,0,1,0.5,1,,명중한 적 혼란(이동 거리 감소),혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
zhangliao_p,atk_pct,,vs_status,0,0.5,,,,공격력 증가 · 조건: 상대가 상태 이상일 때,공격력 +비율(0.2 = +20 %),,
|
||||||
|
zhangliao_p,counter_taken_pct,,vs_status,0,-1,,,,받는 반격 피해 증감 · 조건: 상대가 상태 이상일 때,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
xuhuang_a,axe_aura,,,3,1.2,0.8,2,도끼 반경(기본값)·공격 배율·튕김 배율,주위를 도는 도끼 — 자신을 친 근접 유닛에게 반격 피해 + 그 유닛이 더 멀리 튕김 · 3턴 지속,도끼 반경,반격 피해 = 공격력 × 배수,공격자 튕김 거리 배율
|
||||||
|
xuhuang_p,hit_bleed,,,0,3,0.5,0.3,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소),출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
xuhuang_p,hit_bleed,,crit,0,3,0.5,1,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소) · 조건: 치명타일 때,출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
zhanghe_a,teleport,,,0,5,,,,반경 안 원하는 곳으로 이동(대상 무시 · 지점 탭),이동 반경,,
|
||||||
|
zhanghe_a,extra_action,,,0,1,,,,행동 뒤 한 번 더 행동(재행동),재행동 횟수,,
|
||||||
|
zhanghe_p,push_mult,,,0,2,,,,밀치는 거리 배율,밀기 ×배율(2 = 2배),,
|
||||||
|
zhanghe_p,pushed_ally_atk_add,,,0,1.0,,,밀린 아군 공격 + 자신 공격력 × p1,밀린 아군이 적을 치면 자신의 공격력만큼 더함,자신 공격력 × 배수 가산,,
|
||||||
|
zhanghe_p,self_pushed_mult,,,0,2,,,자신이 밀려 공격 = 피해 × p1,자신이 밀려 적을 친 공격 피해 배율,피해 ×배율,,
|
||||||
|
caoren_a,dmg_taken_pct,aura,,3,-0.3,,,,받는 피해 증감 · 대상: 자신의 행동 반경 안 아군 · 3턴 지속,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
caoren_a,zone_slow,,,3,0.5,,,반경 안 적 이동 비용 +,자신의 행동 반경을 지나는 적의 이동 비용 증가 · 3턴 지속,이동 비용 +비율(0.5 = 1.5배),,
|
||||||
|
caoren_p,hp_max_pct,camp:wei,,0,0.2,,,,최대 생명력 증가 · 대상: 위 진영 유닛,최대 HP +비율,,
|
||||||
|
caoren_p,heal_on_action_end,,,0,0.2,,,,행동을 멈출 때마다 생명력 회복,최대 HP × 비율 회복,,
|
||||||
|
caoren_p,unused_move_dr,,,0,0.5,,,,남은(쓰지 않은) 이동 거리만큼 받는 피해 감소,최대 감소 비율(이동 안 함 = 최대),,
|
||||||
|
yujin_a,ally_hit_reaction,,,0,1,,,,이 행동으로 맞힌(밀친) 행동 끝난 아군 재행동,1 = 켬,,
|
||||||
|
yujin_p,move_bank,,,0,2,,,최대 축적 턴,이동하지 않은 턴의 이동력 축적,최대 축적 배수(2 = 이동 거리 최대 2배),,
|
||||||
|
simayi_a,aoe_drain,,,0,1.5,0.6,,공격 배율·피해 → 아군 회복 비율,행동 반경 안 적 전원 피해 → 준 피해만큼 아군 회복,피해 = 공격력 × 배수,준 피해 합 × 비율을 범위 안 다른 아군 각각 회복,
|
||||||
|
simayi_p,splash_radius_mult,camp:wei:strategist,,0,1.5,,,,책사 광역 범위 배율 · 대상: 위 진영 책사 유닛,광역 반경 ×배율,,
|
||||||
|
simayi_p,range_mult,,,0,1.5,,,,공격 사거리 배율,사거리 ×배율(1.5 = +50 %),,
|
||||||
|
simayi_p,drain_aura,,,0,0.3,,,,행동 반경 안 적이 입은 피해만큼 자신 회복,그 피해 × 비율 회복,,
|
||||||
|
xiahouyuan_a,shot_fan,,,0,5,3,60,발 수·연속 횟수·부채꼴 각(기본값),부채꼴 연속 사격,한 번에 쏘는 화살 수,연속 횟수,부채꼴 전체 각도(도)
|
||||||
|
xiahouyuan_p,multishot,,,0,3,30,,발 수·부채꼴 각(기본값),일반 사격이 부채꼴 여러 발(멀티샷),화살 수,부채꼴 전체 각도(도),
|
||||||
|
guojia_a,dmg_pct,range,,2,0.3,,,,주는 피해 증가 · 대상: 시전 순간 행동 반경 안 아군 · 2턴 지속,피해 +비율(0.5 = +50 %),,
|
||||||
|
guojia_a,grant_reaction,,,0,1,,,,이미 행동한 다른 아군에게 재행동 부여(없으면 다른 아군이 행동 뒤 재행동),인원,,
|
||||||
|
guojia_p,kill_in_range_reward,,,0,1,0.3,,쿨 −턴·최대 HP 회복,자신의 행동 반경 안에서 적을 처치한 아군 보상,액티브 쿨 −턴,최대 HP × 비율 회복,
|
||||||
|
jiaxu_a,shot_blast,,,0,1.5,2.2,,독구름 반경(기본값)·공격 배율,투사체가 첫 적 자리에서 폭발 — 반경 안 적 전원 피해,폭발 반경,피해 = 공격력 × 배수,
|
||||||
|
jiaxu_a,hit_poison,,,0,5,0.02,1,,명중한 적 중독(턴 시작·행동 끝마다 생명력 감소),중독 턴,1회 최대 HP × 비율(중첩 수만큼 곱),확률
|
||||||
|
jiaxu_p,hit_poison,,,0,5,0.02,1,턴(기본값 = 독계),명중한 적 중독(턴 시작·행동 끝마다 생명력 감소),중독 턴,1회 최대 HP × 비율(중첩 수만큼 곱),확률
|
||||||
|
jiaxu_p,poison_stack,,,0,5,,,,중독 중첩 허용,최대 중첩,,
|
||||||
|
dianwei_a,dmg_pct,,,0,1.0,,,,주는 피해 증가,피해 +비율(0.5 = +50 %),,
|
||||||
|
dianwei_a,push_mult,,,0,2,,,,밀치는 거리 배율,밀기 ×배율(2 = 2배),,
|
||||||
|
dianwei_a,self_bounce_mult,,,0,0,,,,부딪힌 뒤 자신이 튕기는 거리 배율,튕김 ×배율(0 = 튕기지 않음 · 2 = 2배),,
|
||||||
|
dianwei_p,dmg_taken_pct,,,0,-0.2,,,,받는 피해 증감,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
dianwei_p,gimmick_mult,,,0,2,,,지형 오브젝트 밀기 거리·피해 배율,지형 오브젝트(바위·목책 등) 밀기 거리·그것으로 준 피해 배율,×배율,,
|
||||||
|
xunyu_a,counter_taken_pct,range,,1,-1,,,,받는 반격 피해 증감 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
xunyu_a,atk_pct,range,,1,0.3,,,,공격력 증가 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,공격력 +비율(0.2 = +20 %),,
|
||||||
|
xunyu_p,ally_hit_heal,,,0,0.5,,,,자신의 공격이 아군에 맞으면 피해 대신 회복,회복 = 공격력 × 배수,,
|
||||||
|
xunyu_p,ally_hit_buff,,,1,0,0.15,,,자신의 공격이 아군에 맞으면 버프(피해 없음) · 1턴 지속,이동 거리 +비율,공격력 +비율,
|
||||||
|
xiahoumao_a,dmg_taken_pct,,,1,-0.3,,,,받는 피해 증감 · 1턴 지속,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
xiahoumao_p,sling_dist_pct,,,0,0.1,,,,이동(슬링) 거리 증가,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
wanglang_a,shot_pierce,,,0,1.8,1,0,아군 1 = 아군도 피해,직선 관통 사격 — 경로 위 유닛 전원(장애물 무시),피해 = 공격력 × 배수,아군 처리(0 통과 · 1 피해 · 2 회복),밀기 거리(0 = 없음)
|
||||||
|
wanglang_a,hit_confuse,,,0,1,0.5,1,,명중한 적 혼란(이동 거리 감소),혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
wanglang_p,range_mult,,,0,1.1,,,,공격 사거리 배율,사거리 ×배율(1.5 = +50 %),,
|
||||||
|
xiahouen_a,first_hit_bonus,,,0,0.5,,,,이 행동에서 처음 맞힌 대상에게 추가 피해,추가 피해 = 공격력 × 비율,,
|
||||||
|
xiahouen_p,hp_max_pct,,,0,0.1,,,,최대 생명력 증가,최대 HP +비율,,
|
||||||
|
sunjian_a,atk_pct,,,0,0.5,,,,공격력 증가,공격력 +비율(0.2 = +20 %),,
|
||||||
|
sunjian_a,pierce_through,,,0,1,,,1 = 아군도 통과,보병·창병도 기병처럼 적을 관통하며 경로 위 적 전원 피해,1 = 아군도 통과 · 0 = 아군 앞 정지,,
|
||||||
|
sunjian_a,hit_bleed,,,0,2,0.5,1,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소),출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
sunjian_a,invuln,,,1,1,,,행동 끝 뒤 1턴 피해 무효,무적 — 모든 피해 무효(행동 중이 아닌 동안) · 1턴 지속,1 = 켬,,
|
||||||
|
sunjian_p,atk_pct,camp:wu,,0,0.2,,,,공격력 증가 · 대상: 오 진영 유닛,공격력 +비율(0.2 = +20 %),,
|
||||||
|
sunjian_p,kill_reaction,,,0,1,,,턴당,적 처치 시 재행동(턴당 1회),1 = 켬,,
|
||||||
|
zhouyu_a,shot_blast,,,0,2.5,3.5,,운석 반경(기본값)·공격 배율,투사체가 첫 적 자리에서 폭발 — 반경 안 적 전원 피해,폭발 반경,피해 = 공격력 × 배수,
|
||||||
|
zhouyu_a,hit_burn,,,0,3,0.05,1,,명중한 적 화상(불태우기 · 턴 시작마다 생명력 감소),화상 턴,1회 최대 HP × 비율,확률
|
||||||
|
zhouyu_a,fire_zone,,,0,1.5,3,1.0,불꽃 반경·턴·닿을 때 공격 배율,폭발 자리에 불꽃 지대 — 닿는 적 피해 + 화상,지대 반경,유지 턴,닿을 때 피해 = 공격력 × 배수
|
||||||
|
zhouyu_p,skill_hp_cost,,,0,0.05,,,,액티브 스킬 시전 시 생명력 소모,최대 HP × 비율 소모(쓰러지지는 않음),,
|
||||||
|
zhouyu_p,skill_power_low_hp,,,0,0.1,0.1,,HP p2 잃을 때마다 스킬 위력 +p1,생명력이 낮을수록 스킬 위력 증가,단계당 스킬 위력 +비율,단계 = 잃은 HP 비율(0.1 = 10 %마다),
|
||||||
|
zhouyu_p,melee_penalty_off,,,0,1,,,,궁병·책사 근접 전투 패널티 없음,1 = 켬,,
|
||||||
|
zhouyu_p,range_mult,,armed,0,1.5,,,,공격 사거리 배율 · 조건: 액티브 사용 중일 때,사거리 ×배율(1.5 = +50 %),,
|
||||||
|
huanggai_a,dmg_taken_pct,,,1,-0.3,,,,받는 피해 증감 · 1턴 지속,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
huanggai_a,damage_to_atk,,,1,1,,,,이번 턴 받은 피해를 다음 턴 공격력에 더함 · 1턴 지속,1 = 켬,,
|
||||||
|
huanggai_a,heal_on_kill,,,1,1.0,,,,적 처치 시 생명력 회복 · 1턴 지속,최대 HP × 비율 회복,,
|
||||||
|
huanggai_p,dmg_taken_pct,,,0,-0.15,,,,받는 피해 증감,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
huanggai_p,low_hp_taken,,,0,0.002,0.01,,잃은 HP 1 %당 받는 피해·반격 피해,생명력이 낮을수록 받는 피해·주는 반격 증가,잃은 HP 1 %당 받는 피해 +비율,잃은 HP 1 %당 반격 피해 +비율,
|
||||||
|
ganning_a,crit_always,,,0,1,,,,무조건 치명타,1 = 켬,,
|
||||||
|
ganning_a,shot_ricochet,,,0,3,0.5,,도탄 수·공격 배율(아군 포함),명중 대상 주위 가까운 유닛에게 도탄(아군 포함),도탄 대상 수,도탄 피해 = 공격력 × 배수,
|
||||||
|
ganning_p,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
ganning_p,ally_hit_warp,,,0,1,,,,자신의 공격이 아군에 맞으면 그 아군 곁으로 이동 + 남은 행동 범위로 재공격,1 = 켬,,
|
||||||
|
chengpu_a,repeat_action,,,0,1,,,,같은 거리·방향으로 한 번 더 공격,1 = 켬,,
|
||||||
|
chengpu_p,push_mult,,,0,1.5,,,,밀치는 거리 배율,밀기 ×배율(2 = 2배),,
|
||||||
|
chengpu_p,dmg_pct,,vs_type:cavalry,0,0.5,,,,주는 피해 증가 · 조건: 상대가 기병일 때,피해 +비율(0.5 = +50 %),,
|
||||||
|
sunce_a,sling_dist_pct,,,0,1.0,,,,이동(슬링) 거리 증가,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
sunce_a,pierce_through,,,0,1,,,,보병·창병도 기병처럼 적을 관통하며 경로 위 적 전원 피해,1 = 아군도 통과 · 0 = 아군 앞 정지,,
|
||||||
|
sunce_a,return_always,,,0,1,,,,공격 후 항상 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,,
|
||||||
|
sunce_a,wall_bounce,,,0,1,,,,전장 가장자리에 닿으면 핀볼처럼 튕겨 남은 거리 소진,1 = 켬,,
|
||||||
|
sunce_p,sling_dist_pct,camp:wu,,0,0.3,,,,이동(슬링) 거리 증가 · 대상: 오 진영 유닛,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
sunce_p,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
sunce_p,return_on_hit,,,0,0,0,,,피해를 입히면 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,1 = 복귀 경로의 유닛을 함께 쓸어 감,
|
||||||
|
sunce_p,dash_dmg_pct,,,0,1.0,,,최대(기본값 = 장비·여포),돌진(슬링) 거리에 비례해 공격력 증가,슬링 최대 거리로 당겼을 때 공격 +비율(최대),,
|
||||||
|
sunquan_a,shot_blast,,,0,1.5,1.8,,화살비 반경(기본값)·공격 배율,투사체가 첫 적 자리에서 폭발 — 반경 안 적 전원 피해,폭발 반경,피해 = 공격력 × 배수,
|
||||||
|
sunquan_a,hit_confuse,,,0,1,0.5,1,,명중한 적 혼란(이동 거리 감소),혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
sunquan_p,range_mult,camp:wu:archer,,0,1.5,,,,공격 사거리 배율 · 대상: 오 진영 궁병 유닛,사거리 ×배율(1.5 = +50 %),,
|
||||||
|
sunquan_p,arrow_pass_allies,,,0,1,0,,,화살이 아군을 통과,1 = 켬,1 = 장애물도 통과,
|
||||||
|
sunquan_p,melee_penalty_off,,,0,1,,,,궁병·책사 근접 전투 패널티 없음,1 = 켬,,
|
||||||
|
lusu_a,shot_barricade,,,0,3,1.0,,목책 칸 수·밀기,슬링 목표점에 목책 세움 — 그 자리 적 피해·밀침,목책 칸 수,밀기 거리,
|
||||||
|
lusu_p,atk_per_ally_range,,,0,0.05,,,,행동 반경 안 아군 1기당 공격력 증가,아군 1기당 공격 +비율,,
|
||||||
|
lusu_p,turn_start_heal_range,,,0,0.1,,,,자기 턴 시작 시 행동 반경 안 아군 회복,최대 HP × 비율 회복,,
|
||||||
|
lusu_p,death_banner,,,0,5,,,깃발 턴,쓰러진 자리에 깃발 — 패시브 효과 유지,유지 턴,,
|
||||||
|
lvmeng_a,reaction_on_hit,,,0,1,3,,재행동·최대,이 행동으로 적에게 피해를 주면 재행동(연쇄),피해 시 재행동 수,연쇄 최대 재행동,
|
||||||
|
lvmeng_a,reaction_on_kill,,,0,1,3,,,이 행동으로 적을 처치하면 재행동(연쇄),처치 시 재행동 수,연쇄 최대 재행동,
|
||||||
|
lvmeng_p,counter_taken_pct,,,0,-0.3,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
lvmeng_p,reaction_atk_stack,,,0,0.2,,,,이번 턴 재행동할 때마다 공격력 증가(2배씩 · 턴마다 초기화),첫 재행동 공격 +비율(다음은 2배 · 4배),,
|
||||||
|
luxun_a,shot_firewall,,,0,0.6,3,3.0,장벽 반폭(기본값)·턴·닿을 때 공격 배율,진행 방향 불 장벽(지대) — 닿는 적 피해 + 화상,장벽 반폭,유지 턴,닿을 때 피해 = 공격력 × 배수
|
||||||
|
luxun_a,hit_burn,,,0,3,0.05,1,,명중한 적 화상(불태우기 · 턴 시작마다 생명력 감소),화상 턴,1회 최대 HP × 비율,확률
|
||||||
|
luxun_p,dmg_pct,camp:wu,vs_status,0,0.5,,,,주는 피해 증가 · 대상: 오 진영 유닛 · 조건: 상대가 상태 이상일 때,피해 +비율(0.5 = +50 %),,
|
||||||
|
luxun_p,hit_confuse,,,0,1,0.5,1,,명중한 적 혼란(이동 거리 감소),혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
sunshangxiang_a,volley,,,0,1.0,1.0,,공격 배율(기본값)·밀기(기본값),사거리 안 적 전원에게 화살 — 피해·밀침(치명타 판정),피해 = 공격력 × 배수,밀기 거리,
|
||||||
|
sunshangxiang_a,hit_bleed,,crit,0,2,0.5,1,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소) · 조건: 치명타일 때,출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
sunshangxiang_p,dmg_pct,,vs_male_hero,0,0.5,,,,주는 피해 증가 · 조건: 상대가 남성 영웅일 때,피해 +비율(0.5 = +50 %),,
|
||||||
|
sunshangxiang_p,melee_penalty_off,,,0,1,,,,궁병·책사 근접 전투 패널티 없음,1 = 켬,,
|
||||||
|
xiaoqiao_a,heal_atk,range,,0,0.8,,,,회복 · 대상: 시전 순간 행동 반경 안 아군,회복 = 공격력 × 배수,,
|
||||||
|
xiaoqiao_a,turn_end_heal,,,1,3,0.3,,반경·공격 배율,턴 종료 시 주위 아군 회복 · 1턴 지속,반경,회복 = 공격력 × 배수,
|
||||||
|
xiaoqiao_p,dmg_taken_pct,,vs_male_hero,0,-0.3,,,,받는 피해 증감 · 조건: 상대가 남성 영웅일 때,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
xiaoqiao_p,ally_hit_heal,,,0,0.5,,,,자신의 공격이 아군에 맞으면 피해 대신 회복,회복 = 공격력 × 배수,,
|
||||||
|
lingtong_a,aoe_strike,,,0,3,1.5,1,,자신 주위 적 전원 공격·밀침,반경,피해 = 공격력 × 배수(0 = 피해 없음),바깥으로 밀기 거리
|
||||||
|
lingtong_p,atk_pct,,,0,0.15,,,,공격력 증가,공격력 +비율(0.2 = +20 %),,
|
||||||
|
lingtong_p,push_mult,,,0,1.3,,,,밀치는 거리 배율,밀기 ×배율(2 = 2배),,
|
||||||
|
zhoutai_a,shot_pierce,,,0,1.8,1,1.0,검기 · 아군도 피해 · 밀기(기본값),직선 관통 사격 — 경로 위 유닛 전원(장애물 무시),피해 = 공격력 × 배수,아군 처리(0 통과 · 1 피해 · 2 회복),밀기 거리(0 = 없음)
|
||||||
|
zhoutai_p,hit_confuse,,crit,0,1,0.5,1,,명중한 적 혼란(이동 거리 감소) · 조건: 치명타일 때,혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
zhoutai_p,crit_always,,vs_type:spear,0,1,,,,무조건 치명타 · 조건: 상대가 창병일 때,1 = 켬,,
|
||||||
|
zhugejin_a,shot_whirl,,,0,1.0,3.0,0,회오리 반폭(기본값)·밀기(기본값)·공격 배율(피해 없음),회오리 — 직선 위 적을 진행 방향으로 크게 밀침,회오리 반폭,밀기 거리,피해 = 공격력 × 배수(0 = 없음)
|
||||||
|
zhugejin_a,hit_confuse,,,0,1,0.5,1,,명중한 적 혼란(이동 거리 감소),혼란 턴,이동 거리 배율(0.5 = 반감),확률
|
||||||
|
zhugejin_p,splash_ally_ratio,,,0,0,,,,자신의 광역이 아군에게 주는 피해 비율,아군 피해 비율(0 = 아군 피해 없음),,
|
||||||
|
dongxi_a,atk_ratio,,,0,1.5,,,,이 공격의 피해 = 공격력의 배수,공격력 배수(1.8 = 180 %),,
|
||||||
|
dongxi_p,atk_pct,,,0,0.1,,,,공격력 증가,공격력 +비율(0.2 = +20 %),,
|
||||||
|
lvbu_a,invuln,,,1,1,,,,무적 — 모든 피해 무효(행동 중이 아닌 동안) · 1턴 지속,1 = 켬,,
|
||||||
|
lvbu_a,counter_dmg_pct,,,1,1.0,,,,주는 반격 피해 증가 · 1턴 지속,반격 피해 +비율(0.5 = +50 % · 1.0 = 2배),,
|
||||||
|
lvbu_a,sling_dist_pct,,,1,0.5,,,,이동(슬링) 거리 증가 · 1턴 지속,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
lvbu_a,return_always,,,1,0,,,,공격 후 항상 출발 위치로 복귀 · 1턴 지속,1 = 복귀하며 경로 위 적에게도 피해,,
|
||||||
|
lvbu_p,atk_pct,camp:other,,0,0.2,,,,공격력 증가 · 대상: 군웅 진영 유닛,공격력 +비율(0.2 = +20 %),,
|
||||||
|
lvbu_p,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
lvbu_p,matchup_floor,,,0,1.0,,,,상성 무시(불리한 상성만 무시 · 유리 상성은 유지),상성 배율 하한(1.0 = 불리 상성 없음),,
|
||||||
|
lvbu_p,dash_dmg_pct,,,0,1.0,,,,돌진(슬링) 거리에 비례해 공격력 증가,슬링 최대 거리로 당겼을 때 공격 +비율(최대),,
|
||||||
|
zhangjiao_a,invuln,,,1,1,,,,무적 — 모든 피해 무효(행동 중이 아닌 동안) · 1턴 지속,1 = 켬,,
|
||||||
|
zhangjiao_p,dmg_taken_from_archer_pct,,,0,-0.3,,,,적 궁병에게 받는 피해 증감,받는 피해 비율(−0.3 = 30 % 감소),,
|
||||||
|
zhangjiao_p,hit_poison,,,0,5,0.02,0.5,턴(기본값)·확률,명중한 적 중독(턴 시작·행동 끝마다 생명력 감소),중독 턴,1회 최대 HP × 비율(중첩 수만큼 곱),확률
|
||||||
|
huaxiong_a,dmg_pct,,,0,1.0,,,,주는 피해 증가,피해 +비율(0.5 = +50 %),,
|
||||||
|
huaxiong_a,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
huaxiong_p,atk_pct,,turn_le:3,0,0.5,,,,공격력 증가 · 조건: 전투 3턴까지,공격력 +비율(0.2 = +20 %),,
|
||||||
|
huaxiong_p,counter_taken_pct,,turn_le:3,0,-0.5,,,,받는 반격 피해 증감 · 조건: 전투 3턴까지,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
yanliang_a,pierce_through,,,0,0,,,0 = 아군에 막힘,보병·창병도 기병처럼 적을 관통하며 경로 위 적 전원 피해,1 = 아군도 통과 · 0 = 아군 앞 정지,,
|
||||||
|
yanliang_a,hit_bleed,,,0,1,0.5,1,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소),출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
yanliang_p,counter_dmg_pct,,,0,0.5,,,,주는 반격 피해 증가,반격 피해 +비율(0.5 = +50 % · 1.0 = 2배),,
|
||||||
|
yanliang_p,counter_lifesteal,,,0,0.3,,,,반격 피해만큼 회복,반격 피해 × 비율 회복,,
|
||||||
|
wenchou_a,dmg_pct,,,0,0.5,,,,주는 피해 증가,피해 +비율(0.5 = +50 %),,
|
||||||
|
wenchou_a,return_always,,,0,0,,,,공격 후 항상 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,,
|
||||||
|
wenchou_p,heal_on_kill,,,0,0.3,,,,적 처치 시 생명력 회복,최대 HP × 비율 회복,,
|
||||||
|
gongsunzan_a,sling_dist_pct,,,0,0.5,,,,이동(슬링) 거리 증가,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
gongsunzan_a,lifesteal,,,0,0.5,,,,입힌 피해만큼 회복(흡혈),입힌 피해 × 비율 회복,,
|
||||||
|
gongsunzan_a,return_always,,,0,0,,,,공격 후 항상 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,,
|
||||||
|
gongsunzan_p,dmg_pct,,vs_type:archer,0,1.0,,,,주는 피해 증가 · 조건: 상대가 궁병일 때,피해 +비율(0.5 = +50 %),,
|
||||||
|
gongsunzan_p,heal_on_kill,,,0,0.3,,,,적 처치 시 생명력 회복,최대 HP × 비율 회복,,
|
||||||
|
dongzhuo_a,rampage,,,0,3,1.5,,돌진 초·밀치기 거리(기본값),일정 시간 직진 난동 — 닿는 유닛 전원 밀침(적 = 피해) · 가장자리 튕김 · 자신은 튕기지 않음,돌진 시간(초 · 이동 속도 × 초 = 거리),옆으로 밀치는 거리,
|
||||||
|
dongzhuo_p,dmg_taken_pct,camp:other,,0,-0.1,,,,받는 피해 증감 · 대상: 군웅 진영 유닛,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
dongzhuo_p,lifesteal,,,0,0.3,0.05,,p2 = 아군 1기 줄 때마다 +,입힌 피해만큼 회복(흡혈),입힌 피해 × 비율 회복,아군 1기 줄 때마다 비율 +,
|
||||||
|
lijue_a,splash_heal_lowest,,,0,0.5,2,,광역 피해 비율·인접 반경(기본값),광역으로 준 피해만큼 인접한 가장 약한 아군 회복,광역 피해 × 비율 회복,인접 반경,
|
||||||
|
lijue_p,action_end_sacrifice,,,0,0.1,0.5,2,HP 소모·공격 %·인접 반경(기본값),행동 끝 = 생명력 소모 후 인접 아군 공격력 강화(대상 없으면 발동 안 함),HP 소모 비율(HP 10 % 이하면 소모 없이),인접 아군 공격 +비율(1턴),인접 반경
|
||||||
|
yuanshao_a,sling_dist_pct,range,,1,0.5,,,,이동(슬링) 거리 증가 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,이동 거리 +비율(1.0 = 2배),,
|
||||||
|
yuanshao_a,crit_dmg_pct,range,,1,0.5,,,,치명타 피해량 증가 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,치명타 피해 +비율(0.5 = +50 %),,
|
||||||
|
yuanshao_a,crit_chance,range,,1,0.2,,,,치명타 확률 증가 · 대상: 시전 순간 행동 반경 안 아군 · 1턴 지속,확률 +(0.2 = +20 %p),,
|
||||||
|
yuanshao_p,hp_max_pct,camp:other,,0,0.2,,,,최대 생명력 증가 · 대상: 군웅 진영 유닛,최대 HP +비율,,
|
||||||
|
yuanshao_p,hit_bleed,,crit,0,2,0.5,1,,명중한 적 출혈(움직인 거리에 비례해 생명력 감소) · 조건: 치명타일 때,출혈 턴,슬링 최대 거리만큼 움직일 때 최대 HP × 비율,확률
|
||||||
|
yuanshao_p,hit_push,,crit,0,0.3,,,자신 슬링 최대 거리 × p1,명중한 적 밀쳐냄 · 조건: 치명타일 때,밀기 = 자신 슬링 최대 거리 × 비율,,
|
||||||
|
yuanshu_a,heal_self_pct,,,0,0.6,,,,자신 생명력 회복,최대 HP × 비율,,
|
||||||
|
yuanshu_a,dmg_taken_pct,,,1,-0.3,,,,받는 피해 증감 · 1턴 지속,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
yuanshu_p,dmg_taken_pct,,,0,0.15,,,,받는 피해 증감,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
yuanshu_p,atk_pct,radius:3,,0,0.3,,,,공격력 증가 · 대상: 자신 주위 3 안 아군,공격력 +비율(0.2 = +20 %),,
|
||||||
|
yuanshu_p,on_attacked_confuse,,,0,1,0.5,0.5,,자신을 공격한 대상 혼란(확률),혼란 턴,혼란 이동 배율(0.5 = 반감),확률
|
||||||
|
diaochan_a,charm_pull,,,0,1,1,,행동 불가 턴·남성만,행동 반경 안 적을 행동 불가 + 자신 쪽으로 끌어옴,행동 불가 턴,1 = 남성만,
|
||||||
|
diaochan_p,atk_pct,camp_male:other,,0,0.25,,,,공격력 증가 · 대상: 군웅 진영 남성 유닛,공격력 +비율(0.2 = +20 %),,
|
||||||
|
diaochan_p,multi_male_stun,,,0,2,0.5,1,남성 수·확률·턴,한 행동으로 남성 유닛 여럿을 맞히면 확률 기절,최소 남성 수,기절 확률,기절 턴
|
||||||
|
diaochan_p,dmg_taken_pct,,vs_male,0,-0.3,,,,받는 피해 증감 · 조건: 상대가 남성일 때,받는 피해 비율(−0.3 = 30 % 감소 · +0.15 = 15 % 증가),,
|
||||||
|
zhurong_a,shot_as_archer,,,0,1,,,,근접 병종이 궁병처럼 원거리 사격,1 = 켬,,
|
||||||
|
zhurong_a,atk_ratio,,,0,2.2,,,,이 공격의 피해 = 공격력의 배수,공격력 배수(1.8 = 180 %),,
|
||||||
|
zhurong_a,hit_poison,,,0,5,0.02,1,,명중한 적 중독(턴 시작·행동 끝마다 생명력 감소),중독 턴,1회 최대 HP × 비율(중첩 수만큼 곱),확률
|
||||||
|
zhurong_p,lifesteal,,,0,0.2,,,,입힌 피해만큼 회복(흡혈),입힌 피해 × 비율 회복,,
|
||||||
|
zhurong_p,counter_dmg_pct,,,0,0.5,,,,주는 반격 피해 증가,반격 피해 +비율(0.5 = +50 % · 1.0 = 2배),,
|
||||||
|
zhurong_p,terrain_immune,,,0,1,,,,지형(늪·고지) 이동 감소 무시,1 = 켬,,
|
||||||
|
zhurong_p,atk_pct,,vs_female_hero,0,0.5,,,,공격력 증가 · 조건: 상대가 여성 영웅일 때,공격력 +비율(0.2 = +20 %),,
|
||||||
|
menghuo_a,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
menghuo_a,self_bounce_mult,,,0,0,,,,부딪힌 뒤 자신이 튕기는 거리 배율,튕김 ×배율(0 = 튕기지 않음 · 2 = 2배),,
|
||||||
|
menghuo_a,reaction_on_terrain,,,0,1,,,,지형물에 닿거나 이동 감소 지형을 지나면 재행동,재행동 횟수,,
|
||||||
|
menghuo_p,lifesteal,,,0,0.2,,,,입힌 피해만큼 회복(흡혈),입힌 피해 × 비율 회복,,
|
||||||
|
menghuo_p,revive,,,0,0.1,7,0.6,HP 비율·횟수·맞기 전 HP 조건,쓰러지면 부활,부활 HP 비율(1.0 = 100 %),전투당 횟수,맞기 전 HP 가 이 비율 이상일 때만(0 = 조건 없음)
|
||||||
|
gaoshun_a,hit_stun,,,0,1,1,1,p2 1 = 밀린 아군·지형 피해도,명중한 적 기절(다음 자기 턴 행동 불가),기절 턴,1 = 밀린 아군·지형 오브젝트로 준 피해에도,확률(빈칸 = 100 %)
|
||||||
|
gaoshun_p,push_continue,,,0,1,,,,아군·지형 오브젝트를 밀친 뒤 남은 이동 거리로 재행동,1 = 켬,,
|
||||||
|
zhanglu_a,shot_single,,,0,1,,,,책사 사격이 광역 없는 직선 투사체,1 = 켬,,
|
||||||
|
zhanglu_a,atk_ratio,,,0,2.5,,,,이 공격의 피해 = 공격력의 배수,공격력 배수(1.8 = 180 %),,
|
||||||
|
zhanglu_a,hit_poison,,,0,5,0.02,1,,명중한 적 중독(턴 시작·행동 끝마다 생명력 감소),중독 턴,1회 최대 HP × 비율(중첩 수만큼 곱),확률
|
||||||
|
zhanglu_a,poison_zone,,,0,1.0,1,,반경(기본값)·턴,명중 자리에 독 지대 — 닿는 적 중독,지대 반경,유지 턴,
|
||||||
|
zhanglu_p,zombie,,,0,1,,,되살린 HP,처치한 적이 같은 편 망령으로 되살아나 1번 공격(조작 불가 · 다음 턴 소멸),망령 HP,,
|
||||||
|
yanbaihu_a,atk_pct,,,0,0.3,,,,공격력 증가,공격력 +비율(0.2 = +20 %),,
|
||||||
|
yanbaihu_a,crit_chance,,,0,0.15,,,,치명타 확률 증가,확률 +(0.2 = +20 %p),,
|
||||||
|
yanbaihu_p,hp_max_pct,,,0,0.1,,,,최대 생명력 증가,최대 HP +비율,,
|
||||||
|
hanxuan_a,counter_taken_pct,,,0,-1,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
hanxuan_a,push_mult,,,0,2,,,,밀치는 거리 배율,밀기 ×배율(2 = 2배),,
|
||||||
|
hanxuan_p,counter_taken_pct,,,0,-0.2,,,,받는 반격 피해 증감,받는 반격 비율(−1 = 반격 안 받음 · −0.5 = 반감),,
|
||||||
|
jinxuan_a,crit_chance,,,0,0.3,,,,치명타 확률 증가,확률 +(0.2 = +20 %p),,
|
||||||
|
jinxuan_a,crit_dmg_pct,,,0,0.5,,,,치명타 피해량 증가,치명타 피해 +비율(0.5 = +50 %),,
|
||||||
|
jinxuan_p,dmg_taken_from_archer_pct,,,0,-0.15,,,,적 궁병에게 받는 피해 증감,받는 피해 비율(−0.3 = 30 % 감소),,
|
||||||
|
jinxuan_p,dmg_pct,,vs_type:archer,0,0.15,,,,주는 피해 증가 · 조건: 상대가 궁병일 때,피해 +비율(0.5 = +50 %),,
|
||||||
|
zhaofan_a,shot_pierce,,,0,1.6,2,0,아군 2 = 회복(ally_hit_heal),직선 관통 사격 — 경로 위 유닛 전원(장애물 무시),피해 = 공격력 × 배수,아군 처리(0 통과 · 1 피해 · 2 회복),밀기 거리(0 = 없음)
|
||||||
|
zhaofan_a,ally_hit_heal,,,0,0.6,,,,자신의 공격이 아군에 맞으면 피해 대신 회복,회복 = 공격력 × 배수,,
|
||||||
|
zhaofan_p,negate_hits,,,0,1,,,,받는 피해 무효화(전투 시작부터),무효화 횟수,,
|
||||||
|
zhangren_a,shot_pierce,,,0,1.5,0,0,,직선 관통 사격 — 경로 위 유닛 전원(장애물 무시),피해 = 공격력 × 배수,아군 처리(0 통과 · 1 피해 · 2 회복),밀기 거리(0 = 없음)
|
||||||
|
zhangren_p,range_mult,,,0,1.3,,,,공격 사거리 배율,사거리 ×배율(1.5 = +50 %),,
|
||||||
|
chunyuqiong_a,return_on_hit,,,0,0,0,,,피해를 입히면 출발 위치로 복귀,1 = 복귀하며 경로 위 적에게도 피해,1 = 복귀 경로의 유닛을 함께 쓸어 감,
|
||||||
|
chunyuqiong_p,atk_missing_hp,,,0,1.0,,,,잃은 생명력 비율만큼 공격력 증가,잃은 HP 비율 × 계수(1.0 = HP 1 % 감소당 공격 1 %),,
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 3e88ae866fb008444905b1951fa85a30
|
||||||
|
TextScriptImporter:
|
||||||
|
externalObjects: {}
|
||||||
|
userData:
|
||||||
|
assetBundleName:
|
||||||
|
assetBundleVariant:
|
||||||
|
|
@ -1,145 +1,145 @@
|
||||||
id,hero_id,kind,name_ko,hanja,trigger,effect_key,p1,p2,p3,cooldown,lv_max,lv_scale,desc_ko,needs_impl
|
id,hero_id,kind,name_ko,hanja,trigger,effect_key,p1,p2,p3,cooldown,lv_max,lv_scale,desc_ko,needs_impl
|
||||||
liubei_a,liubei,active,인덕,仁德,next_action,extra_move_count;self_bounce_mult,2,0,,3,5,0.1,다음 행동: 알까기 명중 시 자신은 튕기지 않고(자기 튕김 0) 추가 이동이 2회 [p1 추가 이동 횟수 · p2 자기 튕김 배율],0
|
liubei_a,liubei,active,이도류 검법,,next_action,extra_action;counter_taken_pct,,,,3,5,0.1,다음 행동이 2번 행동 가능하며 · 1턴 간 적에게 반격 받지 않음,0
|
||||||
liubei_p,liubei,passive,도원의 맹약,,always,pushed_ally_attack_pct,0.5,,,0,5,0.1,아군이 밀려서 하는 공격 피해 +50 %,0
|
liubei_p,liubei,passive,도원의 맹약,,always,atk_pct;revive,,,,0,5,0.1,모든 촉 진영 유닛 공격력 + 20% 증가하고 · 자신이 사망할 경우 생명력 100% 상태로 1회 부활 (스테이지당 1회),0
|
||||||
guanyu_a,guanyu,active,청룡언월,靑龍偃月,next_action,overwatch_radius_mult;overwatch_unlimited,2,1,,3,5,0.1,다음 행동: 명중 시 견제 반경 ×2 · 그 적 턴 동안 견제 횟수 무제한,0
|
guanyu_a,guanyu,active,청룡언월,靑龍偃月,instant,aoe_strike;hit_bleed,,,,3,5,0.1,즉시: 청룡언월도를 휘둘러 반경 3m 내 모든 적에게 공격력의 250% 피해를 입히며 1m를 밀쳐내고 출혈 효과를 부여,0
|
||||||
guanyu_p,guanyu,passive,무성,武聖,always,counter_ratio,1.0,,,0,5,0.1,반격 피해 0.5 → 1.0 배 [p1 반격 배율(기본 0.5)],0
|
guanyu_p,guanyu,passive,무신,,always,counter_dmg_pct;counter_taken_pct;kill_stack_atk,,,,0,5,0.1,모든 촉 진영의 반격 피해가 50%증가하며 · 자신은 적에게 반격 받지 않으며 · 자신이 적을 처치할 때마다 공격력 10%씩 씩 증가 (최대 100%),0
|
||||||
zhangfei_a,zhangfei,active,장판 일갈,長坂一喝,instant,aoe_push_fear,0.8,4,,4,5,0.1,즉시 ★: 반경 4 안 적 전원을 바깥으로 0.8 밀고 · 이번 적 턴 그 적들은 교전 반응 없음 [p1 밀기 거리 · p2 반경],1
|
zhangfei_a,zhangfei,active,장판 일갈,長坂一喝,instant,aoe_strike;hit_stun,,,,4,5,0.1,즉시: 반경 4m 범위 내 적 전원을 바깥으로 1m 밀쳐내고 피격 된 적을 1턴간 기절,0
|
||||||
zhangfei_p,zhangfei,passive,만인지적,萬人之敵,always,pierce_dmg_mult,1.3,2,,0,5,0.1,관통으로 2기 이상 맞히면 피해 +30 % [p2 최소 관통 수 · 키 근사],0
|
zhangfei_p,zhangfei,passive,만인지적,萬人之敵,always,sling_dist_pct;dash_dmg_pct;double_hit_stun;lifesteal,,,,0,5,0.1,모든 촉 진영의 이동 거리가 30% 증가하고 · 돌진 거리에 비해 피해가 늘어나고(최대 100% · 슬링샷 거리 기준) 2회 피해를 연속 받은 적을 1턴간 기절 시키고 · 기절한 적을 공격할 경우 피해의 50%만큼 생명력 회복,0
|
||||||
zhaoyun_a,zhaoyun,active,단기구주,單騎救主,next_action,pierce_no_counter;pierce_return_on_miss,1,1,,3,5,0.1,다음 행동: 관통 중 반격을 받지 않고 · 빗나가도 출발점 복귀,0
|
zhaoyun_a,zhaoyun,active,단기구주,單騎救主,next_action,sling_dist_pct;dmg_pct,,,,3,5,0.1,다음 행동의 이동 거리가 2배로 늘어나고 피해량이 50% 증가,0
|
||||||
zhaoyun_p,zhaoyun,passive,상산의 창,,always,pierce_knockback_mult,2,,,0,5,0.1,관통 피해를 준 적 밀기 0.35 → 0.7 [0.35→0.7 = ×2],0
|
zhaoyun_p,zhaoyun,passive,상산의 창,,always,counter_taken_pct;return_on_hit,,,,0,5,0.1,적에게 반격 받지 않으며 · 거리에 상관 없이 이동 경로 상 적에게 피해를 입힌 경우 항상 제자리로 복귀하고 · 복귀할 때에도 피해를 입힐 수 있음,0
|
||||||
huangzhong_a,huangzhong,active,백발백중,百發百中,next_action,arrow_pass_allies;arrow_crit_always,1,1,,2,5,0.1,다음 행동: 화살이 아군을 통과(사선)하고 명중 여부와 무관하게 크리,1
|
huangzhong_a,huangzhong,active,백발백중,百發百中,next_action,shot_giant;hit_stun,,,,2,5,0.1,다음 행동 시 공격 거리에 무관하게 지정한 방향으로 거대한 화살을 날려 범위 내 첫 명중 된 적에게 공격력의 500%의 피해를 입히고 · 1턴간 기절 시킴,0
|
||||||
huangzhong_p,huangzhong,passive,노익장,老益壯,always,dmg_pct_hp_below,0.4,0.5,,0,5,0.1,HP 50 % 이하일 때 공 +40 % [p1 공 증가 · p2 HP 임계],0
|
huangzhong_p,huangzhong,passive,노익장,老益壯,always,arrow_pass_allies;crit_chance;crit_dmg_pct,,,,0,5,0.1,모든 공격이 아군과 장애물을 통과해 공격할 수 있으며 · 자신의 치명타 확률이 20% 증가하고 · 치명타 피해량이 50% 증가,0
|
||||||
zhugeliang_a,zhugeliang,active,동남풍,東南風,instant,sling_dist_pct,0.3,0,1,4,5,0.1,즉시: 이번 턴 아군 전원 슬링 거리 +30 % [p1 비율 · p2 절대 가산 · p3 1=아군 전원],0
|
zhugeliang_a,zhugeliang,active,칠성 기원,七星祈願,instant,heal_atk;cleanse,,,,4,5,0.1,즉시: 행동 반경 내 모든 아군의 생명력을 공격력의 100%만큼 회복시키고 · 상태 이상 효과를 제거,0
|
||||||
zhugeliang_p,zhugeliang,passive,신산,神算,always,enemy_aggro_add,-1.5,,,0,5,0.1,적 교전 범위 −1.5(적이 더 늦게 움직인다),0
|
zhugeliang_p,zhugeliang,passive,와룡의 가호,臥龍加護,always,counter_taken_pct;turn_end_heal_lowest,,,,0,5,0.1,모든 촉 진영 유닛의 반격 피해를 50% 반감시키고 · 턴 종료 시 자신의 반경 3m 범위의 생명력이 가장 낮은 아군의 생명력을 공격력의 50%만큼 회복,0
|
||||||
zhoucang_a,zhoucang,active,청룡도 지기,,next_action,push_mult,2,,,3,5,0.1,다음 행동: 알까기 밀기 ×2,0
|
zhoucang_a,zhoucang,active,대도 일섬,大刀一閃,next_action,atk_ratio;hit_bleed;push_mult,,,,3,5,0.1,다음 행동은 180%의 피해를 입히며 · 2턴간 출혈 효과를 유발하며 2배 거리를 밀쳐냄,0
|
||||||
zhoucang_p,zhoucang,passive,산채 체력,,always,hp_max_pct,0.2,,,0,5,0.1,최대 HP +20 %,0
|
zhoucang_p,zhoucang,passive,혈흔 추격,血痕追擊,always,atk_pct,,,,0,5,0.1,출혈 된 대상을 공격할 경우 공격력 50% 증가,0
|
||||||
guanping_a,guanping,active,호위,護衛,instant,guard_share,0.5,1.5,,3,5,0.1,즉시 ★: 이번 적 턴 반경 1.5 안 아군이 받는 피해의 50 % 를 관평이 대신 받음 [p1 대신 받는 비율 · p2 반경],1
|
guanping_a,guanping,active,철벽 호위,鐵壁護衛,instant,guard_radius_mult;guard_dr,,,,3,5,0.1,즉시: 2턴간 호위 범위가 2배로 늘어나며 · 호위 시 받는 피해 50% 감소,0
|
||||||
guanping_p,guanping,passive,후계,後繼,always,overwatch_radius_add,0.5,,,0,5,0.1,견제 반경 +0.5,0
|
guanping_p,guanping,passive,호위,護衛,always,counter_dmg_pct;guard,,,,0,5,0.1,반격 피해가 50% 증가하며 · 항상 자신 주위 2m 범위 내 아군의 피해를 대신 받음,0
|
||||||
xushu_a,xushu,active,팔문 간파,八門看破,next_action,splash_ally_ratio,0,,,3,5,0.1,다음 행동: 광역 피해가 아군에게 0(광역),1
|
xushu_a,xushu,active,단복의 화계,單福火計,next_action,shot_blast;hit_burn,,,,3,5,0.1,다음 행동 시 지정한 방향으로 거대한 화염구를 날려 범위 내 모든 적에게 공격력의 250%의 피해를 입히고 · 3턴간 불태우기 효과 유발 (불태우기 효과 : 매턴 시작 시 생명력의 5% 피해),0
|
||||||
xushu_p,xushu,passive,단기,單騎,always,range_add,0.5,,,0,5,0.1,사거리 +0.5,0
|
xushu_p,xushu,passive,팔문 간파,八門看破,always,range_mult;splash_ally_ratio,,,,0,5,0.1,공격 사거리 50% 증가 및 자신의 광역 공격이 아군에게는 피해를 입히지 않음,0
|
||||||
taishici_a,taishici,active,신사,神射,next_action,range_mult,1.5,,,3,5,0.1,다음 행동: 사거리 ×1.5,0
|
taishici_a,taishici,active,관통 화살,,next_action,shot_pierce,,,,3,5,0.1,다음 행동: 경로 상 모든 적을 관통하며 공격력의 300%의 피해를 입힘 (장애물 무시),0
|
||||||
taishici_p,taishici,passive,돌파,突破,always,melee_penalty_off,1,,,0,5,0.1,궁수 근접 패널티 없음(피해 100 % · 피격 100 %),0
|
taishici_p,taishici,passive,신궁,神弓,always,melee_penalty_off;atk_pct;crit_dmg_pct,,,,0,5,0.1,근접 유닛과의 전투 시 패널티가 없으며 · 자신의 공격력과 치명타 피해량이 50%씩 증가,0
|
||||||
machao_a,machao,active,금마초,錦馬超,next_action,pierce_dmg_mult;pierce_knockback_mult,1.5,2,,3,5,0.1,다음 행동: 관통 피해 +50 % · 관통한 적 밀기 ×2,0
|
machao_a,machao,active,금마 일격,錦馬一擊,next_action,atk_pct;crit_always,,,,3,5,0.1,다음 행동의 공격력이 50% 증가하고 · 무조건 치명타 피해를 입힘,0
|
||||||
machao_p,machao,passive,신위,神威,always,pierce_dmg_pct_per_hit,0.08,0.4,,0,5,0.1,관통한 적 1기당 공 +8 %(최대 +40 % · 전투 중 누적) [p1 1기당 · p2 최대],0
|
machao_p,machao,passive,복수의 화신,復讐化身,always,lifesteal;dmg_per_hit,,,,0,5,0.1,입힌 피해의 20%만큼 생명력을 회복하고 · 공격으로 명중 된 적 1기당 피해량 25%씩 증가,0
|
||||||
weiyan_a,weiyan,active,자오곡 기책,子午谷奇策,next_action,matchup_floor;dmg_pct,1.0,0.3,,3,5,0.1,다음 행동: 상성 불리(기병 0.7)를 1.0 으로 · 피해 +30 %,0
|
weiyan_a,weiyan,active,자오곡 기책,子午谷奇策,next_action,swap_on_hit;extra_action,,,,3,5,0.1,다음 행동 시 명중 된 대상과 위치를 자신의 위치와 교환하고 · 한번 더 행동할 수 있음,0
|
||||||
weiyan_p,weiyan,passive,반골,反骨,always,dmg_taken_pct,-0.2,0.5,,0,5,0.1,HP 50 % 이하일 때 받는 피해 −20 % [p2 HP 임계(이하일 때)],0
|
weiyan_p,weiyan,passive,반골,反骨,always,matchup_floor;atk_missing_hp,,,,0,5,0.1,상성 효과를 무시하며 · HP가 낮을수록 공격력이 증가 (남은 HP %비율 기준 · 최대 100%),0
|
||||||
pangtong_a,pangtong,active,봉추의 계,鳳雛之計,instant,aoe_push_fear,0.5,3,,4,5,0.1,즉시 ★: 반경 3 안 적 전원을 0.5 밀고 이번 적 턴 교전 반응 없음 [p1 밀기 거리 · p2 반경],1
|
pangtong_a,pangtong,active,봉추의 계,鳳雛之計,instant,heal_atk;hot,,,,4,5,0.1,즉시: 행동 반경 내 모든 아군의 생명력을 공격력의 50%만큼 즉시 회복시키고 · 3턴간 매 턴마다 공격력의 10%씩 생명력을 회복,0
|
||||||
pangtong_p,pangtong,passive,연환,連環,always,splash_ratio,0.8,,,0,5,0.1,광역 피해 비율 0.6 → 0.8 [광역 비율 절대값(기본 0.6)],0
|
pangtong_p,pangtong,passive,연환 방비,連環防備,always,dmg_taken_ranged_pct;turn_end_cleanse,,,,0,5,0.1,모든 촉 진영 유닛의 원거리 공격 피해를 50% 반감시키고 · 턴 종료 시 자신의 반경 3m 범위의 아군 1기의 상태이상 효과를 제거,0
|
||||||
jiangwei_a,jiangwei,active,구벌중원,九伐中原,next_action,push_mult;overwatch_count,1.5,2,,3,5,0.1,다음 행동: 밀며 전진 거리 ×1.5 · 명중 시 견제 횟수 2회 [밀며 전진 거리 배율 · 키 근사],0
|
jiangwei_a,jiangwei,active,담대여두,膽大如斗,instant,heal_self_pct;guard_radius_mult,,,,3,5,0.1,즉시: 자신의 생명력을 즉시 50% 회복하고 · 반격 피해 범위를 3턴간 50% 확장,0
|
||||||
jiangwei_p,jiangwei,passive,기린아,麒麟兒,always,overwatch_dmg_mult,1.3,,,0,5,0.1,견제 피해 ×1.3,0
|
jiangwei_p,jiangwei,passive,기린아의 수호,麒麟兒守護,always,counter_dmg_pct;guard_counter,,,,0,5,0.1,자신의 반격 피해가 50% 증가하고 · 자신의 반격 범위 내 아군이 공격 받을 경우 자신이 피해를 대신 받아 반격 함.,0
|
||||||
liufeng_a,liufeng,active,양자의 분투,養子奮鬪,next_action,pierce_dmg_mult,1.4,,,3,5,0.1,다음 행동: 관통 피해 +40 %,0
|
liufeng_a,liufeng,active,양자의 분투,養子奮鬪,next_action,atk_ratio;hit_confuse,,,,3,5,0.1,다음 행동은 185%의 피해를 입히며 · 경로상 피해를 입은 적들에게 1턴간 혼란 효과를 유발시킴.,0
|
||||||
liufeng_p,liufeng,passive,상용 수비,上庸守備,always,dmg_taken_pct,-0.1,,,0,5,0.1,받는 피해 −10 %,0
|
liufeng_p,liufeng,passive,추격 제압,追擊制壓,always,counter_taken_pct;hit_stun,,,,0,5,0.1,혼란 상태의 적을 공격할 경우 반격 피해를 받지 않고 1턴간 기절 시킴,0
|
||||||
jianyong_a,jianyong,active,유세,遊說,instant,aoe_push_fear,0,3,,4,5,0.1,즉시 ★: 반경 3 안 적 전원 이번 적 턴 교전 반응 없음 [p1 밀기 0 · p2 반경 · 교전 반응 정지만],1
|
jianyong_a,jianyong,active,유세,遊說,next_action,ally_hit_heal;hit_stun,,,,4,5,0.1,다음 행동으로 공격 대상이 아군일 경우 공격력의 80%만큼 회복시키고 · 공격 대상이 적일 경우 1턴간 기절 시킴,0
|
||||||
jianyong_p,jianyong,passive,말재주,,always,cooldown_add,-1,0,,0,5,0.1,자기 액티브 쿨 −1(최소 1) ★ [p2 0=자기만 · 최소 1 · 신규 키],1
|
jianyong_p,jianyong,passive,말재주,,always,heal_cleanse;on_attacked_stun,,,,0,5,0.1,자신의 행동으로 회복 된 대상의 상태이상 효과를 제거할 수 있으며 · 자신을 공격한 대상 1기를 1턴 간 기절시킴(발동 시 3턴 후 1회 충전),0
|
||||||
sunqian_a,sunqian,active,사신,使臣,instant,sling_dist_pct,0.2,0,1,4,5,0.1,즉시: 이번 턴 아군 전원 슬링 거리 +20 % [p3 1=아군 전원],0
|
sunqian_a,sunqian,active,화친,和親,next_action,splash_heal,,,,4,5,0.1,다음 행동: 다음 공격이 명중 된 대상 주위에 범위 내 모든 유닛(적군 포함)의 생명력을 공격력의 60%만큼 회복,0
|
||||||
sunqian_p,sunqian,passive,연락,連絡,always,team_range_add,0.3,,,0,5,0.1,아군 궁병·책사 사거리 +0.3 [대상 = 궁병·책사],0
|
sunqian_p,sunqian,passive,향도,嚮導,always,sling_dist_pct,,,,0,5,0.1,자신 주위 3m 반경 내 아군의 행동 반경을 30% 증가,0
|
||||||
mizhu_a,mizhu,active,거부의 화살,巨富之矢,next_action,arrow_crit_always,1,,,3,5,0.1,다음 행동: 명중 여부와 무관하게 크리,0
|
mizhu_a,mizhu,active,행진가,,instant,sling_dist_pct;counter_taken_pct,,,,3,5,0.1,즉시: 1턴 간 행동 범위 내 아군의 이동력이 50% 상승하며 · 받는 반격 피해가 50% 반감 됨,0
|
||||||
mizhu_p,mizhu,passive,재력,財力,always,hp_max_pct,0.15,,,0,5,0.1,최대 HP +15 %,0
|
mizhu_p,mizhu,passive,지원,,always,ally_hit_buff,,,,0,5,0.1,자신의 공격 대상이 아군일 경우 1턴간 이동력과 공격력 20% 증가,0
|
||||||
caocao_a,caocao,active,난세의 간웅,,instant,team_dmg_taken_pct_turn,-0.3,,,4,5,0.1,즉시: 이번 적 턴 아군 전원 받는 피해 −30 %,0
|
caocao_a,caocao,active,난세의 간웅,,next_action,pass_allies;aim_push,,,,4,5,0.1,다음 행동은 아군을 통과하며 · 공격에 명중 된 적이 있을 경우 피해를 입힌 후 밀려나지 않고 원하는 방향으로 밀쳐낼 수 있음 (슬링샷 동작을 한번 더 조작해 유닛을 밀칠 수 있음),0
|
||||||
caocao_p,caocao,passive,패자,霸者,always,team_atk_pct_full,0.1,5,,0,5,0.1,출전 5인일 때 아군 전원 공 +10 % [p2 출전 인원 조건],0
|
caocao_p,caocao,passive,패자,霸者,always,atk_pct;matchup_floor;on_ally_death_atk,,,,0,5,0.1,모든 위 진영 유닛 공격력 +20% 증가하고 · 자신은 상성 효과를 무시하며 · 아군이 사망할 때마다 남은 아군의 공격력이 + 10%씩 증가,0
|
||||||
xiahoudun_a,xiahoudun,active,발시담정,拔矢啖睛,next_action,pierce_dmg_mult;pierce_no_counter,1.5,1,,3,5,0.1,다음 행동: 관통 피해 +50 % · 반격 받지 않음,0
|
xiahoudun_a,xiahoudun,active,맹장,猛將,next_action,sling_dist_pct;return_aim,,,,3,5,0.1,다음 행동은 이동 거리가 50% 증가하고 · 행동 거리에 상관 없이 원하는 방향으로 복귀할 수 있음. (슬링샷 조작으로 복귀),0
|
||||||
xiahoudun_p,xiahoudun,passive,맹장,猛將,always,dmg_pct_hp_below,0.5,0.3,,0,5,0.1,HP 30 % 이하일 때 공 +50 % [p1 공 증가 · p2 HP 임계],0
|
xiahoudun_p,xiahoudun,passive,회군 돌파,回軍突破,always,dmg_pct;return_on_hit;matchup_floor,,,,0,5,0.1,모든 위군 진영의 기병 피해가 50% 증가하고 · 공격 후 복귀 경로에 유닛이 있는 경우 복귀 경로로 적을 밀쳐내며 함께 원래 위치로 복귀(아군 포함)하고 · 자신은 상성 효과를 무시,0
|
||||||
xuchu_a,xuchu,active,호치,虎痴,next_action,self_bounce_mult;push_mult;extra_move_attack,0,1.5,1,3,5,0.1,다음 행동: 자기 튕김 0 · 밀기 ×1.5 · 추가 이동에서 공격 가능,0
|
xuchu_a,xuchu,active,호치,虎痴,next_action,self_bounce_mult;hit_stun,,,,3,5,0.1,다음 행동은 자신의 튕겨지는 거리가 2배로 증가하고 · 자신에게 명중 된 모든 적을 1턴간 기절 시킴,0
|
||||||
xuchu_p,xuchu,passive,거구,巨軀,always,knockback_taken_pct,-0.5,,,0,5,0.1,밀리는 거리 −50 %,0
|
xuchu_p,xuchu,passive,거구,巨軀,always,hp_max_pct;knockback_taken_pct;regen_pct,,,,0,5,0.1,최대 생명력이 50% 증가하고 · 적 공격에 밀쳐지지 않으며 · 매턴 생명력이 5%씩 회복,0
|
||||||
zhangliao_a,zhangliao,active,위진소요,威震逍遙,next_action,pierce_repeat,1,,,4,5,0.1,다음 행동: 명중 시 복귀 대신 목표점에서 같은 방향으로 한 번 더 관통 [p1 추가 관통 횟수],0
|
zhangliao_a,zhangliao,active,요래,遼來,next_action,drag_back;hit_confuse,,,,4,5,0.1,다음 행동으로 경로상 명중 된 적이 있을 경우 피해를 입힌 후 자신이 있던 위치로 끌고 온 후 1턴간 혼란 상태에 빠뜨림(혼란 : 이동력 50% 감소),0
|
||||||
zhangliao_p,zhangliao,passive,팔백 돌격,,always,pierce_dmg_pct_per_hit,0.05,0.25,,0,5,0.1,관통한 적 1기당 공 +5 %(최대 +25 % · 전투 중 누적) [p1 1기당 · p2 최대],0
|
zhangliao_p,zhangliao,passive,위진소요,威震逍遙,always,atk_pct;counter_taken_pct,,,,0,5,0.1,상태 이상에 걸린 적 공격 시 공격력 50% 증가하고 · 반격 피해를 받지 않음,0
|
||||||
xuhuang_a,xuhuang,active,대부,大斧,next_action,matchup_floor,1.0,,,3,5,0.1,다음 행동: 상성 불리(기병 0.7)를 1.0 으로,0
|
xuhuang_a,xuhuang,active,대부,大斧,instant,axe_aura,,,,3,5,0.1,즉시: 3턴 간 자신 주위를 맴도는 도끼를 생성해 이동 경로상 도끼 범위에 닿거나 자신을 공격한 대상에게 공격력의 80%의 피해를 추가로 입히고 · 도끼가 회전할 때 자신을 공격한 대상을 2배 멀리 튕겨나게 함,0
|
||||||
xuhuang_p,xuhuang,passive,군율,軍律,always,counter_taken_pct,-0.3,,,0,5,0.1,받는 반격 피해 −30 %,0
|
xuhuang_p,xuhuang,passive,혈부,血斧,always,hit_bleed,,,,0,5,0.1,공격 대상에게 30% 확률로 3턴간 지속하는 출혈 효과를 부여(출혈 : 이동 거리의 50%에 비례한 생명력 감소)하고 · 대상에게 치명타 발생 시 100% 확률로 출혈 효과를 부여 (액티브 스킬 - 회전 도끼에도 동일한 효과),0
|
||||||
zhanghe_a,zhanghe,active,변진,變陣,instant,teleport,3,,,3,5,0.1,즉시 ★: 반경 3 안 빈 지점으로 이동(행동 소모 없음) [p1 반경],1
|
zhanghe_a,zhanghe,active,변진,變陣,instant,teleport;extra_action,,,,3,5,0.1,즉시: 반경 5m 내 모든 대상을 무시하고 이동 가능(슬링샷 조작으로 원하는 이동 위치 결정)하고 · 이동 후 재행동 가능,0
|
||||||
zhanghe_p,zhanghe,passive,교변,巧變,always,overwatch_dmg_mult,1.5,,,0,5,0.1,견제 피해 ×1.5,0
|
zhanghe_p,zhanghe,passive,교변,巧變,always,push_mult;pushed_ally_atk_add;self_pushed_mult,,,,0,5,0.1,아군이나 적을 미는 거리가 2배 증가하고 · 아군을 밀쳐 적을 공격한 경우 자신의 공격력 만큼 아군의 피해가 증가하고 · 자신이 밀쳐져 적을 공격한 경우에는 2배의 피해를 입힘,0
|
||||||
caoren_a,caoren,active,철벽,鐵壁,instant,dmg_taken_aura,-0.4,2,,3,5,0.1,즉시 ★: 이번 적 턴 반경 2 안 아군 전원 받는 피해 −40 % [p1 받는 피해 · p2 반경],1
|
caoren_a,caoren,active,철벽,鐵壁,instant,dmg_taken_pct;zone_slow,,,,3,5,0.1,즉시: 3턴간 자신의 행동 반경 내 모든 아군의 받는 피해가 30% 감소되며 · 자신의 행동 반경을 지나가는 적들의 이동소모량이 50% 증가,0
|
||||||
caoren_p,caoren,passive,수성,守城,always,dmg_taken_pct,-0.33,,,0,5,0.1,방 +50 % [방어 키 없음 · 키 근사 = 방 ×1.5 → 받는 피해 ÷1.5 환산(추정)],0
|
caoren_p,caoren,passive,수성,守城,always,hp_max_pct;heal_on_action_end;unused_move_dr,,,,0,5,0.1,모든 위군 생명력이 20% 증가하고 · 행동을 멈출 때마다 생명력이 20% 회복되며 · 남은 이동거리의 50%만큼 받는 피해를 차감(이동하지 않았을 경우 · 최대 50%의 피해 차감),0
|
||||||
yujin_a,yujin,active,군령,軍令,next_action,pushed_ally_attack_count,2,,,3,5,0.1,다음 행동: 알까기로 밀린 아군의 공격 2회,0
|
yujin_a,yujin,active,군령,軍令,next_action,ally_hit_reaction,,,,3,5,0.1,다음 행동으로 행동 종료한 아군이 명중 된 경우 · 재행동 가능(다수 아군 명중 시 모두 재행동 가능),0
|
||||||
yujin_p,yujin,passive,진중,陣中,always,counter_ratio,1.0,1,,0,5,0.1,밀렸을 때 반격 1.0 배 [p2 1=밀렸을 때만 · 키 근사],0
|
yujin_p,yujin,passive,진중,陣中,always,move_bank,,,,0,5,0.1,이번턴에 이동하지 않았다면 최대 2턴 거리의 이동력을 축적 가능,0
|
||||||
simayi_a,simayi,active,은인자중,隱忍自重,instant,team_dmg_taken_pct_turn,-0.4,,,4,5,0.1,즉시: 이번 적 턴 아군 전원 받는 피해 −40 %,0
|
simayi_a,simayi,active,고평릉의 변,高平陵之變,instant,aoe_drain,,,,4,5,0.1,즉시: 행동 반경 모든 적에게 공격력의 150% 만큼의 피해를 입히고 · 입힌 피해의 60% 만큼 자신을 제외한 범위 내 모든 아군을 치유,0
|
||||||
simayi_p,simayi,passive,응변,應變,always,range_add;splash_radius_mult,0.5,1.2,,0,5,0.1,사거리 +0.5 · 광역 반경 ×1.2,0
|
simayi_p,simayi,passive,응시낭고,鷹視狼顧,always,splash_radius_mult;range_mult;drain_aura,,,,0,5,0.1,모든 위군 책사의 광역 피해 범위가 50% 증가하고 · 자신의 사거리가 50% 증가하며 · 행동 반경 내 적이 입은 피해의 30% 만큼 자신의 생명력을 회복,0
|
||||||
xiahouyuan_a,xiahouyuan,active,신속,神速,next_action,range_mult;arrow_crit_always,1.5,1,,3,5,0.1,다음 행동: 사거리 ×1.5 · 명중 여부와 무관하게 크리,0
|
xiahouyuan_a,xiahouyuan,active,전광 연사,電光連射,next_action,shot_fan,,,,3,5,0.1,다음 행동 시 제자리에서 방사형(부채꼴)으로 지정한 방향으로 화살을 5발씩 3회 연속 발동,0
|
||||||
xiahouyuan_p,xiahouyuan,passive,삼일 오백리,三日五百,always,sling_dist_pct,0.2,,,0,5,0.1,슬링 거리 +20 %,0
|
xiahouyuan_p,xiahouyuan,passive,삼연시,三連矢,always,multishot,,,,0,5,0.1,원거리 공격으로 대상을 타겟한 경우 방사형으로 3발의 화살을 동시에 발사해 경로 상 유닛에게 피해를 입힘(멀티샷),0
|
||||||
guojia_a,guojia,active,십승십패,十勝十敗,instant,team_dmg_pct_turn,0.25,,,4,5,0.1,즉시 ★: 이번 턴 아군 전원 가하는 피해 +25 % [신규 키],1
|
guojia_a,guojia,active,십승십패,十勝十敗,instant,dmg_pct;grant_reaction,,,,4,5,0.1,즉시: 행동 범위 내 아군 전원이 입히는 피해량이 2턴간 30% 증가 하고 · 임의의 아군에게 재행동 기회 1회 부여(이미 행동 종료한 아군에게는 재행동 기회 부여),0
|
||||||
guojia_p,guojia,passive,귀재,鬼才,always,cooldown_add,-1,0,,0,5,0.1,자기 액티브 쿨 −1(최소 1) ★ [p1 쿨 가산 · p2 0=자기만 · 최소 1 · 신규 키],1
|
guojia_p,guojia,passive,귀재,鬼才,always,kill_in_range_reward,,,,0,5,0.1,자신의 행동 범위 내 적을 처치한 아군은 스킬 쿨타임이 1초 감소하고 · 최대 생명력의 30%만큼 회복,0
|
||||||
jiaxu_a,jiaxu,active,독계,毒計,next_action,splash_ally_ratio;splash_ratio,0,1.0,,3,5,0.1,다음 행동: 광역 아군 피해 0 · 광역 비율 1.0(광역),1
|
jiaxu_a,jiaxu,active,독계,毒計,next_action,shot_blast;hit_poison,,,,3,5,0.1,다음 행동 시 지정한 대상 범위에 독구름을 생성해 공격력의 220%의 피해를 입히고 · 5턴간 중독 상태로 만듬 (중독 : 매턴 시작과 행동 종료 시점 마다 최대 생명력의 2%씩 감소),0
|
||||||
jiaxu_p,jiaxu,passive,난세의 독사,,always,dmg_vs_general_mult,1.2,1,,0,5,0.1,보스에게 주는 피해 +20 % [p2 1=보스만],0
|
jiaxu_p,jiaxu,passive,난세의 독사,,always,hit_poison;poison_stack,,,,0,5,0.1,자신의 공격에 당한 적은 항상 중독 상태가 되며 · 중독 효과가 최대 5회까지 중첩될 수 있음,0
|
||||||
dianwei_a,dianwei,active,쌍철극,雙鐵戟,next_action,dmg_pct;self_bounce_mult,0.6,0,,3,5,0.1,다음 행동: 알까기 피해 +60 % · 자기 튕김 0,0
|
dianwei_a,dianwei,active,쌍철극,雙鐵戟,next_action,dmg_pct;push_mult;self_bounce_mult,,,,3,5,0.1,다음 행동 시 입히는 피해량과 밀리는 거리가 100% 증가하며 · 자신은 튕기지 않음,0
|
||||||
dianwei_p,dianwei,passive,고지악래,古之惡來,always,hp_max_pct,0.25,,,0,5,0.1,최대 HP +25 %,0
|
dianwei_p,dianwei,passive,고지악래,古之惡來,always,dmg_taken_pct;gimmick_mult,,,,0,5,0.1,자신의 받는 피해가 20% 감소하며 · 지형 오브젝트를 밀치는 거리와 지형 오브젝트를 이용한 피해량이 2배로 증가,0
|
||||||
xunyu_a,xunyu,active,왕좌지재,王佐之才,instant,counter_ratio,1.0,0,1,4,5,0.1,즉시: 이번 턴 아군 전원 반격 배율 1.0 [p1 반격 배율 · p3 1=아군 전원 · 이번 턴],0
|
xunyu_a,xunyu,active,왕좌지재,王佐之才,instant,counter_taken_pct;atk_pct,,,,4,5,0.1,즉시: 행동 범위 내 아군이 1턴간 반격 받지 않게 되며 · 공격력이 30% 증가,0
|
||||||
xunyu_p,xunyu,passive,보급,補給,always,hp_max_pct,0.1,1,,0,5,0.1,아군 전원 최대 HP +10 % [p2 1=아군 전원],0
|
xunyu_p,xunyu,passive,보급,補給,always,ally_hit_heal;ally_hit_buff,,,,0,5,0.1,자신의 공격 대상이 아군일 경우 · 공격력의 50%만큼 생명력을 회복시키며 · 1턴간 공격력 15% 증가 버프효과 부여,0
|
||||||
xiahoumao_a,xiahoumao,active,부마의 허세,駙馬虛勢,instant,dmg_taken_pct,-0.3,,,3,5,0.1,즉시: 이번 적 턴 자기 받는 피해 −30 % [즉시 · 이번 적 턴 · 자기만],0
|
xiahoumao_a,xiahoumao,active,부마의 허세,駙馬虛勢,instant,dmg_taken_pct,,,,3,5,0.1,즉시: 1턴 간 자신이 받는 피해 -30 %,0
|
||||||
xiahoumao_p,xiahoumao,passive,도주 본능,,always,pierce_return_on_miss,1,,,0,5,0.1,관통이 빗나가도 출발점 복귀,0
|
xiahoumao_p,xiahoumao,passive,도주 본능,,always,sling_dist_pct,,,,0,5,0.1,자신의 이동력 +10%,0
|
||||||
wanglang_a,wanglang,active,설전,舌戰,instant,charm,4,1,,5,5,0.1,즉시 ★: 반경 4 안 가장 가까운 적 무장 1기 — 다음 적 턴 행동 불가 [p1 반경 · p2 턴 · 무장 우선 · 신규 키],1
|
wanglang_a,wanglang,active,설전,舌戰,next_action,shot_pierce;hit_confuse,,,,5,5,0.1,다음 행동은 직선 범위 내 대상에게 180%의 피해를 입히며 · 1턴간 혼란 효과를 유발(아군이 명중할 경우 아군에 피해),0
|
||||||
wanglang_p,wanglang,passive,노신,老臣,always,hp_max_pct,0.1,,,0,5,0.1,최대 HP +10 %,0
|
wanglang_p,wanglang,passive,노신,老臣,always,range_mult,,,,0,5,0.1,최대 사거리+10%,0
|
||||||
xiahouen_a,xiahouen,active,청강검,靑釭劍,next_action,pierce_dmg_mult,1.4,,,3,5,0.1,다음 행동: 관통 피해 +40 %,0
|
xiahouen_a,xiahouen,active,청강검 발도,靑釭劍拔刀,next_action,first_hit_bonus,,,,3,5,0.1,다음 행동으로 최초 명중 된 대상에게 공격력의 50% 피해 추가,0
|
||||||
xiahouen_p,xiahouen,passive,보검의 광채,,always,sling_dist_pct,0.1,,,0,5,0.1,슬링 거리 +10 %,0
|
xiahouen_p,xiahouen,passive,보검의 광채,,always,hp_max_pct,,,,0,5,0.1,최대 생명력 +10%,0
|
||||||
sunjian_a,sunjian,active,강동의 호랑이,,next_action,dmg_pct;extra_move_attack,0.5,1,,3,5,0.1,다음 행동: 알까기 피해 +50 % · 추가 이동에서 공격 가능,0
|
sunjian_a,sunjian,active,강동의 호랑이,,next_action,atk_pct;pierce_through;hit_bleed;invuln,,,,3,5,0.1,다음 행동 시 공격력이 50% 증가하고 · 모든 유닛을 통과하며 공격할 수 있으며 경로 상 피격 된 모든 적에게 출혈 피해를 입힘 · 행동 종료 후 1턴간 모든 피해를 무효화함(튕겨질 순 있고 · 반격 피해는 입힐 수 있음),0
|
||||||
sunjian_p,sunjian,passive,파로,破虜,always,dmg_vs_general_mult,1.2,1,,0,5,0.1,보스에게 주는 피해 +20 % [p2 1=보스만(0=무장 전체) · 키 근사],0
|
sunjian_p,sunjian,passive,파로,破虜,always,atk_pct;kill_reaction,,,,0,5,0.1,모든 오군 진영 공격력 +20% 증가하고 · 자신의 공격으로 적을 처치할 경우 재행동 기회 1회 부여(턴 당 1회),0
|
||||||
zhouyu_a,zhouyu,active,화공,火攻,next_action,splash_radius_mult;splash_ratio,2,1.0,,4,5,0.1,다음 행동: 광역 반경 ×2 · 광역 비율 1.0(광역),1
|
zhouyu_a,zhouyu,active,적벽의 불비,赤壁火雨,next_action,shot_blast;hit_burn;fire_zone,,,,4,5,0.1,다음 행동 시 명중한 대상 주위에 운석을 소환해 넓은 범위에 공격력의 350%의 피해를 입히고 · 피격 된 대상을 불태우기 상태에 빠뜨리며 · 운석이 낙하한 장소에 불꽃을 생성(닿는 적에게 즉시 공격력의 100%의 피해를 입히고 · 3턴간 유지되는 화상 효과를 입힘),0
|
||||||
zhouyu_p,zhouyu,passive,대도독,大都督,always,team_range_add,0.5,,,0,5,0.1,아군 궁병·책사 사거리 +0.5 [대상 = 궁병·책사],0
|
zhouyu_p,zhouyu,passive,대도독,大都督,always,skill_hp_cost;skill_power_low_hp;melee_penalty_off;range_mult,,,,0,5,0.1,스킬 시전 시 생명력이 5% 감소하지만 · 생명이 낮아질 때마다 스킬의 위력이 10%씩 증가하며 · 자신은 근접 유닛과의 전투에 패널티를 받지 않고 · 스킬 사거리가 50% 증가,0
|
||||||
huanggai_a,huanggai,active,고육계,苦肉計,instant,hp_cost_atk,0.6,0.3,,3,5,0.1,즉시 ★: 자기 HP 30 % 소모 · 이번 턴 공 +60 % [p1 공 증가 · p2 HP 소모],1
|
huanggai_a,huanggai,active,고육계,苦肉計,instant,dmg_taken_pct;damage_to_atk;heal_on_kill,,,,3,5,0.1,즉시: 1턴간 자신의 받는 피해가 30% 감소되며 · 자신이 이번 턴에 받은 피해가 다음 턴의 공격력에 추가되며 · 적을 처치한 경우 생명력 100% 회복,0
|
||||||
huanggai_p,huanggai,passive,노장,老將,always,dmg_taken_pct,-0.15,,,0,5,0.1,받는 피해 −15 %,0
|
huanggai_p,huanggai,passive,노장,老將,always,dmg_taken_pct;low_hp_taken,,,,0,5,0.1,자신의 받는 피해 -15 % · 생명력이 낮아질수록 받는 피해가 0.2%씩 증가하고 반격 피해가 1%씩 증가,0
|
||||||
ganning_a,ganning,active,백기습영,百騎襲營,next_action,teleport;arrow_crit_always,0,1,,3,5,0.1,다음 행동: 사거리 밖이어도 목표점까지 이동 후 사격 · 명중 시 크리 [전용 키 없음 · 키 근사 = 이동(p1 0=목표점 · 반경 제한 없음) 후 사격 + 크리],1
|
ganning_a,ganning,active,금범 도탄,錦帆跳彈,next_action,crit_always;shot_ricochet,,,,3,5,0.1,다음 행동으로 발사 된 화살은 무조건 치명타로 명중되며 · 피격 된 대상과 가장 가까운 3명의 유닛(아군포함)에게 공격력의 50% 만큼의 도탄 된 화살로 피해를 입힘,0
|
||||||
ganning_p,ganning,passive,금범적,錦帆賊,always,counter_taken_pct,-1.0,,,0,5,0.1,근접 반격을 받지 않음 [근접 반격 한정 · 키 근사],0
|
ganning_p,ganning,passive,백기습영,百騎襲營,always,counter_taken_pct;ally_hit_warp,,,,0,5,0.1,근접 반격을 받지 않으며 · 아군에게 자신의 공격이 명중 될 경우 아군의 위치로 이동할 수 있고(아군 피해 없음) 남은 행동 범위 내 재공격 가능,0
|
||||||
chengpu_a,chengpu,active,철척사모,鐵脊蛇矛,next_action,push_mult,1.5,,,3,5,0.1,다음 행동: 밀며 전진 거리 ×1.5 [밀며 전진 거리 배율 · 키 근사],0
|
chengpu_a,chengpu,active,연환격,連環擊,next_action,repeat_action,,,,3,5,0.1,다음 행동 시 직전 행동 거리와 방향으로 동일하게 한번 더 공격 시도,0
|
||||||
chengpu_p,chengpu,passive,삼대 노신,,always,overwatch_count,2,,,0,5,0.1,견제 횟수 2회,0
|
chengpu_p,chengpu,passive,거마창,拒馬槍,always,push_mult;dmg_pct,,,,0,5,0.1,밀치는 거리가 50% 증가하고 · 상대 병종이 기병일 경우 피해량 50% 증가,0
|
||||||
sunce_a,sunce,active,소패왕,小霸王,next_action,pierce_dmg_mult;pierce_return_on_miss,2,1,,4,5,0.1,다음 행동: 관통 피해 ×2 · 빗나가도 출발점 복귀,0
|
sunce_a,sunce,active,소패왕,小霸王,next_action,sling_dist_pct;pierce_through;return_always;wall_bounce,,,,4,5,0.1,다음 행동 시 이동 거리가 2배가 되며 지정한 방향으로 모든 유닛을 관통하며 피해를 입히며 · 멈춘 후 원래 자리로 복귀하며 경로상 모든 적에게 피해를 입힘(화면 가장자리에 닿은 경우 핀볼처럼 튕겨지며 이동 거리를 소진),0
|
||||||
sunce_p,sunce,passive,강동의 호걸,,always,counter_taken_pct,-0.5,,,0,5,0.1,받는 반격 피해 −50 %,0
|
sunce_p,sunce,passive,강동의 호걸,,always,sling_dist_pct;counter_taken_pct;return_on_hit;dash_dmg_pct,,,,0,5,0.1,모든 오군의 이동 거리가 30% 증가하고 · 자신은 적에게 반격 받지 않으며 · 적에게 피해를 입힌 경우 무조건 출발 위치로 복귀할 수 있고 · 돌진 거리에 비례해 공격력 증가,0
|
||||||
sunquan_a,sunquan,active,벽안의 군주,碧眼兒,instant,team_dmg_taken_pct_turn;enemy_aggro_add,-0.3,-1,,4,5,0.1,즉시: 이번 적 턴 아군 전원 받는 피해 −30 % · 적 교전 범위 −1,0
|
sunquan_a,sunquan,active,화살비,箭雨,next_action,shot_blast;hit_confuse,,,,4,5,0.1,다음 행동 시 타겟이 된 대상 주위에 화살비를 쏟아 공격력의 180%의 피해를 입히고 · 혼란에 빠뜨려 이동 거리를 50% 반감 시킴,0
|
||||||
sunquan_p,sunquan,passive,강동의 주인,,always,team_atk_pct_full,0.1,5,,0,5,0.1,출전 5인일 때 아군 전원 공 +10 % [p2 출전 인원 조건],0
|
sunquan_p,sunquan,passive,강동의 주인,,always,range_mult;arrow_pass_allies;melee_penalty_off,,,,0,5,0.1,모든 오군의 궁수 사거리가 50% 증가하며 · 자신의 공격이 아군 유닛에게 피해를 주지 않고 관통해 적을 공격할 수 있으며 · 자신은 근접 유닛과의 전투에서 패널티를 받지 않음,0
|
||||||
lusu_a,lusu,active,단도부회,單刀赴會,instant,dmg_taken_aura,-0.4,3,,3,5,0.1,즉시 ★: 이번 적 턴 반경 3 안 아군 받는 피해 −40 % [p1 받는 피해 · p2 반경 · 대상 아군],1
|
lusu_a,lusu,active,녹각진,鹿角陣,next_action,shot_barricade,,,,3,5,0.1,다음 행동으로 지정 된 위치(슬링샷 범위)에 목책을(3열) 생성하고 적이 있는 위치에 목책이 설치 된 경우 피해(필드 오브젝트와 동일)를 주며 튕겨낼 수 있음,0
|
||||||
lusu_p,lusu,passive,후덕,厚德,always,splash_ally_ratio,0.5,,,0,5,0.1,광역 아군 피해 비율 0.5,0
|
lusu_p,lusu,passive,동맹의 깃발,同盟之旗,always,atk_per_ally_range;turn_start_heal_range;death_banner,,,,0,5,0.1,자신의 행동 범위 내 아군 1기당 공격력이 5%씩 증가하고 · 매턴 시작 시 행동 범위 내 아군의 생명력을 10% 회복시킬 수 있으며 · 사망할 경우 쓰러진 장소에 5턴간 효과를 유지하는 깃발을 남겨 자신의 패시브 효과를 전장에 남김,0
|
||||||
lvmeng_a,lvmeng,active,백의도강,白衣渡江,next_action,extra_move_attack;enemy_aggro_off_turn,1,1,,4,5,0.1,다음 행동: 알까기 명중 시 추가 이동에서 공격 가능 · 맞은 적은 이번 적 턴 교전 반응 없음 [p2 대상 = 맞은 적 1기],0
|
lvmeng_a,lvmeng,active,백의도강,白衣渡江,next_action,reaction_on_hit;reaction_on_kill,,,,4,5,0.1,다음 행동으로 적에게 피해를 입힌 경우 · 한번 더 행동할 수 있으며 적을 처치한 경우 한번 더 행동 가능(최대 3회),0
|
||||||
lvmeng_p,lvmeng,passive,괄목상대,刮目相對,always,dmg_pct,0.3,5,,0,5,0.1,5턴째부터 공 +30 % [p2 발동 턴(이상)],0
|
lvmeng_p,lvmeng,passive,괄목상대,刮目相對,always,counter_taken_pct;reaction_atk_stack,,,,0,5,0.1,반격 피해를 30% 반감하며 · 매턴 재행동이 발생할 때마다 추가 공격력이 20% 부여되며 · 추가 공격력은 재행동 횟수에 비례해 2배씩 증가(20%→40%→80%) (추가 공격력은 턴마다 초기화),0
|
||||||
luxun_a,luxun,active,이릉의 화공,夷陵火攻,next_action,splash_radius_mult;splash_ratio,2,0.9,,4,5,0.1,다음 행동: 광역 반경 ×2 · 광역 비율 0.9(광역),1
|
luxun_a,luxun,active,이릉의 화공,夷陵火攻,next_action,shot_firewall;hit_burn,,,,4,5,0.1,다음 행동으로 지정한 방향으로 모든 유닛에게 피해를 입히는 불 장벽을 생성해 3턴간 전장에 유지되며 · 닿는 적에게는 즉시 300%의 피해를 입히고 · 3턴간 화상 효과를 입힘,0
|
||||||
luxun_p,luxun,passive,연영,連營,always,splash_ratio,0.7,,,0,5,0.1,광역 피해 비율 0.6 → 0.7 [광역 비율 절대값],0
|
luxun_p,luxun,passive,백면서생,白面書生,always,dmg_pct;hit_confuse,,,,0,5,0.1,모든 오군이 상태이상 효과에 걸린 적 공격 시 피해량이 50% 증가하며 · 자신의 공격에 피격 된 대상은 1턴간 혼란에 빠뜨림 (혼란 : 이동 속도 50% 감소),0
|
||||||
sunshangxiang_a,sunshangxiang,active,궁요희,弓腰姬,next_action,arrow_pass_allies;range_add,1,1,,3,5,0.1,다음 행동: 화살이 아군을 통과(사선) · 사거리 +1,1
|
sunshangxiang_a,sunshangxiang,active,궁요희,弓腰姬,instant,volley;hit_bleed,,,,3,5,0.1,즉시: 행동 범위 내 모든 적에게 화살을 발사해 피해를 입히고 · 밀쳐내며 치명타 발생 시 2턴간 출혈 효과를 유발함,0
|
||||||
sunshangxiang_p,sunshangxiang,passive,효희,梟姬,always,melee_penalty_off,1,,,0,5,0.1,궁수 근접 패널티 없음,0
|
sunshangxiang_p,sunshangxiang,passive,효희,梟姬,always,dmg_pct;melee_penalty_off,,,,0,5,0.1,남성 영웅에 대한 피해량이 50% 증가하고 · 근접 피해 패널티를 받지 않음.(반격 피해에도 피해량 50% 증가 적용),0
|
||||||
xiaoqiao_a,xiaoqiao,active,금슬,琴瑟,instant,cooldown_add,-1,1,,4,5,0.1,즉시 ★: 아군 전원 액티브 쿨 −1 [p1 쿨 가산 · p2 1=아군 전원 · 신규 키],1
|
xiaoqiao_a,xiaoqiao,active,춘풍가,春風歌,instant,heal_atk;turn_end_heal,,,,4,5,0.1,즉시: 행동 범위 내 아군의 생명력을 공격력의 80% 만큼 즉시 회복하고 턴 종료 시 자신의 3m 주위에 있는 아군의 생명력을 공격력의 30% 만큼 회복,0
|
||||||
xiaoqiao_p,xiaoqiao,passive,이교,二喬,always,team_range_add,0.5,,,0,5,0.1,아군 책사 사거리 +0.5 [대상 = 책사],0
|
xiaoqiao_p,xiaoqiao,passive,경국지색,傾國之色,always,dmg_taken_pct;ally_hit_heal,,,,0,5,0.1,남성 영웅에게 받는 피해가 30% 감소하며 · 자신의 공격 대상이 아군인 경우 공격력의 50% 만큼 생명력을 회복,0
|
||||||
lingtong_a,lingtong,active,유수의 복수,濡須,next_action,push_mult;overwatch_radius_add,1.5,1,,3,5,0.1,다음 행동: 밀며 전진 거리 ×1.5 · 명중 시 견제 반경 +1 [밀며 전진 거리 배율 · 키 근사],0
|
lingtong_a,lingtong,active,선풍 창술,旋風槍術,instant,aoe_strike,,,,3,5,0.1,즉시: 반경 3m 내 모든 적에게 공격력의 150% 피해를 입히고 1m 밀쳐냄,0
|
||||||
lingtong_p,lingtong,passive,용장,勇將,always,counter_taken_pct,-0.3,,,0,5,0.1,받는 반격 피해 −30 %,0
|
lingtong_p,lingtong,passive,용장,勇將,always,atk_pct;push_mult,,,,0,5,0.1,자신의 공격력 +15% 증가하고 · 밀치는 거리 +30% 증가,0
|
||||||
zhoutai_a,zhoutai,active,열두 상처,十二創,instant,guard_share,0.5,2,,3,5,0.1,즉시 ★: 이번 적 턴 반경 2 안 아군이 받는 피해의 50 % 를 주태가 대신 받음 [p1 대신 받는 비율 · p2 반경],1
|
zhoutai_a,zhoutai,active,검기 일섬,劍氣一閃,next_action,shot_pierce,,,,3,5,0.1,다음 행동 시 공격 방향으로 검기를 날려 모든 유닛에게 공격력의 180%의 피해를 입히고 · 밀쳐냄,0
|
||||||
zhoutai_p,zhoutai,passive,철벽 호위,,always,dmg_taken_pct,-0.3,0.5,,0,5,0.1,HP 50 % 이하일 때 받는 피해 −30 % [p2 HP 임계(이하일 때)],0
|
zhoutai_p,zhoutai,passive,파창,破槍,always,hit_confuse;crit_always,,,,0,5,0.1,치명타 피해를 입힌 적에게 혼란 효과를 부여하고 · 창병에게는 항상 치명타로 피해를 입힘,0
|
||||||
zhugejin_a,zhugejin,active,형제의 계,兄弟之計,next_action,splash_ally_ratio,0,,,3,5,0.1,다음 행동: 광역 아군 피해 0(광역),1
|
zhugejin_a,zhugejin,active,선풍계,旋風計,next_action,shot_whirl;hit_confuse,,,,3,5,0.1,다음 행동 시 회오리 바람을 소환해 지정한 방향의 적을 크게 밀쳐내고 낙하한 적에게 1턴간 혼란 효과를 부여,0
|
||||||
zhugejin_p,zhugejin,passive,온후,溫厚,always,dmg_taken_pct,-0.05,,1,0,5,0.1,아군 전원 받는 피해 −5 % [p3 1=아군 전원],0
|
zhugejin_p,zhugejin,passive,온후,溫厚,always,splash_ally_ratio,,,,0,5,0.1,자신의 광역 공격이 아군에게는 피해를 입히지 않음,0
|
||||||
dongxi_a,dongxi,active,유수의 창,濡須之槍,next_action,push_mult,1.3,,,3,5,0.1,다음 행동: 밀며 전진 거리 ×1.3 [밀며 전진 거리 배율 · 키 근사],0
|
dongxi_a,dongxi,active,유수의 창,濡須之槍,next_action,atk_ratio,,,,3,5,0.1,다음 행동 시 공격력의 150%의 피해를 입힘,0
|
||||||
dongxi_p,dongxi,passive,장사,壯士,always,overwatch_dmg_mult,1.2,,,0,5,0.1,견제 피해 ×1.2,0
|
dongxi_p,dongxi,passive,장사,壯士,always,atk_pct,,,,0,5,0.1,자신의 공격력 +10%,0
|
||||||
lvbu_a,lvbu,active,천하무쌍,天下無雙,next_action,pierce_dmg_mult;pierce_no_counter,2,1,,4,5,0.1,다음 행동: 관통 피해 ×2 · 반격 받지 않음,0
|
lvbu_a,lvbu,active,천하무쌍,天下無雙,instant,invuln;counter_dmg_pct;sling_dist_pct;return_always,,,,4,5,0.1,즉시: 1턴간 모든 공격을 무효화(무적)하고 · 2배의 반격 피해를 입히며 · 이동 거리가 1.5배 증가하고 공격 후 공격 전 제자리로 복귀한다.,0
|
||||||
lvbu_p,lvbu,passive,적토마,赤兎馬,always,sling_dist_pct,0,1.5,0,0,5,0.1,슬링 거리 +1.5 [절대 가산 키 없음 · p2 절대 가산 · 키 근사],0
|
lvbu_p,lvbu,passive,적토마 질주,,always,atk_pct;counter_taken_pct;matchup_floor;dash_dmg_pct,,,,0,5,0.1,모든 군영 유닛의 공격력 20% 증가하고 자신은 반격 받지 않으며 · 상성을 무시한 공격이 가능하고 이동 거리에 비례해 공격력 증가한다. (최대 100%),0
|
||||||
zhangjiao_a,zhangjiao,active,태평요술,太平妖術,instant,enemy_aggro_off_turn,1,,,5,5,0.1,즉시 ★: 이번 적 턴 적 전원 교전 반응 없음(안개) [대상 = 적 전원],1
|
zhangjiao_a,zhangjiao,active,태평요술,太平妖術,instant,invuln,,,,5,5,0.1,즉시: 자신의 모습을 안개에 숨겨 1턴 간 피해를 받지 않음,0
|
||||||
zhangjiao_p,zhangjiao,passive,창천이사,蒼天已死,always,dmg_taken_from_archer_pct,-0.3,,,0,5,0.1,적 궁병에게 받는 피해 −30 %,0
|
zhangjiao_p,zhangjiao,passive,창천이사,蒼天已死,always,dmg_taken_from_archer_pct;hit_poison,,,,0,5,0.1,궁병에게 받는 피해가 30 % 감소하며 · 자신의 공격에 명중 된 적에게 50% 확률로 중독 효과를 유발,0
|
||||||
huaxiong_a,huaxiong,active,참장,斬將,next_action,dmg_vs_general_mult,2,0,,3,5,0.1,다음 행동: 알까기 대상이 무장(영웅·보스)이면 피해 ×2 [p2 0=무장 전체(영웅·보스)],0
|
huaxiong_a,huaxiong,active,참장,斬將,next_action,dmg_pct;counter_taken_pct,,,,3,5,0.1,다음 행동의 피해량이 100% 증가하고 · 자신은 반격 피해를 받지 않는다.,0
|
||||||
huaxiong_p,huaxiong,passive,선봉,先鋒,always,dmg_pct_turn1,0.3,,,0,5,0.1,1턴째 공 +30 %,0
|
huaxiong_p,huaxiong,passive,선봉,先鋒,always,atk_pct;counter_taken_pct,,,,0,5,0.1,전투 시작 후 3턴 간 자신의 공격력 50% 증가하고 · 반격 피해가 50% 반감,0
|
||||||
yanliang_a,yanliang,active,백마의 창,,next_action,overwatch_radius_add,1.5,,,3,5,0.1,다음 행동: 명중 시 견제 반경 +1.5,0
|
yanliang_a,yanliang,active,백마의 창,,next_action,pierce_through;hit_bleed,,,,3,5,0.1,다음 행동은 적을 관통하며 경로상 모든 적에게 피해를 입히며 피해를 입힌 적에게 1턴간 출혈 효과 부여,0
|
||||||
yanliang_p,yanliang,passive,하북 명장,,always,heal_on_overwatch_kill,0.2,,,0,5,0.1,견제로 적을 쓰러뜨리면 HP 20 % 회복,0
|
yanliang_p,yanliang,passive,하북 명장,,always,counter_dmg_pct;counter_lifesteal,,,,0,5,0.1,반격 피해가 50% 증가하며 · 반격 피해의 30%만큼 생명력 회복,0
|
||||||
wenchou_a,wenchou,active,연진 돌격,,next_action,pierce_knockback_mult,2,,,3,5,0.1,다음 행동: 관통 밀기 ×2(경로를 비운다),0
|
wenchou_a,wenchou,active,연진 돌격,,next_action,dmg_pct;return_always,,,,3,5,0.1,다음 행동의 피해량이 50% 증가하고 · 공격 후 제자리로 복귀한다.,0
|
||||||
wenchou_p,wenchou,passive,복수,復讐,always,on_ally_death_atk,0.2,,,0,5,0.1,아군 1기 전사마다 공 +20 %(중첩) [중첩],0
|
wenchou_p,wenchou,passive,복수,復讐,always,heal_on_kill,,,,0,5,0.1,자신이 적을 쓰러뜨릴 경우 · 생명력 30% 회복,0
|
||||||
gongsunzan_a,gongsunzan,active,백마의종,白馬義從,next_action,pierce_knockback_mult,2,,,3,5,0.1,다음 행동: 관통으로 맞힌 적 전부 밀기 ×2,0
|
gongsunzan_a,gongsunzan,active,종횡무진,,next_action,sling_dist_pct;lifesteal;return_always,,,,3,5,0.1,다음 행동의 이동력이 50% 증가하고 · 피해의 50%만큼 생명력을 회복하며 공격 후 제자리로 복귀,0
|
||||||
gongsunzan_p,gongsunzan,passive,백마장사,,always,dmg_taken_from_archer_pct,-0.3,,,0,5,0.1,적 궁병에게 받는 피해 −30 %,0
|
gongsunzan_p,gongsunzan,passive,백마장사,,always,dmg_pct;heal_on_kill,,,,0,5,0.1,적 궁병에게 입히는 피해가 2배가 되며 · 적을 처치할 경우 생명력 30% 회복,0
|
||||||
dongzhuo_a,dongzhuo,active,폭정,暴政,instant,hp_cost_atk,0.8,0.2,,4,5,0.1,즉시 ★: 자기 HP 20 % 소모 · 이번 턴 공 +80 % [p1 공 증가 · p2 HP 소모],1
|
dongzhuo_a,dongzhuo,active,폭정,暴政,next_action,rampage,,,,4,5,0.1,다음 행동이 3초 동안 경로상 모든 유닛을 강하게 튕겨내며 돌진 피해를 입히지만 자신은 튕겨지지 않음(화면 가장자리에 닿을 경우에도 튕겨지며 핀볼처럼 동작),0
|
||||||
dongzhuo_p,dongzhuo,passive,비둔,肥鈍,always,hp_max_pct;knockback_taken_pct,0.4,-0.3,,0,5,0.1,최대 HP +40 % · 밀리는 거리 −30 %,0
|
dongzhuo_p,dongzhuo,passive,생명 강탈,,always,dmg_taken_pct;lifesteal,,,,0,5,0.1,모든 군영 유닛의 받는 피해를 10% 감소 시키며 · 자신이 입힌 피해의 30%만큼 생명력을 회복하고 · 아군 유닛이 1기가 줄어들 때마다 회복 효과는 5%씩 증가,0
|
||||||
lijue_a,lijue,active,장안 약탈,長安掠奪,next_action,pierce_knockback_mult,2,,,3,5,0.1,다음 행동: 관통 밀기 ×2,0
|
lijue_a,lijue,active,약탈,掠奪,next_action,splash_heal_lowest,,,,3,5,0.1,다음 행동으로 입힌 광역 피해의 50% 만큼 자신과 인접한 가장 생명력이 낮은 아군의 생명력을 회복,0
|
||||||
lijue_p,lijue,passive,서량 잔당,,always,on_ally_death_atk,0.1,,,0,5,0.1,아군 1기 전사마다 공 +10 %(중첩) [중첩],0
|
lijue_p,lijue,passive,혈맹,血盟,always,action_end_sacrifice,,,,0,5,0.1,행동 종료 시 자신의 생명력을 10% 차감한 후 인접한 아군의 공격력을 1턴간 50% 강화 (대상이 없을 경우 발동하지 않고 · 생명 10%이하가 되면 생명력 차감 없이 발동),0
|
||||||
yuanshao_a,yuanshao,active,사세삼공,四世三公,instant,team_dmg_pct_turn,0.2,,,4,5,0.1,즉시 ★: 이번 턴 아군 전원 가하는 피해 +20 % [신규 키],1
|
yuanshao_a,yuanshao,active,사세삼공,四世三公,instant,sling_dist_pct;crit_dmg_pct;crit_chance,,,,4,5,0.1,즉시: 1턴간 행동 범위 내 모든 아군의 이동력과 치명타 피해량이 50% 증가하며 · 치명타 발생 확률이 20% 증가,0
|
||||||
yuanshao_p,yuanshao,passive,명문,名門,always,dmg_taken_pct,-0.1,,5,0,5,0.1,출전 5인일 때 아군 전원 받는 피해 −10 % [p3 출전 인원 조건 · 대상 아군 전원],0
|
yuanshao_p,yuanshao,passive,명문의 위세,名門威勢,always,hp_max_pct;hit_bleed;hit_push,,,,0,5,0.1,모든 군영 영웅의 생명력이 20% 증가하고 · 자신의 공격이 치명타로 명중할 경우 대상에게 2턴간 출혈 효과를 유발한 후 최대 이동거리의 30%만큼 밀쳐냄(출혈 피해 추가),0
|
||||||
yuanshu_a,yuanshu,active,꿀물,蜜水,instant,heal_self_pct,0.3,,,4,5,0.1,즉시 ★: 자기 HP 30 % 회복 [신규 키],1
|
yuanshu_a,yuanshu,active,꿀물,蜜水,instant,heal_self_pct;dmg_taken_pct,,,,4,5,0.1,즉시 자신의 생명력을 60% 회복하고 · 1턴간 받는 피해 30% 감소,0
|
||||||
yuanshu_p,yuanshu,passive,옥새의 주인,,always,dmg_taken_pct,-0.2,2,,0,5,0.1,보스에게 받는 피해 −20 % [p2 2=보스에게서만],0
|
yuanshu_p,yuanshu,passive,옥새의 주인,,always,dmg_taken_pct;atk_pct;on_attacked_confuse,,,,0,5,0.1,받는 피해가 15% 증가하는 대신 자신 주위 3m 반경 내 아군의 공격력을 30% 증가시키고 자신을 공격한 대상에게 50% 확률로 혼란 효과 부여,0
|
||||||
diaochan_a,diaochan,active,연환계,連環計,instant,charm,4,1,,4,5,0.1,즉시 ★: 반경 4 안 가장 가까운 적 무장 1기(없으면 병사)를 매혹 — 다음 적 턴 행동 불가 [p1 반경 · p2 턴 · 대상 무장 우선 · 신규 키],1
|
diaochan_a,diaochan,active,미인계,,instant,charm_pull,,,,4,5,0.1,즉시: 행동 범위 내 적 남성 유닛을 1턴간 행동 불가 상태에 빠뜨리고 · 즉시 자신의 방향으로 강제 이동 (강제 이동 중 지형지물이나 유닛이 있을 경우 튕겨나가며 막힘),0
|
||||||
diaochan_p,diaochan,passive,폐월,閉月,always,dmg_taken_pct,-0.25,3,,0,5,0.1,근접 유닛에게 받는 피해 −25 % [p2 3=근접 유닛에게서만],0
|
diaochan_p,diaochan,passive,절세미인,,always,atk_pct;multi_male_stun;dmg_taken_pct,,,,0,5,0.1,모든 군영의 남성 유닛의 공격력이 25% 증가하며 · 자신의 공격이 두명 이상의 남성 유닛에게 명중 될 경우 50% 확률로 1턴간 기절하고 · 모든 남성 유닛에게 받는 피해 30% 감소,0
|
||||||
zhurong_a,zhurong,active,비도,飛刀,next_action,spear_hit_two;counter_taken_pct,2,-1,,3,5,0.1,다음 행동: 밀며 전진 끝에 닿은 적 2기 동시 피해 · 반격 받지 않음,0
|
zhurong_a,zhurong,active,비도,飛刀,next_action,shot_as_archer;atk_ratio;hit_poison,,,,3,5,0.1,다음 행동 시 궁병과 동일하게 공격력의 220%의 원거리 공격을 시전할 수 있으며 · 명중 된 대상에게 중독 효과를 유발 (중독 : 매턴 시작과 행동 종료 시점 마다 최대 생명력의 2%씩 감소),0
|
||||||
zhurong_p,zhurong,passive,남만의 여왕,,always,overwatch_radius_add,0.5,,,0,5,0.1,견제 반경 +0.5,0
|
zhurong_p,zhurong,passive,남만의 여왕,,always,lifesteal;counter_dmg_pct;terrain_immune;atk_pct,,,,0,5,0.1,입힌 피해의 20%만큼 생명력을 회복하고 · 반격 피해가 50% 증가하며 지형 효과에 이동력 감소 영향을 받지 않고 여성 영웅을 공격할 때 공격력 50% 증가,0
|
||||||
menghuo_a,menghuo,active,칠종칠금,七縱七擒,instant,heal_self_pct;dmg_taken_pct,0.3,-0.3,,4,5,0.1,즉시 ★: 자기 HP 30 % 회복 · 이번 턴 받는 피해 −30 % [p2 이번 턴 · 신규 키],1
|
menghuo_a,menghuo,active,남만왕,南蠻王,next_action,counter_taken_pct;self_bounce_mult;reaction_on_terrain,,,,4,5,0.1,다음 행동은 반격 피해를 받지 않으며 · 자신은 튕기지 않고 경로상 닿은 유닛만 튕겨낼 수 있고(아군 포함) · 지형물(바위 등)에 닿거나 이동력 감소 지형이 경로상 존재할 경우 한번 더 행동할 수 있음,0
|
||||||
menghuo_p,menghuo,passive,남만왕,南蠻王,always,hp_max_pct,0.3,,,0,5,0.1,최대 HP +30 %,0
|
menghuo_p,menghuo,passive,칠종칠금,七縱七擒,always,lifesteal;revive,,,,0,5,0.1,입힌 피해의 20%만큼 생명력을 회복하며 · 최대 생명이 60% 이상일 경우 사망해도 최대 생명력의 10%를 회복한 상태로 최대 7회까지 부활할 수 있지만 최대 생명력의 60% 이하일 때 사망한 경우 즉사,0
|
||||||
gaoshun_a,gaoshun,active,함진영,陷陣營,next_action,push_mult,2,0.3,,3,5,0.1,다음 행동: 밀기 ×2 · 밀린 적이 벽·유닛에 닿으면 +30 % 피해 [p2 벽·유닛 충돌 추가 피해 · 충돌 키 없음],0
|
gaoshun_a,gaoshun,active,함진영,陷陣營,next_action,hit_stun,,,,3,5,0.1,다음 행동으로 피해를 받은 대상은 1턴간 기절하며 · 아군을 밀쳐 적을 공격하거나 맵 오브젝트를 밀쳐 피해를 입힌 경우에도 피격 된 대상을 1턴 간 기절,0
|
||||||
gaoshun_p,gaoshun,passive,무언의 규율,,always,counter_ratio,1.0,,,0,5,0.1,반격 1.0 배,0
|
gaoshun_p,gaoshun,passive,함진 돌파,陷陣突破,always,push_continue,,,,0,5,0.1,아군이나 맵 오브젝트를 밀친 후 남은 이동력만큼 추가 행동 가능,0
|
||||||
zhanglu_a,zhanglu,active,오두미도,五斗米道,instant,heal_self_pct;dmg_taken_pct,0.25,-0.2,,4,5,0.1,즉시 ★: 자기 HP 25 % 회복 · 이번 턴 받는 피해 −20 % [p2 이번 턴 · 신규 키],1
|
zhanglu_a,zhanglu,active,오두미 독술,五斗米毒術,next_action,shot_single;atk_ratio;hit_poison;poison_zone,,,,4,5,0.1,다음 행동이 직선형 투사체로 전환되며 · 타격 된 적에게 공격력의 250%의 피해를 입히고 중독시키며 · 1턴간 유지되는 소규모 독지대를 형성,0
|
||||||
zhanglu_p,zhanglu,passive,한중의 교주,,always,splash_ally_ratio,0.5,,,0,5,0.1,광역 아군 피해 비율 0.5,0
|
zhanglu_p,zhanglu,passive,한중의 교주,,always,zombie,,,,0,5,0.1,자신이 처치한 대상이 1턴간 체력1 상태로 되살아나 적을 공격할 수 있음(1턴 후 강제로 소멸되며 · 조작 불가),0
|
||||||
yanbaihu_a,yanbaihu,active,동오의 덕왕,東吳德王,next_action,push_mult,1.5,,,3,5,0.1,다음 행동: 알까기 밀기 ×1.5,0
|
yanbaihu_a,yanbaihu,active,동오의 덕왕,東吳德王,next_action,atk_pct;crit_chance,,,,3,5,0.1,다음 행동 시 공격력이 30% 증가하고 · 치명타 확률 15% 상승,0
|
||||||
yanbaihu_p,yanbaihu,passive,산적 두목,,always,hp_max_pct,0.1,,,0,5,0.1,최대 HP +10 %,0
|
yanbaihu_p,yanbaihu,passive,산적 두목,,always,hp_max_pct,,,,0,5,0.1,최대 HP+10% 증가,0
|
||||||
hanxuan_a,hanxuan,active,장사 태수,長沙太守,next_action,overwatch_radius_add,0.5,,,3,5,0.1,다음 행동: 명중 시 견제 반경 +0.5,0
|
hanxuan_a,hanxuan,active,장사 태수,長沙太守,next_action,counter_taken_pct;push_mult,,,,3,5,0.1,다음 행동 시 반격 받지 않으며 · 2배 더 먼거리를 밀쳐낸다.,0
|
||||||
hanxuan_p,hanxuan,passive,의심,疑心,always,counter_taken_pct,-0.2,,,0,5,0.1,받는 반격 피해 −20 %,0
|
hanxuan_p,hanxuan,passive,의심,疑心,always,counter_taken_pct,,,,0,5,0.1,받는 반격 피해 −20 %,0
|
||||||
jinxuan_a,jinxuan,active,무릉의 화살,武陵之矢,next_action,range_add,0.5,,,3,5,0.1,다음 행동: 사거리 +0.5,0
|
jinxuan_a,jinxuan,active,무릉의 화살,武陵之矢,next_action,crit_chance;crit_dmg_pct,,,,3,5,0.1,다음 행동 시 치명타 확률이 30% · 치명타 피해량이 50% 증가,0
|
||||||
jinxuan_p,jinxuan,passive,성주,城主,always,dmg_taken_from_archer_pct,-0.15,,,0,5,0.1,적 궁병에게 받는 피해 −15 %,0
|
jinxuan_p,jinxuan,passive,성주,城主,always,dmg_taken_from_archer_pct;dmg_pct,,,,0,5,0.1,적 궁병에게 받는 피해는 15 % 감소하고 · 입히는 피해는 15% 증가,0
|
||||||
zhaofan_a,zhaofan,active,계양의 계,桂陽之計,next_action,splash_radius_mult,1.3,,,3,5,0.1,다음 행동: 광역 반경 ×1.3(광역),1
|
zhaofan_a,zhaofan,active,계양의 계,桂陽之計,next_action,shot_pierce;ally_hit_heal,,,,3,5,0.1,다음 행동의 공격이 직선 형태로 변형되고 · 대상이 아군일 경우 공격력의 60%만큼 생명력을 회복하고 · 공격 대상이 적군일 경우 공격력의 160%만큼 피해를 입힘,0
|
||||||
zhaofan_p,zhaofan,passive,처세,處世,always,dmg_taken_pct,-0.1,3,,0,5,0.1,근접 유닛에게 받는 피해 −10 % [p2 3=근접 유닛에게서만],0
|
zhaofan_p,zhaofan,passive,처세,處世,always,negate_hits,,,,0,5,0.1,전투 시작 후 발생 된 어떤 피해든 1회를 무효화,0
|
||||||
zhangren_a,zhangren,active,낙봉파의 화살,落鳳坡,next_action,dmg_vs_general_mult,1.5,0,,3,5,0.1,다음 행동: 무장(영웅·보스) 상대 피해 ×1.5 [p2 0=무장 전체],0
|
zhangren_a,zhangren,active,낙봉파의 화살,落鳳坡,next_action,shot_pierce,,,,3,5,0.1,다음 행동의 공격은 아군을 무시하며 직선 범위 내 적을 공격력의 150%의 피해로 공격 가능,0
|
||||||
zhangren_p,zhangren,passive,충신,忠臣,always,range_add,0.3,,,0,5,0.1,사거리 +0.3,0
|
zhangren_p,zhangren,passive,매복 사수,埋伏射手,always,range_mult,,,,0,5,0.1,자신의 공격 사거리 30% 증가,0
|
||||||
chunyuqiong_a,chunyuqiong,active,오소 돌격,烏巢突擊,next_action,pierce_knockback_mult,1.5,,,3,5,0.1,다음 행동: 관통 밀기 ×1.5,0
|
chunyuqiong_a,chunyuqiong,active,오소 돌격,烏巢突擊,next_action,return_on_hit,,,,3,5,0.1,다음 행동으로 공격 행동 시 제자리로 복귀 가능,0
|
||||||
chunyuqiong_p,chunyuqiong,passive,하북 숙장,宿將,always,dmg_pct_hp_below,0.3,0.3,,0,5,0.1,HP 30 % 이하일 때 공 +30 % [p1 공 증가 · p2 HP 임계],0
|
chunyuqiong_p,chunyuqiong,passive,하북 숙장,宿將,always,atk_missing_hp,,,,0,5,0.1,자신의 HP가 1% 감소할 때마다 이하일 때 공격력이 1% 증가,0
|
||||||
|
|
|
||||||
|
|
|
@ -27,6 +27,7 @@ MonoBehaviour:
|
||||||
skillPassive: liubei_p
|
skillPassive: liubei_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 1-1
|
firstStage: 1-1
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -74,6 +75,7 @@ MonoBehaviour:
|
||||||
skillPassive: guanyu_p
|
skillPassive: guanyu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 1-1
|
firstStage: 1-1
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -121,6 +123,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhangfei_p
|
skillPassive: zhangfei_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 1-1
|
firstStage: 1-1
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -168,6 +171,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhaoyun_p
|
skillPassive: zhaoyun_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-3
|
firstStage: 3-3
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -215,6 +219,7 @@ MonoBehaviour:
|
||||||
skillPassive: huangzhong_p
|
skillPassive: huangzhong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -262,6 +267,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhugeliang_p
|
skillPassive: zhugeliang_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 8-13
|
firstStage: 8-13
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -309,6 +315,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhoucang_p
|
skillPassive: zhoucang_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 7-2
|
firstStage: 7-2
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -356,6 +363,7 @@ MonoBehaviour:
|
||||||
skillPassive: guanping_p
|
skillPassive: guanping_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 7-7
|
firstStage: 7-7
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -403,6 +411,7 @@ MonoBehaviour:
|
||||||
skillPassive: xushu_p
|
skillPassive: xushu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 8-6
|
firstStage: 8-6
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -450,6 +459,7 @@ MonoBehaviour:
|
||||||
skillPassive: taishici_p
|
skillPassive: taishici_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-8
|
firstStage: 3-8
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -497,6 +507,7 @@ MonoBehaviour:
|
||||||
skillPassive: machao_p
|
skillPassive: machao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -544,6 +555,7 @@ MonoBehaviour:
|
||||||
skillPassive: weiyan_p
|
skillPassive: weiyan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -591,6 +603,7 @@ MonoBehaviour:
|
||||||
skillPassive: pangtong_p
|
skillPassive: pangtong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -638,6 +651,7 @@ MonoBehaviour:
|
||||||
skillPassive: jiangwei_p
|
skillPassive: jiangwei_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -685,6 +699,7 @@ MonoBehaviour:
|
||||||
skillPassive: liufeng_p
|
skillPassive: liufeng_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 8-11
|
firstStage: 8-11
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -732,6 +747,7 @@ MonoBehaviour:
|
||||||
skillPassive: jianyong_p
|
skillPassive: jianyong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-12
|
firstStage: 3-12
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -779,6 +795,7 @@ MonoBehaviour:
|
||||||
skillPassive: sunqian_p
|
skillPassive: sunqian_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-12
|
firstStage: 3-12
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -826,6 +843,7 @@ MonoBehaviour:
|
||||||
skillPassive: mizhu_p
|
skillPassive: mizhu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-12
|
firstStage: 3-12
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -873,6 +891,7 @@ MonoBehaviour:
|
||||||
skillPassive: caocao_p
|
skillPassive: caocao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 1-8
|
firstStage: 1-8
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -920,6 +939,7 @@ MonoBehaviour:
|
||||||
skillPassive: xiahoudun_p
|
skillPassive: xiahoudun_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 5-14
|
firstStage: 5-14
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -967,6 +987,7 @@ MonoBehaviour:
|
||||||
skillPassive: xuchu_p
|
skillPassive: xuchu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 5-15
|
firstStage: 5-15
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1014,6 +1035,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhangliao_p
|
skillPassive: zhangliao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 4-14
|
firstStage: 4-14
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1061,6 +1083,7 @@ MonoBehaviour:
|
||||||
skillPassive: xuhuang_p
|
skillPassive: xuhuang_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 5-15
|
firstStage: 5-15
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1108,6 +1131,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhanghe_p
|
skillPassive: zhanghe_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 7-9
|
firstStage: 7-9
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1155,6 +1179,7 @@ MonoBehaviour:
|
||||||
skillPassive: caoren_p
|
skillPassive: caoren_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 8-10
|
firstStage: 8-10
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1202,6 +1227,7 @@ MonoBehaviour:
|
||||||
skillPassive: yujin_p
|
skillPassive: yujin_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-15
|
firstStage: 3-15
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1249,6 +1275,7 @@ MonoBehaviour:
|
||||||
skillPassive: simayi_p
|
skillPassive: simayi_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1296,6 +1323,7 @@ MonoBehaviour:
|
||||||
skillPassive: xiahouyuan_p
|
skillPassive: xiahouyuan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1343,6 +1371,7 @@ MonoBehaviour:
|
||||||
skillPassive: guojia_p
|
skillPassive: guojia_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1390,6 +1419,7 @@ MonoBehaviour:
|
||||||
skillPassive: jiaxu_p
|
skillPassive: jiaxu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1437,6 +1467,7 @@ MonoBehaviour:
|
||||||
skillPassive: dianwei_p
|
skillPassive: dianwei_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1484,6 +1515,7 @@ MonoBehaviour:
|
||||||
skillPassive: xunyu_p
|
skillPassive: xunyu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1531,6 +1563,7 @@ MonoBehaviour:
|
||||||
skillPassive: xiahoumao_p
|
skillPassive: xiahoumao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1578,6 +1611,7 @@ MonoBehaviour:
|
||||||
skillPassive: wanglang_p
|
skillPassive: wanglang_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1625,6 +1659,7 @@ MonoBehaviour:
|
||||||
skillPassive: xiahouen_p
|
skillPassive: xiahouen_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 9-8
|
firstStage: 9-8
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1672,6 +1707,7 @@ MonoBehaviour:
|
||||||
skillPassive: sunjian_p
|
skillPassive: sunjian_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 2-3
|
firstStage: 2-3
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1719,6 +1755,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhouyu_p
|
skillPassive: zhouyu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 10-3
|
firstStage: 10-3
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1766,6 +1803,7 @@ MonoBehaviour:
|
||||||
skillPassive: huanggai_p
|
skillPassive: huanggai_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 10-7
|
firstStage: 10-7
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1813,6 +1851,7 @@ MonoBehaviour:
|
||||||
skillPassive: ganning_p
|
skillPassive: ganning_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 10-3
|
firstStage: 10-3
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1860,6 +1899,7 @@ MonoBehaviour:
|
||||||
skillPassive: chengpu_p
|
skillPassive: chengpu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 2-3
|
firstStage: 2-3
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1907,6 +1947,7 @@ MonoBehaviour:
|
||||||
skillPassive: sunce_p
|
skillPassive: sunce_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -1954,6 +1995,7 @@ MonoBehaviour:
|
||||||
skillPassive: sunquan_p
|
skillPassive: sunquan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2001,6 +2043,7 @@ MonoBehaviour:
|
||||||
skillPassive: lusu_p
|
skillPassive: lusu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2048,6 +2091,7 @@ MonoBehaviour:
|
||||||
skillPassive: lvmeng_p
|
skillPassive: lvmeng_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2095,6 +2139,7 @@ MonoBehaviour:
|
||||||
skillPassive: luxun_p
|
skillPassive: luxun_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2142,6 +2187,7 @@ MonoBehaviour:
|
||||||
skillPassive: sunshangxiang_p
|
skillPassive: sunshangxiang_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: f
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2189,6 +2235,7 @@ MonoBehaviour:
|
||||||
skillPassive: xiaoqiao_p
|
skillPassive: xiaoqiao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: f
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2236,6 +2283,7 @@ MonoBehaviour:
|
||||||
skillPassive: lingtong_p
|
skillPassive: lingtong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2283,6 +2331,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhoutai_p
|
skillPassive: zhoutai_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2330,6 +2379,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhugejin_p
|
skillPassive: zhugejin_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2377,6 +2427,7 @@ MonoBehaviour:
|
||||||
skillPassive: dongxi_p
|
skillPassive: dongxi_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2424,6 +2475,7 @@ MonoBehaviour:
|
||||||
skillPassive: lvbu_p
|
skillPassive: lvbu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 2-10
|
firstStage: 2-10
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2471,6 +2523,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhangjiao_p
|
skillPassive: zhangjiao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 1-10
|
firstStage: 1-10
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2518,6 +2571,7 @@ MonoBehaviour:
|
||||||
skillPassive: huaxiong_p
|
skillPassive: huaxiong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 2-5
|
firstStage: 2-5
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2565,6 +2619,7 @@ MonoBehaviour:
|
||||||
skillPassive: yanliang_p
|
skillPassive: yanliang_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 6-5
|
firstStage: 6-5
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2612,6 +2667,7 @@ MonoBehaviour:
|
||||||
skillPassive: wenchou_p
|
skillPassive: wenchou_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 3-3
|
firstStage: 3-3
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2659,6 +2715,7 @@ MonoBehaviour:
|
||||||
skillPassive: gongsunzan_p
|
skillPassive: gongsunzan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 2-1
|
firstStage: 2-1
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2706,6 +2763,7 @@ MonoBehaviour:
|
||||||
skillPassive: dongzhuo_p
|
skillPassive: dongzhuo_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2753,6 +2811,7 @@ MonoBehaviour:
|
||||||
skillPassive: lijue_p
|
skillPassive: lijue_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2800,6 +2859,7 @@ MonoBehaviour:
|
||||||
skillPassive: yuanshao_p
|
skillPassive: yuanshao_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2847,6 +2907,7 @@ MonoBehaviour:
|
||||||
skillPassive: yuanshu_p
|
skillPassive: yuanshu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2894,6 +2955,7 @@ MonoBehaviour:
|
||||||
skillPassive: diaochan_p
|
skillPassive: diaochan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: f
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2941,6 +3003,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhurong_p
|
skillPassive: zhurong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: f
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -2988,6 +3051,7 @@ MonoBehaviour:
|
||||||
skillPassive: menghuo_p
|
skillPassive: menghuo_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3035,6 +3099,7 @@ MonoBehaviour:
|
||||||
skillPassive: gaoshun_p
|
skillPassive: gaoshun_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage: 4-10
|
firstStage: 4-10
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3082,6 +3147,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhanglu_p
|
skillPassive: zhanglu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3129,6 +3195,7 @@ MonoBehaviour:
|
||||||
skillPassive: yanbaihu_p
|
skillPassive: yanbaihu_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3176,6 +3243,7 @@ MonoBehaviour:
|
||||||
skillPassive: hanxuan_p
|
skillPassive: hanxuan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3223,6 +3291,7 @@ MonoBehaviour:
|
||||||
skillPassive: jinxuan_p
|
skillPassive: jinxuan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3270,6 +3339,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhaofan_p
|
skillPassive: zhaofan_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3317,6 +3387,7 @@ MonoBehaviour:
|
||||||
skillPassive: zhangren_p
|
skillPassive: zhangren_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3364,6 +3435,7 @@ MonoBehaviour:
|
||||||
skillPassive: chunyuqiong_p
|
skillPassive: chunyuqiong_p
|
||||||
isSoldier: 0
|
isSoldier: 0
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear:
|
gear:
|
||||||
- grade: green
|
- grade: green
|
||||||
slots:
|
slots:
|
||||||
|
|
@ -3411,6 +3483,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_infantry_2
|
- id: soldier_infantry_2
|
||||||
nameKo: "\uC601\uC6C5 \uBCF4\uBCD1"
|
nameKo: "\uC601\uC6C5 \uBCF4\uBCD1"
|
||||||
|
|
@ -3426,6 +3499,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_infantry_3
|
- id: soldier_infantry_3
|
||||||
nameKo: "\uC804\uC124 \uBCF4\uBCD1"
|
nameKo: "\uC804\uC124 \uBCF4\uBCD1"
|
||||||
|
|
@ -3441,6 +3515,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_spear_1
|
- id: soldier_spear_1
|
||||||
nameKo: "\uC815\uC608 \uCC3D\uBCD1"
|
nameKo: "\uC815\uC608 \uCC3D\uBCD1"
|
||||||
|
|
@ -3456,6 +3531,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_spear_2
|
- id: soldier_spear_2
|
||||||
nameKo: "\uC601\uC6C5 \uCC3D\uBCD1"
|
nameKo: "\uC601\uC6C5 \uCC3D\uBCD1"
|
||||||
|
|
@ -3471,6 +3547,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_spear_3
|
- id: soldier_spear_3
|
||||||
nameKo: "\uC804\uC124 \uCC3D\uBCD1"
|
nameKo: "\uC804\uC124 \uCC3D\uBCD1"
|
||||||
|
|
@ -3486,6 +3563,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_cavalry_1
|
- id: soldier_cavalry_1
|
||||||
nameKo: "\uC815\uC608 \uAE30\uBCD1"
|
nameKo: "\uC815\uC608 \uAE30\uBCD1"
|
||||||
|
|
@ -3501,6 +3579,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_cavalry_2
|
- id: soldier_cavalry_2
|
||||||
nameKo: "\uC601\uC6C5 \uAE30\uBCD1"
|
nameKo: "\uC601\uC6C5 \uAE30\uBCD1"
|
||||||
|
|
@ -3516,6 +3595,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_cavalry_3
|
- id: soldier_cavalry_3
|
||||||
nameKo: "\uC804\uC124 \uAE30\uBCD1"
|
nameKo: "\uC804\uC124 \uAE30\uBCD1"
|
||||||
|
|
@ -3531,6 +3611,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_archer_1
|
- id: soldier_archer_1
|
||||||
nameKo: "\uC815\uC608 \uAD81\uBCD1"
|
nameKo: "\uC815\uC608 \uAD81\uBCD1"
|
||||||
|
|
@ -3546,6 +3627,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_archer_2
|
- id: soldier_archer_2
|
||||||
nameKo: "\uC601\uC6C5 \uAD81\uBCD1"
|
nameKo: "\uC601\uC6C5 \uAD81\uBCD1"
|
||||||
|
|
@ -3561,6 +3643,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_archer_3
|
- id: soldier_archer_3
|
||||||
nameKo: "\uC804\uC124 \uAD81\uBCD1"
|
nameKo: "\uC804\uC124 \uAD81\uBCD1"
|
||||||
|
|
@ -3576,6 +3659,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_strategist_1
|
- id: soldier_strategist_1
|
||||||
nameKo: "\uC815\uC608 \uCC45\uC0AC"
|
nameKo: "\uC815\uC608 \uCC45\uC0AC"
|
||||||
|
|
@ -3591,6 +3675,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_strategist_2
|
- id: soldier_strategist_2
|
||||||
nameKo: "\uC601\uC6C5 \uCC45\uC0AC"
|
nameKo: "\uC601\uC6C5 \uCC45\uC0AC"
|
||||||
|
|
@ -3606,6 +3691,7 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
- id: soldier_strategist_3
|
- id: soldier_strategist_3
|
||||||
nameKo: "\uC804\uC124 \uCC45\uC0AC"
|
nameKo: "\uC804\uC124 \uCC45\uC0AC"
|
||||||
|
|
@ -3621,4 +3707,5 @@ MonoBehaviour:
|
||||||
skillPassive:
|
skillPassive:
|
||||||
isSoldier: 1
|
isSoldier: 1
|
||||||
firstStage:
|
firstStage:
|
||||||
|
gender: m
|
||||||
gear: []
|
gear: []
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -31,6 +31,13 @@ namespace TKS.OutGame
|
||||||
[Tooltip("병사 유닛 = 승급·진화 없음 · 중복 = 골드")]
|
[Tooltip("병사 유닛 = 승급·진화 없음 · 중복 = 골드")]
|
||||||
public bool isSoldier;
|
public bool isSoldier;
|
||||||
public string firstStage;
|
public string firstStage;
|
||||||
|
[Tooltip("m|f — 스킬 조건(남성·여성 영웅 · 2026-11-10 #1112)")]
|
||||||
|
public string gender = "m";
|
||||||
|
|
||||||
|
public bool Female
|
||||||
|
{
|
||||||
|
get { return gender == "f"; }
|
||||||
|
}
|
||||||
public List<GearSet> gear = new List<GearSet>();
|
public List<GearSet> gear = new List<GearSet>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,8 +60,33 @@ namespace TKS.OutGame
|
||||||
public int gold;
|
public int gold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>skill_effects.csv 1행 = 스킬 효과 1줄(2026-11-10 #1112 · 키 · 대상 · 조건 · 지속 · p1~p3 · 뜻 = Runtime/Skills/SkillKeys).</summary>
|
||||||
|
[Serializable]
|
||||||
|
public class Effect
|
||||||
|
{
|
||||||
|
public string skillId;
|
||||||
|
public string key;
|
||||||
|
[Tooltip("빈칸 = 자신 · allies · camp:<진영>[:<병종>] · camp_male:<진영> · radius:<r> · range · aura")]
|
||||||
|
public string target;
|
||||||
|
[Tooltip("crit · vs_status · vs_bleed · vs_confused · vs_stunned · vs_type:<병종> · vs_male · vs_male_hero · vs_female_hero · turn_le:<n> · armed")]
|
||||||
|
public string cond;
|
||||||
|
[Tooltip("0 = 상시·장착 중·1회 · n = n 라운드")]
|
||||||
|
public int dur;
|
||||||
|
public float p1;
|
||||||
|
public float p2;
|
||||||
|
public float p3;
|
||||||
|
public int pMask;
|
||||||
|
public string note;
|
||||||
|
|
||||||
|
public bool HasP(int i)
|
||||||
|
{
|
||||||
|
return (pMask & (1 << i)) != 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public List<Skill> skills = new List<Skill>();
|
public List<Skill> skills = new List<Skill>();
|
||||||
public List<LevelCost> levels = new List<LevelCost>();
|
public List<LevelCost> levels = new List<LevelCost>();
|
||||||
|
public List<Effect> effects = new List<Effect>();
|
||||||
|
|
||||||
public Skill Get(string id)
|
public Skill Get(string id)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ namespace TKS.OutGame
|
||||||
{
|
{
|
||||||
"heroes.csv", "skills.csv", "hero_levels.csv", "promotion.csv", "evolve.csv", "gear_items.csv",
|
"heroes.csv", "skills.csv", "hero_levels.csv", "promotion.csv", "evolve.csv", "gear_items.csv",
|
||||||
"hero_gear.csv", "skill_levels.csv", "gacha.csv", "gacha_pool.csv", "stage_rewards.csv", "economy.csv",
|
"hero_gear.csv", "skill_levels.csv", "gacha.csv", "gacha_pool.csv", "stage_rewards.csv", "economy.csv",
|
||||||
|
"skill_effects.csv", // 스킬 효과 1줄 표(2026-11-10 #1112)
|
||||||
// C 단계(PD #1093 C)
|
// C 단계(PD #1093 C)
|
||||||
"training.csv", "training_ranks.csv", "patrol.csv", "dev_tree.csv", "treasures.csv", "treasure_sets.csv", "chests.csv", "tower.csv", "dungeons.csv", "arena.csv",
|
"training.csv", "training_ranks.csv", "patrol.csv", "dev_tree.csv", "treasures.csv", "treasure_sets.csv", "chests.csv", "tower.csv", "dungeons.csv", "arena.csv",
|
||||||
};
|
};
|
||||||
|
|
@ -128,7 +129,7 @@ namespace TKS.OutGame
|
||||||
d.Evolve = ParseEvolve(rows["evolve.csv"], e);
|
d.Evolve = ParseEvolve(rows["evolve.csv"], e);
|
||||||
d.Levels = ParseLevels(rows["hero_levels.csv"], e);
|
d.Levels = ParseLevels(rows["hero_levels.csv"], e);
|
||||||
d.Gear = ParseGear(rows["gear_items.csv"], e);
|
d.Gear = ParseGear(rows["gear_items.csv"], e);
|
||||||
d.Skills = ParseSkills(rows["skills.csv"], rows["skill_levels.csv"], e);
|
d.Skills = ParseSkills(rows["skills.csv"], rows["skill_levels.csv"], rows["skill_effects.csv"], e);
|
||||||
d.Heroes = ParseHeroes(rows["heroes.csv"], rows["hero_gear.csv"], d, e);
|
d.Heroes = ParseHeroes(rows["heroes.csv"], rows["hero_gear.csv"], d, e);
|
||||||
d.Gacha = ParseGacha(rows["gacha.csv"], rows["gacha_pool.csv"], d, e);
|
d.Gacha = ParseGacha(rows["gacha.csv"], rows["gacha_pool.csv"], d, e);
|
||||||
d.Rewards = ParseRewards(rows["stage_rewards.csv"], d, e);
|
d.Rewards = ParseRewards(rows["stage_rewards.csv"], d, e);
|
||||||
|
|
@ -231,9 +232,48 @@ namespace TKS.OutGame
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SkillInfoDb ParseSkills(List<Dictionary<string, string>> rows, List<Dictionary<string, string>> lvRows, List<string> e)
|
static SkillInfoDb ParseSkills(List<Dictionary<string, string>> rows, List<Dictionary<string, string>> lvRows, List<Dictionary<string, string>> fxRows, List<string> e)
|
||||||
{
|
{
|
||||||
var t = ScriptableObject.CreateInstance<SkillInfoDb>();
|
var t = ScriptableObject.CreateInstance<SkillInfoDb>();
|
||||||
|
// 효과 1줄 표(2026-11-10 #1112) — 키 · 대상 · 조건 · 지속 · p1~p3 · 스킬마다 1줄 이상
|
||||||
|
const string fx = "skill_effects.csv";
|
||||||
|
var fxKeys = new Dictionary<string, List<string>>();
|
||||||
|
if (Cols(fxRows, fx, e, "skill_id", "key", "target", "cond", "dur", "p1", "p2", "p3", "note"))
|
||||||
|
{
|
||||||
|
foreach (var r in fxRows)
|
||||||
|
{
|
||||||
|
var x = new SkillInfoDb.Effect
|
||||||
|
{
|
||||||
|
skillId = Get(r, "skill_id"), key = Get(r, "key"), target = Get(r, "target"), cond = Get(r, "cond"), note = Get(r, "note"),
|
||||||
|
dur = Get(r, "dur").Length == 0 ? 0 : Int(r, "dur", fx, e),
|
||||||
|
};
|
||||||
|
string at = fx + " 행 " + Get(r, "#") + " " + x.skillId + "/" + x.key;
|
||||||
|
if (!SkillKeys.Known(x.key)) e.Add(at + ": 처리기 없는 키");
|
||||||
|
var pc = new[] { "p1", "p2", "p3" };
|
||||||
|
var pv = new float[3];
|
||||||
|
for (int i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
var v = Get(r, pc[i]);
|
||||||
|
if (v.Length == 0) continue;
|
||||||
|
if (!float.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out pv[i])) e.Add(at + ": " + pc[i] + " 숫자 아님(「" + v + "」)");
|
||||||
|
else x.pMask |= 1 << i;
|
||||||
|
}
|
||||||
|
x.p1 = pv[0];
|
||||||
|
x.p2 = pv[1];
|
||||||
|
x.p3 = pv[2];
|
||||||
|
if (!x.HasP(0)) e.Add(at + ": p1 비었음");
|
||||||
|
if (x.dur < 0) e.Add(at + ": dur < 0");
|
||||||
|
var probe = new SkillEffect();
|
||||||
|
var te = SkillEffect.ParseTarget(x.target, probe);
|
||||||
|
if (te != null) e.Add(at + ": target " + te);
|
||||||
|
var ce = SkillEffect.ParseCond(x.cond, probe);
|
||||||
|
if (ce != null) e.Add(at + ": cond " + ce);
|
||||||
|
List<string> l;
|
||||||
|
if (!fxKeys.TryGetValue(x.skillId, out l)) fxKeys[x.skillId] = l = new List<string>();
|
||||||
|
if (!l.Contains(x.key)) l.Add(x.key);
|
||||||
|
t.effects.Add(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
const string f = "skills.csv";
|
const string f = "skills.csv";
|
||||||
if (Cols(rows, f, e, "id", "hero_id", "kind", "name_ko", "hanja", "trigger", "effect_key", "p1", "p2", "p3", "cooldown", "lv_max", "lv_scale", "desc_ko", "needs_impl"))
|
if (Cols(rows, f, e, "id", "hero_id", "kind", "name_ko", "hanja", "trigger", "effect_key", "p1", "p2", "p3", "cooldown", "lv_max", "lv_scale", "desc_ko", "needs_impl"))
|
||||||
{
|
{
|
||||||
|
|
@ -268,13 +308,11 @@ namespace TKS.OutGame
|
||||||
s.p1 = ps[0];
|
s.p1 = ps[0];
|
||||||
s.p2 = ps[1];
|
s.p2 = ps[1];
|
||||||
s.p3 = ps[2];
|
s.p3 = ps[2];
|
||||||
|
// effect_key 칸 = 효과 표(skill_effects.csv) 키 목록(;) — 수치는 효과 표에만(2026-11-10 #1112)
|
||||||
var keys = s.EffectKeys;
|
var keys = s.EffectKeys;
|
||||||
if (keys.Length == 0 || keys.Length > 3) e.Add(at + ": effect_key 수 " + keys.Length + " (1~3)");
|
List<string> fk;
|
||||||
for (int i = 0; i < keys.Length; i++)
|
if (!fxKeys.TryGetValue(s.id, out fk)) e.Add(at + ": skill_effects.csv 에 효과 줄 없음");
|
||||||
{
|
else if (string.Join(";", keys) != string.Join(";", fk.ToArray())) e.Add(at + ": effect_key 「" + s.effectKey + "」 ≠ 효과 표 키 「" + string.Join(";", fk.ToArray()) + "」");
|
||||||
if (!SkillKeys.Known(keys[i])) e.Add(at + ": effect_key 「" + keys[i] + "」 처리기 없음");
|
|
||||||
if (i < 3 && !s.HasP(i) && (keys.Length > 1 || i == 0)) e.Add(at + ": effect_key 「" + keys[i] + "」 의 p" + (i + 1) + " 비었음");
|
|
||||||
}
|
|
||||||
if (active && s.cooldown < 1) e.Add(at + ": 액티브 cooldown < 1");
|
if (active && s.cooldown < 1) e.Add(at + ": 액티브 cooldown < 1");
|
||||||
if (s.lvMax < 1) e.Add(at + ": lv_max < 1");
|
if (s.lvMax < 1) e.Add(at + ": lv_max < 1");
|
||||||
t.skills.Add(s);
|
t.skills.Add(s);
|
||||||
|
|
@ -288,6 +326,8 @@ namespace TKS.OutGame
|
||||||
if (t.levels[i].lv != i + 1) e.Add(g + ": lv 가 1부터 연속이 아님(" + (i + 1) + "번째 = " + t.levels[i].lv + ")");
|
if (t.levels[i].lv != i + 1) e.Add(g + ": lv 가 1부터 연속이 아님(" + (i + 1) + "번째 = " + t.levels[i].lv + ")");
|
||||||
foreach (var s in t.skills)
|
foreach (var s in t.skills)
|
||||||
if (s.lvMax > t.levels.Count) e.Add(f + ": " + s.id + " lv_max " + s.lvMax + " > skill_levels.csv 행 " + t.levels.Count);
|
if (s.lvMax > t.levels.Count) e.Add(f + ": " + s.id + " lv_max " + s.lvMax + " > skill_levels.csv 행 " + t.levels.Count);
|
||||||
|
foreach (var x in t.effects)
|
||||||
|
if (t.Get(x.skillId) == null) e.Add(fx + ": " + x.skillId + " 이 skills.csv 에 없음");
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -295,7 +335,7 @@ namespace TKS.OutGame
|
||||||
{
|
{
|
||||||
var t = ScriptableObject.CreateInstance<HeroDb>();
|
var t = ScriptableObject.CreateInstance<HeroDb>();
|
||||||
const string f = "heroes.csv";
|
const string f = "heroes.csv";
|
||||||
if (!Cols(rows, f, e, "id", "name_ko", "hanja", "faction", "unit_type", "rarity", "hp_mult", "atk_mult", "def_mult", "art", "skill_active", "skill_passive", "is_soldier", "first_stage")) return t;
|
if (!Cols(rows, f, e, "id", "name_ko", "hanja", "faction", "unit_type", "rarity", "hp_mult", "atk_mult", "def_mult", "art", "skill_active", "skill_passive", "is_soldier", "first_stage", "gender")) return t;
|
||||||
var seen = new HashSet<string>();
|
var seen = new HashSet<string>();
|
||||||
foreach (var r in rows)
|
foreach (var r in rows)
|
||||||
{
|
{
|
||||||
|
|
@ -308,7 +348,10 @@ namespace TKS.OutGame
|
||||||
id = Get(r, "id"), nameKo = Get(r, "name_ko"), hanja = Get(r, "hanja"), faction = Get(r, "faction"), type = type,
|
id = Get(r, "id"), nameKo = Get(r, "name_ko"), hanja = Get(r, "hanja"), faction = Get(r, "faction"), type = type,
|
||||||
rarity = Int(r, "rarity", f, e), hpMult = Float(r, "hp_mult", f, e), atkMult = Float(r, "atk_mult", f, e), defMult = Float(r, "def_mult", f, e),
|
rarity = Int(r, "rarity", f, e), hpMult = Float(r, "hp_mult", f, e), atkMult = Float(r, "atk_mult", f, e), defMult = Float(r, "def_mult", f, e),
|
||||||
art = Get(r, "art"), skillActive = Get(r, "skill_active"), skillPassive = Get(r, "skill_passive"), isSoldier = soldier == "1", firstStage = Get(r, "first_stage"),
|
art = Get(r, "art"), skillActive = Get(r, "skill_active"), skillPassive = Get(r, "skill_passive"), isSoldier = soldier == "1", firstStage = Get(r, "first_stage"),
|
||||||
|
gender = Get(r, "gender") == "f" ? "f" : "m",
|
||||||
};
|
};
|
||||||
|
var gd = Get(r, "gender");
|
||||||
|
if (gd != "m" && gd != "f") e.Add(f + " 행 " + Get(r, "#") + ": gender 「" + gd + "」 ≠ m|f");
|
||||||
if (!Unique(seen, h.id, f, r, e)) continue;
|
if (!Unique(seen, h.id, f, r, e)) continue;
|
||||||
if (h.rarity < 1 || h.rarity > 3) e.Add(f + ": " + h.id + " rarity " + h.rarity + " ≠ 1~3");
|
if (h.rarity < 1 || h.rarity > 3) e.Add(f + ": " + h.id + " rarity " + h.rarity + " ≠ 1~3");
|
||||||
foreach (var sid in new[] { h.skillActive, h.skillPassive })
|
foreach (var sid in new[] { h.skillActive, h.skillPassive })
|
||||||
|
|
|
||||||
|
|
@ -46,20 +46,11 @@ namespace TKS
|
||||||
public bool Acted, Bonus, Overwatch, Collider, Active;
|
public bool Acted, Bonus, Overwatch, Collider, Active;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SkillSnap
|
|
||||||
{
|
|
||||||
public UnitSkills S;
|
|
||||||
public int ReadyTurn, BonusMovesLeft, PierceHits;
|
|
||||||
public bool Reserved, Armed, BonusAttack, OwUnlimited;
|
|
||||||
public float OwMult, OwAdd;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class Snapshot
|
public class Snapshot
|
||||||
{
|
{
|
||||||
public readonly List<UnitSnap> Units = new List<UnitSnap>();
|
public readonly List<UnitSnap> Units = new List<UnitSnap>();
|
||||||
public readonly List<SkillSnap> Skills = new List<SkillSnap>();
|
/// <summary>스킬 상태 전부(유닛별 값 · 시한 효과 · 상태 이상 · 지대 · 2026-11-10 #1112).</summary>
|
||||||
public List<SkillSystem.Timed> Timed;
|
public SkillSystem.State Skills;
|
||||||
public int LogCount;
|
|
||||||
public GimmickLayer.Snapshot Gimmicks;
|
public GimmickLayer.Snapshot Gimmicks;
|
||||||
public string Message;
|
public string Message;
|
||||||
}
|
}
|
||||||
|
|
@ -77,19 +68,7 @@ namespace TKS
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
var sys = bm.Skills;
|
var sys = bm.Skills;
|
||||||
if (sys != null)
|
if (sys != null) s.Skills = sys.TakeState();
|
||||||
{
|
|
||||||
foreach (var e in sys.Entries)
|
|
||||||
{
|
|
||||||
s.Skills.Add(new SkillSnap
|
|
||||||
{
|
|
||||||
S = e, ReadyTurn = e.ReadyTurn, BonusMovesLeft = e.BonusMovesLeft, PierceHits = e.PierceHits, Reserved = e.Reserved, Armed = e.Armed,
|
|
||||||
BonusAttack = e.BonusAttack, OwUnlimited = e.OwUnlimited, OwMult = e.OwMult, OwAdd = e.OwAdd
|
|
||||||
});
|
|
||||||
}
|
|
||||||
s.Timed = new List<SkillSystem.Timed>(sys.TimedEffects);
|
|
||||||
s.LogCount = sys.Log.Count;
|
|
||||||
}
|
|
||||||
if (GimmickLayer.Current != null) s.Gimmicks = GimmickLayer.Current.Take();
|
if (GimmickLayer.Current != null) s.Gimmicks = GimmickLayer.Current.Take();
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
@ -112,28 +91,7 @@ namespace TKS
|
||||||
if (u.gameObject.activeSelf != x.Active) u.gameObject.SetActive(x.Active);
|
if (u.gameObject.activeSelf != x.Active) u.gameObject.SetActive(x.Active);
|
||||||
}
|
}
|
||||||
var sys = bm.Skills;
|
var sys = bm.Skills;
|
||||||
if (sys != null)
|
if (sys != null) sys.RestoreState(s.Skills);
|
||||||
{
|
|
||||||
foreach (var k in s.Skills)
|
|
||||||
{
|
|
||||||
var e = k.S;
|
|
||||||
e.ReadyTurn = k.ReadyTurn;
|
|
||||||
e.BonusMovesLeft = k.BonusMovesLeft;
|
|
||||||
e.PierceHits = k.PierceHits;
|
|
||||||
e.Reserved = k.Reserved;
|
|
||||||
e.Armed = k.Armed;
|
|
||||||
e.BonusAttack = k.BonusAttack;
|
|
||||||
e.OwUnlimited = k.OwUnlimited;
|
|
||||||
e.OwMult = k.OwMult;
|
|
||||||
e.OwAdd = k.OwAdd;
|
|
||||||
}
|
|
||||||
if (s.Timed != null)
|
|
||||||
{
|
|
||||||
sys.TimedEffects.Clear();
|
|
||||||
sys.TimedEffects.AddRange(s.Timed);
|
|
||||||
}
|
|
||||||
if (sys.Log.Count > s.LogCount) sys.Log.RemoveRange(s.LogCount, sys.Log.Count - s.LogCount);
|
|
||||||
}
|
|
||||||
if (GimmickLayer.Current != null && s.Gimmicks != null) GimmickLayer.Current.Restore(s.Gimmicks);
|
if (GimmickLayer.Current != null && s.Gimmicks != null) GimmickLayer.Current.Restore(s.Gimmicks);
|
||||||
bm.LastMessage = s.Message;
|
bm.LastMessage = s.Message;
|
||||||
Physics2D.SyncTransforms();
|
Physics2D.SyncTransforms();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,369 @@
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace TKS
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 스킬 특수 사격(2026-11-10 #1112 · 효과 표 skill_effects.csv · 수치 = 표 p 칸):
|
||||||
|
/// shot_giant(황충 거대 화살 · 사거리 무관 · 첫 적) · shot_pierce(직선 관통 · 아군 통과/피해/회복 · 밀기) · shot_blast(투사체 → 첫 적 자리 폭발 · 불꽃 지대) ·
|
||||||
|
/// shot_fan(부채꼴 n 발 × 연속) · shot_whirl(회오리 = 직선 안 적 밀기) · shot_barricade(목책 세우기) · shot_firewall(불 장벽 지대) ·
|
||||||
|
/// 일반 사격 덧붙임 = multishot(멀티샷) · shot_ricochet(도탄) · poison_zone(독지대) · 아군 명중 = 회복·버프·이동(ally_hit_*).
|
||||||
|
/// </summary>
|
||||||
|
public partial class ActionResolver
|
||||||
|
{
|
||||||
|
static readonly string[] ShotKeys =
|
||||||
|
{
|
||||||
|
SkillKeys.ShotGiant, SkillKeys.ShotPierce, SkillKeys.ShotBlast, SkillKeys.ShotFan, SkillKeys.ShotWhirl, SkillKeys.ShotBarricade, SkillKeys.ShotFirewall
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool HasSpecialShot(Unit u)
|
||||||
|
{
|
||||||
|
foreach (var k in ShotKeys)
|
||||||
|
if (SkillMods.Has(u, k)) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator SpecialShot(Unit u, Vector2 origin, Vector2 dir, Vector2 target, ActionOutcome o)
|
||||||
|
{
|
||||||
|
o.Fired = true;
|
||||||
|
Face(u, dir);
|
||||||
|
if (u.View != null) u.View.PlayAttack();
|
||||||
|
if (u.Type == UnitType.Strategist) BattleEvents.Raise(BattleEvents.StrategistShots, u);
|
||||||
|
SkillEffect e;
|
||||||
|
if ((e = SkillMods.First(u, SkillKeys.ShotGiant)) != null) yield return GiantArrow(u, dir, e, o);
|
||||||
|
else if ((e = SkillMods.First(u, SkillKeys.ShotPierce)) != null) yield return PierceShot(u, dir, e, o);
|
||||||
|
else if ((e = SkillMods.First(u, SkillKeys.ShotBlast)) != null) yield return BlastShot(u, dir, e, o);
|
||||||
|
else if ((e = SkillMods.First(u, SkillKeys.ShotFan)) != null) yield return FanShot(u, dir, e, o);
|
||||||
|
else if ((e = SkillMods.First(u, SkillKeys.ShotWhirl)) != null) yield return WhirlShot(u, dir, e, o);
|
||||||
|
else if ((e = SkillMods.First(u, SkillKeys.ShotBarricade)) != null) yield return Barricade(u, dir, target, e, o);
|
||||||
|
else if ((e = SkillMods.First(u, SkillKeys.ShotFirewall)) != null) yield return Firewall(u, dir, e, o);
|
||||||
|
battle.NotifyChanged();
|
||||||
|
yield return Fx.Wait(0.2f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>황충 백발백중: 사거리 무관 · 반지름 p1 거대 화살 → 아군·장애물 통과 · 첫 적 = 공격력 × p2.</summary>
|
||||||
|
IEnumerator GiantArrow(Unit u, Vector2 dir, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
Vector2 from = u.Position;
|
||||||
|
float far = battle.Board != null ? 200f : 60f;
|
||||||
|
float travel;
|
||||||
|
GimmickInstance g;
|
||||||
|
Combat.ArrowIgnoreGimmicks = true;
|
||||||
|
Unit hit;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hit = Combat.FirstUnitOnArrow(u, from, dir, far, Mathf.Max(cfg.arrowRadius, e.P), u.Faction, out travel, out g);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Combat.ArrowIgnoreGimmicks = false;
|
||||||
|
}
|
||||||
|
Vector2 to = from + dir * travel;
|
||||||
|
yield return Fx.Arrow(from, to, new Color(1f, 0.85f, 0.4f));
|
||||||
|
if (hit != null)
|
||||||
|
{
|
||||||
|
o.ShotHit = hit;
|
||||||
|
ShotDamage(u, hit, Combat.SkillDamage(cfg, u, hit, e.Get(1, 1f), rng), false, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>직선 관통(태사자·장임·왕랑·조범·주태 검기): 사거리 안 직선 위 유닛 전원(장애물 무시) · 적 = 공격력 × p1 · 아군 = p2(0 통과 · 1 피해 · 2 회복) · p3 = 밀기.</summary>
|
||||||
|
IEnumerator PierceShot(Unit u, Vector2 dir, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
Vector2 from = u.Position;
|
||||||
|
float range = SkillMods.Range(u) + u.Radius;
|
||||||
|
float travel = Combat.WallTravel(u, from, dir, range);
|
||||||
|
var dists = new List<float>();
|
||||||
|
var units = Combat.UnitsOnPath(u, from, dir, travel, dists);
|
||||||
|
yield return Fx.Arrow(from, from + dir * travel, u.Type == UnitType.Strategist ? Fx.StrategistShotColor : new Color(0.75f, 0.95f, 1f));
|
||||||
|
int allyMode = Mathf.RoundToInt(e.Get(1));
|
||||||
|
var pushed = new List<Unit>();
|
||||||
|
foreach (var t in units)
|
||||||
|
{
|
||||||
|
if (!t.IsAlive) continue;
|
||||||
|
if (t.Faction == u.Faction)
|
||||||
|
{
|
||||||
|
if (allyMode == 0) continue;
|
||||||
|
if (allyMode == 2)
|
||||||
|
{
|
||||||
|
AllyHitSpecial(u, t, from, o, true);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (o.ShotHit == null) o.ShotHit = t;
|
||||||
|
ShotDamage(u, t, Combat.SkillDamage(cfg, u, t, e.P, rng), false, o);
|
||||||
|
if (t.IsAlive) pushed.Add(t);
|
||||||
|
}
|
||||||
|
float push = e.Get(2);
|
||||||
|
if (push > 0.01f)
|
||||||
|
{
|
||||||
|
pushed.Reverse(); // 먼 쪽부터 밀어 서로 막히지 않게
|
||||||
|
foreach (var t in pushed)
|
||||||
|
if (t.IsAlive) yield return Push(t, dir, push * SkillMods.KnockbackTakenMult(t, true), u, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>폭발 사격(서서 화염구 · 가후 독구름 · 주유 운석 · 손권 화살비): 투사체 → 사거리 안 첫 적(아군 너머) 자리 · 반경 p1 안 적 전원 = 공격력 × p2 · 주유 = 불꽃 지대.</summary>
|
||||||
|
IEnumerator BlastShot(Unit u, Vector2 dir, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
Vector2 from = u.Position;
|
||||||
|
float range = SkillMods.Range(u) + u.Radius;
|
||||||
|
float travel;
|
||||||
|
GimmickInstance g;
|
||||||
|
var hit = Combat.FirstUnitOnArrow(u, from, dir, range, cfg.arrowRadius, u.Faction, out travel, out g);
|
||||||
|
Vector2 center = hit != null ? hit.Position : from + dir * travel;
|
||||||
|
yield return Fx.Arrow(from, center, u.Type == UnitType.Strategist ? Fx.StrategistShotColor : Fx.ArrowColor);
|
||||||
|
float r = e.P;
|
||||||
|
Fx.Burst(center, r, ExplosionColor);
|
||||||
|
o.ShotHit = hit;
|
||||||
|
foreach (var t in Combat.UnitsInCircle(center, r, u, null))
|
||||||
|
{
|
||||||
|
if (!t.IsAlive || t.Faction == u.Faction) continue;
|
||||||
|
ShotDamage(u, t, Combat.SkillDamage(cfg, u, t, e.Get(1, 1f), rng), false, o);
|
||||||
|
}
|
||||||
|
var fz = SkillMods.First(u, SkillKeys.FireZone);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
if (fz != null && sys != null && sys.Of(u) != null)
|
||||||
|
sys.AddZone(sys.Of(u), center, center, fz.P, Mathf.RoundToInt(fz.Get(1, 1f)), fz.Get(2), SkillMods.First(u, SkillKeys.HitBurn), new Color(1f, 0.45f, 0.1f, 0.35f));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>하후연 전광 연사: 부채꼴(p3 도) p1 발 × p2 번 · 화살마다 일반 사격 규칙(첫 유닛 · 아군 오사).</summary>
|
||||||
|
IEnumerator FanShot(Unit u, Vector2 dir, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
int n = Mathf.Max(1, Mathf.RoundToInt(e.P));
|
||||||
|
int volleys = Mathf.Max(1, Mathf.RoundToInt(e.Get(1, 1f)));
|
||||||
|
float spread = e.Get(2, 30f);
|
||||||
|
for (int v = 0; v < volleys && u.IsAlive; v++)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
float ang = n > 1 ? -spread * 0.5f + spread * i / (n - 1) : 0f;
|
||||||
|
FireOne(u, Rotate(dir, ang), o, 1f);
|
||||||
|
}
|
||||||
|
battle.NotifyChanged();
|
||||||
|
yield return Fx.Wait(0.18f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Vector2 Rotate(Vector2 v, float deg)
|
||||||
|
{
|
||||||
|
float r = deg * Mathf.Deg2Rad;
|
||||||
|
float c = Mathf.Cos(r), s = Mathf.Sin(r);
|
||||||
|
return new Vector2(v.x * c - v.y * s, v.x * s + v.y * c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>화살 1발(일반 사격 규칙 · 아군 통과 패시브 반영 · 아군 명중 = 회복류 또는 오사) — 부채꼴 · 멀티샷.</summary>
|
||||||
|
void FireOne(Unit u, Vector2 d, ActionOutcome o, float ratio)
|
||||||
|
{
|
||||||
|
float range = SkillMods.Range(u) + u.Radius;
|
||||||
|
bool obstacles;
|
||||||
|
var pass = ArrowPass(u, out obstacles);
|
||||||
|
float travel;
|
||||||
|
GimmickInstance g;
|
||||||
|
Combat.ArrowIgnoreGimmicks = obstacles;
|
||||||
|
Unit hit;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
hit = Combat.FirstUnitOnArrow(u, u.Position, d, range, cfg.arrowRadius, pass, out travel, out g);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Combat.ArrowIgnoreGimmicks = false;
|
||||||
|
}
|
||||||
|
Fx.Run(Fx.Arrow(u.Position, u.Position + d * (hit != null ? travel + cfg.arrowRadius : travel)));
|
||||||
|
if (hit == null) return;
|
||||||
|
if (o.ShotHit == null) o.ShotHit = hit;
|
||||||
|
if (hit.Faction == u.Faction && AllyHitSpecial(u, hit, u.Position, o, true)) return;
|
||||||
|
ShotDamage(u, hit, ratio >= 0.999f && ratio <= 1.001f ? Combat.Damage(cfg, u, hit, false, rng, false) : Combat.SkillDamage(cfg, u, hit, ratio, rng), false, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>제갈근 선풍계: 사거리 안 폭 p1 직선 위 적 전원을 진행 방향으로 p2 밀침(먼 쪽부터) · 피해 = 공격력 × p3(0 = 없음) · 명중 상태 이상(혼란).</summary>
|
||||||
|
IEnumerator WhirlShot(Unit u, Vector2 dir, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
Vector2 from = u.Position;
|
||||||
|
float range = SkillMods.Range(u) + u.Radius;
|
||||||
|
float travel = Combat.WallTravel(u, from, dir, range);
|
||||||
|
Vector2 to = from + dir * travel;
|
||||||
|
yield return Fx.Arrow(from, to, new Color(0.7f, 1f, 0.85f));
|
||||||
|
var targets = new List<Unit>();
|
||||||
|
foreach (var t in battle.AliveUnits(u.Faction == Faction.Player ? Faction.Enemy : Faction.Player))
|
||||||
|
if (Combat.SegmentPointDistance(from, to, t.Position) <= e.P + t.Radius) targets.Add(t);
|
||||||
|
targets.Sort((a, b) => Vector2.Distance(from, b.Position).CompareTo(Vector2.Distance(from, a.Position)));
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
foreach (var t in targets)
|
||||||
|
{
|
||||||
|
if (!t.IsAlive) continue;
|
||||||
|
if (e.Get(2) > 0f) ShotDamage(u, t, Combat.SkillDamage(cfg, u, t, e.Get(2), rng), false, o);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
o.Attacked.Add(t);
|
||||||
|
if (sys != null) sys.ApplyHitStatuses(u, t, false, HitKind.Skill);
|
||||||
|
}
|
||||||
|
if (t.IsAlive) yield return Push(t, dir, e.Get(1, 1f) * SkillMods.KnockbackTakenMult(t, true), u, o);
|
||||||
|
}
|
||||||
|
if (o.Attacked.Count == 0) o.MovedOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>노숙 녹각진: 슬링 목표점(사거리 안)에 진행 방향에 수직으로 목책 p1 칸(칸 지름 p3) · 그 자리 적 = 목책 충돌 피해(기믹 표 자기 피해 %)·p2 밀기 · 아군 = 밀기만.</summary>
|
||||||
|
IEnumerator Barricade(Unit u, Vector2 dir, Vector2 target, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
int n = Mathf.Max(1, Mathf.RoundToInt(e.P));
|
||||||
|
float size = Mathf.Max(0.3f, e.Get(2, 1f));
|
||||||
|
float range = SkillMods.Range(u) + u.Radius;
|
||||||
|
Vector2 c = target;
|
||||||
|
if (Vector2.Distance(u.Position, c) > range) c = u.Position + dir * range;
|
||||||
|
c = battle.Board.ClampInside(c, size * 0.5f);
|
||||||
|
Vector2 perp = new Vector2(-dir.y, dir.x);
|
||||||
|
var layer = GimmickLayer.Current;
|
||||||
|
var def = GimmickTables.Db != null ? GimmickTables.Db.Get(GimmickIds.Palisade) : null;
|
||||||
|
float pct = def != null ? Mathf.Max(def.cavSelfPct, def.bouncePct) : 0f;
|
||||||
|
yield return Fx.Arrow(u.Position, c, Fx.StrategistShotColor);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
Vector2 p = c + perp * ((i - (n - 1) * 0.5f) * size);
|
||||||
|
p = battle.Board.ClampInside(p, size * 0.5f);
|
||||||
|
foreach (var t in Combat.UnitsInCircle(p, size * 0.5f, null, null))
|
||||||
|
{
|
||||||
|
if (!t.IsAlive) continue;
|
||||||
|
if (t.Faction != u.Faction && pct > 0f && sys != null)
|
||||||
|
{
|
||||||
|
int d = sys.Hit(t, Mathf.Max(1, Mathf.RoundToInt(t.MaxHp * pct)), u, HitKind.Skill);
|
||||||
|
o.Attacked.Add(t);
|
||||||
|
o.DamageDealt += d;
|
||||||
|
Fx.Popup(t.Position, d + " 목책", new Color(1f, 0.75f, 0.4f));
|
||||||
|
}
|
||||||
|
if (t.IsAlive) yield return Push(t, dir, Mathf.Max(e.Get(1, 1f), size), u, o);
|
||||||
|
}
|
||||||
|
if (layer != null) layer.Spawn(GimmickIds.Palisade, GimmickShape.Circle(p, size * 0.5f));
|
||||||
|
}
|
||||||
|
o.Notes.Add("목책 " + n + "칸");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>육손 이릉의 화공: 진행 방향 사거리까지 반폭 p1 불 장벽(p2 턴) · 그 안 적 = 공격력 × p3 + 화상 · 장벽 = 닿는 적마다 같은 피해·화상(지대).</summary>
|
||||||
|
IEnumerator Firewall(Unit u, Vector2 dir, SkillEffect e, ActionOutcome o)
|
||||||
|
{
|
||||||
|
Vector2 a = u.Position + dir * (u.Radius + e.P);
|
||||||
|
float range = SkillMods.Range(u) + u.Radius;
|
||||||
|
Vector2 b = u.Position + dir * Mathf.Max(u.Radius + e.P, Combat.WallTravel(u, u.Position, dir, range));
|
||||||
|
yield return Fx.Arrow(u.Position, b, new Color(1f, 0.45f, 0.1f));
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
SkillZone z = null;
|
||||||
|
if (sys != null && sys.Of(u) != null)
|
||||||
|
z = sys.AddZone(sys.Of(u), a, b, e.P, Mathf.RoundToInt(e.Get(1, 1f)), e.Get(2, 1f), SkillMods.First(u, SkillKeys.HitBurn), new Color(1f, 0.4f, 0.05f, 0.35f));
|
||||||
|
foreach (var t in battle.AliveUnits(u.Faction == Faction.Player ? Faction.Enemy : Faction.Player))
|
||||||
|
{
|
||||||
|
if (Combat.SegmentPointDistance(a, b, t.Position) > e.P + t.Radius) continue;
|
||||||
|
if (z != null) z.Touched.Add(t);
|
||||||
|
ShotDamage(u, t, Combat.SkillDamage(cfg, u, t, e.Get(2, 1f), rng), false, o);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>일반 사격 덧붙임: 하후연 삼연시(멀티샷 · 좌우 화살) · 감녕 도탄(맞은 유닛과 가까운 n 기 · 아군 포함) · 장로 독지대.</summary>
|
||||||
|
IEnumerator ExtraArrows(Unit u, ShotPlan p, ActionOutcome o)
|
||||||
|
{
|
||||||
|
var ms = SkillMods.First(u, SkillKeys.Multishot, true);
|
||||||
|
if (ms != null && u.Type == UnitType.Archer)
|
||||||
|
{
|
||||||
|
int n = Mathf.Max(1, Mathf.RoundToInt(ms.P));
|
||||||
|
float spread = ms.Get(1, 30f);
|
||||||
|
Vector2 d = (p.To - p.From).sqrMagnitude > 1e-6f ? (p.To - p.From).normalized : Vector2.up;
|
||||||
|
for (int i = 0; i < n; i++)
|
||||||
|
{
|
||||||
|
float ang = n > 1 ? -spread * 0.5f + spread * i / (n - 1) : 0f;
|
||||||
|
if (Mathf.Abs(ang) < 0.01f) continue; // 가운데 = 본 화살
|
||||||
|
FireOne(u, Rotate(d, ang), o, 1f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var rc = SkillMods.First(u, SkillKeys.ShotRicochet);
|
||||||
|
if (rc != null && p.Hit != null)
|
||||||
|
{
|
||||||
|
var near = new List<Unit>();
|
||||||
|
foreach (var t in battle.Units)
|
||||||
|
if (t != null && t.IsAlive && t != u && t != p.Hit) near.Add(t);
|
||||||
|
Vector2 c = p.Hit.Position;
|
||||||
|
near.Sort((a, b) => Vector2.Distance(c, a.Position).CompareTo(Vector2.Distance(c, b.Position)));
|
||||||
|
int cnt = Mathf.Max(0, Mathf.RoundToInt(rc.P));
|
||||||
|
for (int i = 0; i < near.Count && i < cnt; i++)
|
||||||
|
{
|
||||||
|
var t = near[i];
|
||||||
|
Fx.Run(Fx.Arrow(c, t.Position));
|
||||||
|
ShotDamage(u, t, Combat.SkillDamage(cfg, u, t, rc.Get(1, 0.5f), rng), false, o);
|
||||||
|
}
|
||||||
|
if (cnt > 0) o.Notes.Add("도탄");
|
||||||
|
}
|
||||||
|
var pz = SkillMods.First(u, SkillKeys.PoisonZone);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
if (pz != null && sys != null && sys.Of(u) != null && (p.Hit != null || p.HitGimmick != null))
|
||||||
|
sys.AddZone(sys.Of(u), p.To, p.To, pz.P, Mathf.RoundToInt(pz.Get(1, 1f)), 0f, SkillMods.First(u, SkillKeys.HitPoison), new Color(0.6f, 0.3f, 0.9f, 0.3f));
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 자신의 사격이 아군에 맞음: 감녕 백기습영 = 그 아군 곁으로 이동(피해 없음) + 남은 행동 범위로 재행동 · 순욱·소교·간옹·조범 = 공격력 × p 회복 ·
|
||||||
|
/// 미축·순욱 = 이동·공격 버프(지속 턴). 처리했으면 true(피해 없음) · 효과가 없으면 false(오사).
|
||||||
|
/// </summary>
|
||||||
|
bool AllyHitSpecial(Unit u, Unit ally, Vector2 from, ActionOutcome o, bool splash = false)
|
||||||
|
{
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
if (sys == null || ally == null || ally == u) return false;
|
||||||
|
bool any = false;
|
||||||
|
float heal = SkillMods.Sum(u, SkillKeys.AllyHitHeal, true);
|
||||||
|
if (heal > 0f)
|
||||||
|
{
|
||||||
|
sys.Heal(ally, Mathf.RoundToInt(SkillSystem.AtkOf(u) * heal), u);
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
foreach (var h in SkillMods.Get(u, SkillKeys.AllyHitBuff))
|
||||||
|
{
|
||||||
|
if (h.Owner == null) continue;
|
||||||
|
int rounds = Mathf.Max(1, h.E.Dur);
|
||||||
|
if (h.E.P > 0f) sys.AddBuff(h.Owner, ally, SkillKeys.SlingDistPct, h.E.P, rounds);
|
||||||
|
if (h.E.Get(1) > 0f) sys.AddBuff(h.Owner, ally, SkillKeys.AtkPct, h.E.Get(1), rounds);
|
||||||
|
if (h.Passive) sys.NotePassive(h.Owner, h.Def);
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
if (!splash && SkillMods.Has(u, SkillKeys.AllyHitWarp, true))
|
||||||
|
{
|
||||||
|
var s = sys.Of(u);
|
||||||
|
Vector2 d = ally.Position - u.Position;
|
||||||
|
d = d.sqrMagnitude > 1e-6f ? d.normalized : Vector2.up;
|
||||||
|
Vector2 spot = battle.Board.ClampInside(ally.Position - d * (u.Radius + ally.Radius + 0.05f), u.Radius);
|
||||||
|
float used = Vector2.Distance(o.Path.Count > 0 ? o.Path[0] : u.Position, spot);
|
||||||
|
Vector2 start = u.Position;
|
||||||
|
if (!Sim.Active)
|
||||||
|
{
|
||||||
|
u.transform.position = new Vector3(start.x, start.y, 0f);
|
||||||
|
Fx.Run(Fx.MoveAlong(u.transform, start, spot, cfg.moveSpeed * 2f, null));
|
||||||
|
}
|
||||||
|
u.Position = spot;
|
||||||
|
o.Path.Add(spot);
|
||||||
|
float left = SkillMods.SlingMax(u) - used;
|
||||||
|
if (s != null && left >= cfg.minDragDistance)
|
||||||
|
{
|
||||||
|
s.ReactCredits++;
|
||||||
|
s.ReactLimit = left;
|
||||||
|
}
|
||||||
|
o.Notes.Add(ally.DisplayName + " 곁으로");
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
return any;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>이각 약탈: 이번 광역으로 준 피해 × p1 → 자신과 인접(p2)한 가장 HP 비율이 낮은 다른 아군 회복.</summary>
|
||||||
|
void SplashHealLowest(Unit u, int total)
|
||||||
|
{
|
||||||
|
var e = SkillMods.First(u, SkillKeys.SplashHealLowest, true);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
if (e == null || total <= 0 || sys == null) return;
|
||||||
|
Unit pick = null;
|
||||||
|
foreach (var a in battle.AliveUnits(u.Faction))
|
||||||
|
{
|
||||||
|
if (a == u || !SkillMods.Within(u, a, e.Get(1, 2f))) continue;
|
||||||
|
if (pick == null || (float)a.Hp / a.MaxHp < (float)pick.Hp / pick.MaxHp) pick = a;
|
||||||
|
}
|
||||||
|
if (pick != null) sys.Heal(pick, Mathf.RoundToInt(total * e.P), u);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 1f16df9a5a16efb40bd5ff913ee2fa74
|
||||||
|
|
@ -78,7 +78,9 @@ namespace TKS
|
||||||
public IEnumerator Resolve(Unit u, Vector2 target, bool moveOnly, ActionOutcome o)
|
public IEnumerator Resolve(Unit u, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||||
{
|
{
|
||||||
Vector2 origin = u.Position;
|
Vector2 origin = u.Position;
|
||||||
target = ReachPoint(u, target); // 늪·고지 이동 비용 = 실제 도달점(근접 병종 · PD #1097)
|
float slingDist = Vector2.Distance(origin, target);
|
||||||
|
Vector2 aimed = target;
|
||||||
|
target = ReachPoint(u, target); // 늪·고지 이동 비용 = 실제 도달점(근접 병종 · PD #1097) · 조인 철벽 = 반경 안 이동 비용 +
|
||||||
Vector2 delta = target - origin;
|
Vector2 delta = target - origin;
|
||||||
float dist = delta.magnitude;
|
float dist = delta.magnitude;
|
||||||
o.Path.Add(origin);
|
o.Path.Add(origin);
|
||||||
|
|
@ -87,9 +89,54 @@ namespace TKS
|
||||||
|
|
||||||
GimmickBegin(o);
|
GimmickBegin(o);
|
||||||
var skills = SkillSystem.Current;
|
var skills = SkillSystem.Current;
|
||||||
if (skills != null) skills.BeginAction(u, moveOnly, o, dir); // 예약 액티브 장착 + 발동 연출(PD #1093 B) · 이펙트 방향 = 슬링샷 방향
|
if (skills != null)
|
||||||
|
{
|
||||||
|
skills.BeginAction(u, moveOnly, o, dir, slingDist); // 예약 액티브 장착 + 발동 연출 · 돌진 거리 비율(만인지적·강동의 호걸·적토마)
|
||||||
|
if (skills.Ctx != null && (aimed - target).sqrMagnitude > 1e-4f) skills.Ctx.HitTerrain = true; // 이동 감소 지형을 지남(맹획 남만왕)
|
||||||
|
}
|
||||||
// 슬링샷 돌진 중 = 병종 돌진 포즈 1프레임 고정 · 끝나면 대기 (PD 2026-11-05 #1084)
|
// 슬링샷 돌진 중 = 병종 돌진 포즈 1프레임 고정 · 끝나면 대기 (PD 2026-11-05 #1084)
|
||||||
if (u.View != null) u.View.SetCharging(true);
|
if (u.View != null) u.View.SetCharging(true);
|
||||||
|
// 정보 연환격(repeat_action) = 같은 거리·방향으로 한 번 더(멈춘 자리에서)
|
||||||
|
int runs = !moveOnly && SkillMods.Has(u, SkillKeys.RepeatAction) ? 2 : 1;
|
||||||
|
for (int k = 0; k < runs && u.IsAlive; k++)
|
||||||
|
{
|
||||||
|
if (k > 0)
|
||||||
|
{
|
||||||
|
yield return Fx.Wait(0.15f);
|
||||||
|
origin = u.Position;
|
||||||
|
target = battle.Board.ClampInside(origin + delta, u.Radius);
|
||||||
|
Vector2 d2 = target - origin;
|
||||||
|
dist = d2.magnitude;
|
||||||
|
if (dist < 0.05f) break;
|
||||||
|
dir = d2 / dist;
|
||||||
|
DashBegin(u, dir);
|
||||||
|
o.Notes.Add("한 번 더");
|
||||||
|
}
|
||||||
|
yield return RoutineFor(u, origin, dir, dist, target, moveOnly, o);
|
||||||
|
}
|
||||||
|
yield return GimmickAfter(u, moveOnly, o, !(UnitTypeInfo.IsRanged(u.Type) && o.Fired)); // 빙판 · 지대 스윕 · 강(PD #1097)
|
||||||
|
if (u.View != null) u.View.SetCharging(false);
|
||||||
|
if (skills != null) yield return skills.EndAction(u, o); // 출혈·중독 · 지대 · 재행동 · 예약 소모 · 쿨
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>병종 루틴 선택 — 장착 스킬이 바꾸는 것: 동탁 폭정(난동) · 손견·안량 관통(보·창도 기병처럼) · 축융 비도(창병이 궁병처럼 사격).</summary>
|
||||||
|
IEnumerator RoutineFor(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||||
|
{
|
||||||
|
if (!moveOnly && SkillMods.Has(u, SkillKeys.Rampage))
|
||||||
|
{
|
||||||
|
yield return RampageRoutine(u, origin, dir, o);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
if (!moveOnly && SkillMods.Has(u, SkillKeys.ShotAsArcher))
|
||||||
|
{
|
||||||
|
yield return ArcherRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
|
if (!moveOnly && !UnitTypeInfo.IsRanged(u.Type) && u.Type != UnitType.Cavalry && SkillMods.Has(u, SkillKeys.PierceThrough))
|
||||||
|
{
|
||||||
|
yield return CavalryRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
switch (u.Type)
|
switch (u.Type)
|
||||||
{
|
{
|
||||||
case UnitType.Infantry:
|
case UnitType.Infantry:
|
||||||
|
|
@ -99,16 +146,13 @@ namespace TKS
|
||||||
yield return SpearRoutine(u, origin, dir, dist, target, moveOnly, o);
|
yield return SpearRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||||
break;
|
break;
|
||||||
case UnitType.Cavalry:
|
case UnitType.Cavalry:
|
||||||
yield return CavalryRoutine(u, origin, dir, dist, target, moveOnly, o, true);
|
yield return CavalryRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||||
break;
|
break;
|
||||||
case UnitType.Archer:
|
case UnitType.Archer:
|
||||||
case UnitType.Strategist:
|
case UnitType.Strategist:
|
||||||
yield return ArcherRoutine(u, origin, dir, dist, target, moveOnly, o);
|
yield return ArcherRoutine(u, origin, dir, dist, target, moveOnly, o);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
yield return GimmickAfter(u, moveOnly, o, !(UnitTypeInfo.IsRanged(u.Type) && o.Fired)); // 빙판 · 지대 스윕 · 강(PD #1097)
|
|
||||||
if (u.View != null) u.View.SetCharging(false);
|
|
||||||
if (skills != null) skills.EndAction(u, o); // 예약 소모 · 쿨 · 견제 강화 이월
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 미리보기(드라이런) ----------
|
// ---------- 미리보기(드라이런) ----------
|
||||||
|
|
@ -135,14 +179,15 @@ namespace TKS
|
||||||
case UnitType.Infantry:
|
case UnitType.Infantry:
|
||||||
case UnitType.Spear:
|
case UnitType.Spear:
|
||||||
{
|
{
|
||||||
var first = FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
if (SkillMods.Has(u, SkillKeys.PierceThrough) || SkillMods.Has(u, SkillKeys.Rampage)) goto case UnitType.Cavalry;
|
||||||
|
var first = SkillMods.Has(u, SkillKeys.PassAllies) ? FirstEnemyBeforeWall(u, origin, dir, dist, out hitDist) : FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
||||||
if (first != null && first.Faction != u.Faction) result.Add(first);
|
if (first != null && first.Faction != u.Faction) result.Add(first);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case UnitType.Cavalry:
|
case UnitType.Cavalry:
|
||||||
{
|
{
|
||||||
float travel = Combat.FreeTravel(u, origin, dir, dist, false, false);
|
float travel = Combat.FreeTravel(u, origin, dir, dist, false, false);
|
||||||
travel = StopBeforeUnclearedAllies(u, origin, dir, travel);
|
if (!PierceAllies(u)) travel = StopBeforeUnclearedAllies(u, origin, dir, travel);
|
||||||
var dists = new List<float>();
|
var dists = new List<float>();
|
||||||
result.AddRange(Combat.EnemiesOnPath(u, origin, dir, travel, dists));
|
result.AddRange(Combat.EnemiesOnPath(u, origin, dir, travel, dists));
|
||||||
break;
|
break;
|
||||||
|
|
@ -206,6 +251,37 @@ namespace TKS
|
||||||
return first;
|
return first;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>조조 난세의 간웅(pass_allies): 아군은 통과 · 경로 위 첫 적(벽이 먼저면 null).</summary>
|
||||||
|
Unit FirstEnemyBeforeWall(Unit u, Vector2 origin, Vector2 dir, float dist, out float hitDist)
|
||||||
|
{
|
||||||
|
Unit first = Combat.FirstEnemyOnPath(u, origin, dir, dist, out hitDist);
|
||||||
|
if (first == null) return null;
|
||||||
|
float wall = Combat.FreeTravel(u, origin, dir, dist, false, false);
|
||||||
|
if (hitDist > wall + Combat.Epsilon)
|
||||||
|
{
|
||||||
|
hitDist = 0f;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>관통이 아군도 지나는가(손견 강동의 호랑이 · 손책 소패왕 = pierce_through p1 1).</summary>
|
||||||
|
static bool PierceAllies(Unit u)
|
||||||
|
{
|
||||||
|
foreach (var h in SkillMods.Get(u, SkillKeys.PierceThrough))
|
||||||
|
if (h.E.P >= 0.5f) return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>아군 통과 이동(유닛 무시 · 벽·장애물만) → 끝이 유닛과 겹치면 겹치지 않는 곳까지 되짚음.</summary>
|
||||||
|
IEnumerator MoveThrough(Unit u, Vector2 origin, Vector2 dir, float dist, ActionOutcome o)
|
||||||
|
{
|
||||||
|
float free = Combat.FreeTravel(u, origin, dir, dist, true, false);
|
||||||
|
Vector2 end = BackOffFromUnits(u, origin, dir, free, true);
|
||||||
|
yield return Move(u, end, o);
|
||||||
|
o.MovedOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- 보병: 알까기 충돌 ----------
|
// ---------- 보병: 알까기 충돌 ----------
|
||||||
|
|
||||||
IEnumerator InfantryRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
IEnumerator InfantryRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||||
|
|
@ -217,7 +293,8 @@ namespace TKS
|
||||||
}
|
}
|
||||||
|
|
||||||
float hitDist;
|
float hitDist;
|
||||||
Unit first = FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
bool passAllies = SkillMods.Has(u, SkillKeys.PassAllies); // 조조 난세의 간웅 = 아군 통과
|
||||||
|
Unit first = passAllies ? FirstEnemyBeforeWall(u, origin, dir, dist, out hitDist) : FirstUnitBeforeWall(u, origin, dir, dist, out hitDist);
|
||||||
float gDist;
|
float gDist;
|
||||||
Vector2 gNormal;
|
Vector2 gNormal;
|
||||||
var gim = BlockAhead(u, origin, dir, dist, first != null ? hitDist : dist, out gDist, out gNormal);
|
var gim = BlockAhead(u, origin, dir, dist, first != null ? hitDist : dist, out gDist, out gNormal);
|
||||||
|
|
@ -228,7 +305,8 @@ namespace TKS
|
||||||
}
|
}
|
||||||
if (first == null)
|
if (first == null)
|
||||||
{
|
{
|
||||||
yield return MoveFree(u, origin, dir, dist, o);
|
if (passAllies) yield return MoveThrough(u, origin, dir, dist, o);
|
||||||
|
else yield return MoveFree(u, origin, dir, dist, o);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -250,20 +328,68 @@ namespace TKS
|
||||||
o.MovedOnly = true;
|
o.MovedOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 알까기: 부딪힌 순간 양쪽이 동시에 튕긴다 — 상대 = 접촉 방향 × 남은 힘 × collisionPushRatio · 자신 = 반대 방향 × 남은 힘 × collisionSelfBounceRatio
|
|
||||||
Vector2 n = first.Position - contact;
|
Vector2 n = first.Position - contact;
|
||||||
n = n.sqrMagnitude > 1e-6f ? n.normalized : dir;
|
n = n.sqrMagnitude > 1e-6f ? n.normalized : dir;
|
||||||
|
if (SkillMods.Has(u, SkillKeys.SwapOnHit) && first.IsAlive)
|
||||||
|
{
|
||||||
|
yield return SwapWith(u, first, origin, o); // 위연 자오곡 기책: 명중 대상 ↔ 출발 위치(튕김 없음)
|
||||||
|
}
|
||||||
|
else if (enemy && first.IsAlive && SkillMods.Has(u, SkillKeys.AimPush))
|
||||||
|
{
|
||||||
|
// 조조 난세의 간웅: 자신은 밀리지 않고 탭한 방향으로 명중 적을 밀침(적·자동 = 슬링 방향)
|
||||||
|
Vector2 aimDir = n;
|
||||||
|
var sk = SkillSystem.Current;
|
||||||
|
if (sk != null)
|
||||||
|
yield return sk.WaitTap(u, SkillSystem.TapKind.Aim, first.Position, 0f, o.SkillUsed != null ? o.SkillUsed.NameKo + " — 밀칠 방향을 탭" : "밀칠 방향을 탭", first.Position + n,
|
||||||
|
w => { var d = w - first.Position; if (d.sqrMagnitude > 1e-4f) aimDir = d.normalized; });
|
||||||
|
float force = Mathf.Max(remaining, cfg.minDragDistance) * cfg.collisionPushRatio * SkillMods.PushMult(u, true) * SkillMods.KnockbackTakenMult(first, true);
|
||||||
|
if (first.IsAlive) yield return Push(first, aimDir, force, u, o);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 알까기: 부딪힌 순간 양쪽이 동시에 튕긴다 — 상대 = 접촉 방향 × 남은 힘 × collisionPushRatio · 자신 = 반대 방향 × 남은 힘 × collisionSelfBounceRatio
|
||||||
yield return Bounce(u, first, n, remaining, o, enemy ? impact : 0, enemy); // 적과 싸운 뒤 튕김 = 연쇄 1회(PD #1098)
|
yield return Bounce(u, first, n, remaining, o, enemy ? impact : 0, enemy); // 적과 싸운 뒤 튕김 = 연쇄 1회(PD #1098)
|
||||||
|
}
|
||||||
if (!enemy && first.IsAlive) yield return PushedAllyAttack(first, o, u);
|
if (!enemy && first.IsAlive) yield return PushedAllyAttack(first, o, u);
|
||||||
|
if (!enemy) PushContinue(u, remaining); // 고순: 아군을 밀친 뒤 남은 이동력으로 재행동
|
||||||
|
|
||||||
if (isFinal && u.IsAlive)
|
if (isFinal && u.IsAlive)
|
||||||
{
|
{
|
||||||
o.BonusMove = true;
|
o.BonusMove = true;
|
||||||
o.BonusMoves = Mathf.Max(1, SkillMods.Count(u, SkillKeys.ExtraMoveCount, 1)); // 인덕: 추가 이동 n 회
|
o.BonusMoves = 1;
|
||||||
o.BonusAttack = SkillMods.Has(u, SkillKeys.ExtraMoveAttack); // 호치·강동의 호랑이: 추가 이동에서 공격
|
o.BonusAttack = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>고순 함진 돌파(push_continue): 아군·지형 오브젝트를 밀친 뒤 남은 이동 거리만큼 재행동 1회(슬링 상한 = 남은 거리).</summary>
|
||||||
|
void PushContinue(Unit u, float remaining)
|
||||||
|
{
|
||||||
|
var sk = SkillSystem.Current;
|
||||||
|
var s = sk != null ? sk.Of(u) : null;
|
||||||
|
if (s == null || !u.IsAlive || remaining < cfg.minDragDistance || !SkillMods.Has(u, SkillKeys.PushContinue, true)) return;
|
||||||
|
s.ReactCredits++;
|
||||||
|
s.ReactLimit = remaining;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>위연: 명중 대상은 행동 유닛의 출발 위치로 · 행동 유닛은 대상 자리로(동시에).</summary>
|
||||||
|
IEnumerator SwapWith(Unit u, Unit t, Vector2 origin, ActionOutcome o)
|
||||||
|
{
|
||||||
|
Vector2 tFrom = t.Position;
|
||||||
|
Vector2 tTo = battle.Board.ClampInside(origin, t.Radius);
|
||||||
|
Vector2 uTo = battle.Board.ClampInside(tFrom, u.Radius);
|
||||||
|
bool done = false;
|
||||||
|
Fx.Run(Track(Fx.MoveAlong(t.transform, tFrom, tTo, cfg.moveSpeed * 1.5f, null), () => done = true));
|
||||||
|
Face(u, uTo - u.Position);
|
||||||
|
yield return Fx.MoveAlong(u.transform, u.Position, uTo, cfg.moveSpeed * 1.5f, null);
|
||||||
|
while (!done) yield return null;
|
||||||
|
t.Position = tTo;
|
||||||
|
u.Position = uTo;
|
||||||
|
o.Path.Add(uTo);
|
||||||
|
o.Displaced.Add(new Displacement { Unit = t, From = tFrom, To = tTo });
|
||||||
|
o.Notes.Add(t.DisplayName + " 자리 교환");
|
||||||
|
battle.NotifyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
// ---------- 창병: 밀치기 + 반작용(PD 2026-11-06 #1098 · 밀며 함께 전진 폐기) ----------
|
// ---------- 창병: 밀치기 + 반작용(PD 2026-11-06 #1098 · 밀며 함께 전진 폐기) ----------
|
||||||
|
|
||||||
IEnumerator SpearRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
IEnumerator SpearRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||||
|
|
@ -332,156 +458,299 @@ namespace TKS
|
||||||
battle.NotifyChanged();
|
battle.NotifyChanged();
|
||||||
if (!enemy) yield return PushedAllyAttack(first, o, u);
|
if (!enemy) yield return PushedAllyAttack(first, o, u);
|
||||||
}
|
}
|
||||||
if (enemy && u.IsAlive) SpearHitMore(u, first, o);
|
// 목표점 명중 → 대기(견제) = 제거(PD 2026-11-10) · 새 스킬 기획(2026-11-10 #1112)에도 견제 없음
|
||||||
// 목표점 명중 → 대기(견제) = 제거(PD 2026-11-10) · 견제 스킬(청룡언월·후계·교변 …)은 재설계 결정 대기라 BattleManager.ApplyOverwatch 등은 남겨 둠(대기 유닛이 없어 발동 안 함)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>삼첨도(spear_hit_two): 밀며 전진 끝에 창병·밀린 적과 닿은 다른 적까지 합쳐 최대 n 기에 동시 피해(반격 없음).</summary>
|
|
||||||
void SpearHitMore(Unit u, Unit first, ActionOutcome o)
|
|
||||||
{
|
|
||||||
int n = SkillMods.Count(u, SkillKeys.SpearHitTwo, 1);
|
|
||||||
if (n <= 1) return;
|
|
||||||
Physics2D.SyncTransforms();
|
|
||||||
var extra = new List<Unit>();
|
|
||||||
foreach (var near in new[] { u, first })
|
|
||||||
{
|
|
||||||
if (near == null || !near.IsAlive) continue;
|
|
||||||
foreach (var c in Physics2D.OverlapCircleAll(near.Position, near.Radius + Combat.ContactTolerance + 0.5f))
|
|
||||||
{
|
|
||||||
var e = c.GetComponent<Unit>();
|
|
||||||
if (e == null || e == first || e == u || !e.IsAlive || e.Faction == u.Faction || extra.Contains(e)) continue;
|
|
||||||
float gap = Vector2.Distance(near.Position, e.Position) - near.Radius - e.Radius;
|
|
||||||
if (gap <= Combat.ContactTolerance) extra.Add(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extra.Sort((a, b) => Vector2.Distance(u.Position, a.Position).CompareTo(Vector2.Distance(u.Position, b.Position)));
|
|
||||||
for (int i = 0; i < extra.Count && i < n - 1; i++)
|
|
||||||
{
|
|
||||||
var e = extra[i];
|
|
||||||
int dmg = SkillSystem.Deal(e, Combat.Damage(cfg, u, e, false, rng, true));
|
|
||||||
o.Attacked.Add(e);
|
|
||||||
o.DamageDealt += dmg;
|
|
||||||
Fx.Popup(e.Position, dmg.ToString(), Color.white);
|
|
||||||
}
|
|
||||||
battle.NotifyChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 기병: 관통(제자리 복귀 없음 · PD 2026-11-10) ----------
|
// ---------- 기병: 관통(제자리 복귀 없음 · PD 2026-11-10) ----------
|
||||||
|
|
||||||
/// <param name="first">false = 위진소요의 추가 관통(다시 반복하지 않음 · exclude = 앞서 관통한 적 제외).</param>
|
/// <summary>
|
||||||
IEnumerator CavalryRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o, bool first, List<Unit> exclude = null)
|
/// 관통 돌격(기병 · 스킬로 관통하는 보병·창병): 유닛 무시(아군은 지나칠 수 없으면 직전 정지 · 손견·손책 = 아군도 통과) · 경로 위 적 피해·옆 밀기 ·
|
||||||
|
/// 손책 소패왕 = 전장 벽에서 핀볼처럼 튕겨 남은 거리 소진 · 반격 · 장애물 → 장료 요래(끌어오기) → 복귀(조운·손책·여포·문추·공손찬·순우경 · 하후돈 = 탭 지점·쓸어 감).
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="slideExclude">빙판 미끄러짐 재실행(앞서 관통한 적 제외 · 끌어오기·복귀 없음) · null = 일반 행동.</param>
|
||||||
|
IEnumerator CavalryRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o, List<Unit> slideExclude = null)
|
||||||
{
|
{
|
||||||
float travel = Combat.FreeTravel(u, origin, dir, dist, false, false); // 유닛은 무시 · 벽·장애물 기믹만
|
bool slide = slideExclude != null;
|
||||||
travel = StopBeforeUnclearedAllies(u, origin, dir, travel); // 지나칠 수 없는 아군 직전에서 멈춤
|
bool passAll = !moveOnly && PierceAllies(u);
|
||||||
|
bool pinball = !moveOnly && !slide && SkillMods.Has(u, SkillKeys.WallBounce, true);
|
||||||
|
var hit = new List<Unit>();
|
||||||
|
Vector2 start = origin;
|
||||||
|
Vector2 d = dir;
|
||||||
|
float left = dist;
|
||||||
|
GimmickInstance gim = null;
|
||||||
|
Vector2 gNormal = -dir;
|
||||||
|
float gimLeft = 0f;
|
||||||
|
Face(u, dir);
|
||||||
|
for (int seg = 0; seg < 6 && left > 0.05f && u.IsAlive; seg++)
|
||||||
|
{
|
||||||
|
float travel = Combat.FreeTravel(u, start, d, left, false, false); // 유닛은 무시 · 벽·장애물 기믹만
|
||||||
|
if (!passAll) travel = StopBeforeUnclearedAllies(u, start, d, travel); // 지나칠 수 없는 아군 직전에서 멈춤
|
||||||
float gDist;
|
float gDist;
|
||||||
Vector2 gNormal;
|
gim = BlockAhead(u, start, d, left, travel + 0.05f, out gDist, out gNormal); // 장애물 = 관통 불가 → 거기서 정지(PD #1097)
|
||||||
var gim = BlockAhead(u, origin, dir, dist, travel + 0.05f, out gDist, out gNormal); // 장애물 = 관통 불가 → 거기서 정지(PD #1097)
|
|
||||||
var dists = new List<float>();
|
var dists = new List<float>();
|
||||||
var enemies = moveOnly ? new List<Unit>() : Combat.EnemiesOnPath(u, origin, dir, travel, dists);
|
var enemies = moveOnly ? new List<Unit>() : Combat.EnemiesOnPath(u, start, d, travel, dists);
|
||||||
if (exclude != null)
|
if (slide)
|
||||||
{
|
|
||||||
for (int i = enemies.Count - 1; i >= 0; i--)
|
for (int i = enemies.Count - 1; i >= 0; i--)
|
||||||
|
if (slideExclude.Contains(enemies[i]))
|
||||||
{
|
{
|
||||||
if (!exclude.Contains(enemies[i])) continue;
|
|
||||||
enemies.RemoveAt(i);
|
enemies.RemoveAt(i);
|
||||||
dists.RemoveAt(i);
|
dists.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
Vector2 wallN;
|
||||||
Unit finalHit = moveOnly ? null : Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance);
|
bool bounce = false;
|
||||||
bool hitFinal = first && finalHit != null && enemies.Contains(finalHit);
|
if (pinball && gim == null && travel < left - 0.1f)
|
||||||
// 위진소요: 목표점 명중 시 같은 방향 한 번 더 관통 · 제자리 복귀(옛 목표점 명중 · 단기구주 빗나가도 복귀) = 제거(PD 2026-11-10)
|
|
||||||
int repeat = hitFinal && gim == null ? SkillMods.Count(u, SkillKeys.PierceRepeat, 0) : 0;
|
|
||||||
|
|
||||||
Vector2 end = origin + dir * travel;
|
|
||||||
if (repeat <= 0) end = BackOffFromEnemies(u, origin, dir, travel);
|
|
||||||
|
|
||||||
if (enemies.Count == 0)
|
|
||||||
{
|
{
|
||||||
yield return Move(u, end, o);
|
Combat.BoardWallTravel(u, start, d, left, out wallN);
|
||||||
o.MovedOnly = true;
|
if (wallN.sqrMagnitude > 0.5f)
|
||||||
if (gim != null && u.IsAlive) yield return CavalryHitsGimmick(u, gim, Mathf.Max(0f, dist - travel), gNormal, o);
|
{
|
||||||
yield break;
|
bounce = true;
|
||||||
|
gNormal = wallN;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
float dmgMult = SkillMods.PierceDmgMult(u, enemies.Count, true); // 만인지적(2기 이상) · 발시담정 · 천하무쌍
|
Vector2 end = bounce ? start + d * travel : BackOffFromUnits(u, start, d, travel, passAll);
|
||||||
Face(u, dir);
|
if (enemies.Count > 0 && u.View != null) u.View.PlayAttack();
|
||||||
if (u.View != null) u.View.PlayAttack();
|
|
||||||
int idx = 0;
|
int idx = 0;
|
||||||
var hit = new List<Unit>();
|
var segDir = d;
|
||||||
yield return Fx.MoveAlong(u.transform, origin, end, cfg.moveSpeed, travelled =>
|
yield return Fx.MoveAlong(u.transform, start, end, cfg.moveSpeed, travelled =>
|
||||||
{
|
{
|
||||||
while (idx < enemies.Count && travelled >= dists[idx] - 0.01f)
|
while (idx < enemies.Count && travelled >= dists[idx] - 0.01f)
|
||||||
{
|
{
|
||||||
PierceHit(u, enemies[idx], dir, o, dmgMult);
|
PierceHit(u, enemies[idx], segDir, o, 1f);
|
||||||
hit.Add(enemies[idx]);
|
hit.Add(enemies[idx]);
|
||||||
idx++;
|
idx++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
u.Position = end;
|
u.Position = end;
|
||||||
o.Path.Add(end);
|
o.Path.Add(end);
|
||||||
DashEnd(u); // 관통 돌격 끝 = 돌진 이펙트 소멸(PD 2026-11-10)
|
|
||||||
for (; idx < enemies.Count; idx++)
|
for (; idx < enemies.Count; idx++)
|
||||||
{
|
{
|
||||||
if (dists[idx] <= travel + 0.01f)
|
if (dists[idx] > travel + 0.01f) continue;
|
||||||
{
|
PierceHit(u, enemies[idx], segDir, o, 1f);
|
||||||
PierceHit(u, enemies[idx], dir, o, dmgMult);
|
|
||||||
hit.Add(enemies[idx]);
|
hit.Add(enemies[idx]);
|
||||||
}
|
}
|
||||||
|
gimLeft = Mathf.Max(0f, left - travel);
|
||||||
|
left -= travel;
|
||||||
|
if (!bounce) break;
|
||||||
|
d = Vector2.Reflect(d, gNormal).normalized; // 소패왕: 전장 가장자리에서 튕겨 남은 거리를 마저 달림
|
||||||
|
start = end;
|
||||||
|
o.Notes.Add("튕김");
|
||||||
|
DashBegin(u, d);
|
||||||
|
Face(u, d);
|
||||||
}
|
}
|
||||||
yield return Fx.Wait(0.15f);
|
DashEnd(u); // 관통 돌격 끝 = 돌진 이펙트 소멸(PD 2026-11-10)
|
||||||
|
if (hit.Count == 0) o.MovedOnly = true;
|
||||||
|
else yield return Fx.Wait(0.15f);
|
||||||
|
|
||||||
// 살아남은 근접 적의 반격(단기구주·발시담정·천하무쌍 = 반격 없음)
|
// 살아남은 근접 적의 반격(받는 반격 % · −100 % = 없음 → Combat.CounterDamage 가 0)
|
||||||
if (!SkillMods.Has(u, SkillKeys.PierceNoCounter))
|
var countered = new HashSet<Unit>();
|
||||||
{
|
|
||||||
foreach (var e in hit)
|
foreach (var e in hit)
|
||||||
{
|
{
|
||||||
if (!u.IsAlive) break;
|
if (!u.IsAlive) break;
|
||||||
if (!e.IsAlive || !UnitTypeInfo.IsMelee(e.Type)) continue;
|
if (!e.IsAlive || !UnitTypeInfo.IsMelee(e.Type) || !countered.Add(e)) continue;
|
||||||
int c = Combat.CounterDamage(cfg, e, u, rng, true);
|
int c = Combat.CounterDamage(cfg, e, u, rng, true);
|
||||||
if (c <= 0) continue;
|
if (c <= 0) continue;
|
||||||
c = SkillSystem.Deal(u, c);
|
c = SkillSystem.Deal(u, c, e, HitKind.Counter);
|
||||||
o.CounterTaken += c;
|
o.CounterTaken += c;
|
||||||
Fx.Popup(u.Position, c.ToString(), new Color(1f, 0.45f, 0.45f));
|
Fx.Popup(u.Position, c.ToString(), new Color(1f, 0.45f, 0.45f));
|
||||||
battle.NotifyChanged();
|
battle.NotifyChanged();
|
||||||
yield return Fx.Wait(0.12f);
|
yield return Fx.Wait(0.12f);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (gim != null && u.IsAlive)
|
if (gim != null && u.IsAlive)
|
||||||
{
|
{
|
||||||
yield return CavalryHitsGimmick(u, gim, Mathf.Max(0f, dist - travel), gNormal, o);
|
var sk0 = SkillSystem.Current;
|
||||||
|
if (sk0 != null && sk0.Ctx != null && sk0.Ctx.Actor == u) sk0.Ctx.HitTerrain = true;
|
||||||
|
yield return CavalryHitsGimmick(u, gim, gimLeft, gNormal, o);
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
|
if (!u.IsAlive || moveOnly || slide) yield break;
|
||||||
|
|
||||||
if (repeat > 0 && u.IsAlive)
|
if (hit.Count > 0 && SkillMods.Has(u, SkillKeys.DragBack, true)) yield return DragBack(u, origin, dir, hit, o); // 장료 요래
|
||||||
|
|
||||||
|
// 복귀: 항상(return_always) · 피해를 입혔을 때(return_on_hit) · 하후돈 맹장 = 거리와 무관하게 탭한 지점으로
|
||||||
|
var ra = SkillMods.First(u, SkillKeys.ReturnAlways, true);
|
||||||
|
var rh = hit.Count > 0 ? SkillMods.First(u, SkillKeys.ReturnOnHit, true) : null;
|
||||||
|
bool aim = SkillMods.Has(u, SkillKeys.ReturnAim, true);
|
||||||
|
if (ra == null && rh == null && !aim) yield break;
|
||||||
|
bool dmgBack = (ra != null && ra.P >= 0.5f) || (rh != null && rh.P >= 0.5f);
|
||||||
|
bool sweep = (ra != null && ra.Get(1) >= 0.5f) || (rh != null && rh.Get(1) >= 0.5f);
|
||||||
|
Vector2 dest = origin;
|
||||||
|
if (aim)
|
||||||
{
|
{
|
||||||
// 위진소요: 목표점에서 같은 방향으로 같은 거리만큼 한 번 더 관통(앞서 관통한 적 제외 · 그 뒤 복귀 없음)
|
var sk = SkillSystem.Current;
|
||||||
for (int i = 0; i < repeat && u.IsAlive; i++)
|
if (sk != null)
|
||||||
|
yield return sk.WaitTap(u, SkillSystem.TapKind.Return, u.Position, SkillMods.SlingMax(u), (o.SkillUsed != null ? o.SkillUsed.NameKo : "복귀") + " — 복귀할 곳을 탭", origin, w => dest = w);
|
||||||
|
}
|
||||||
|
yield return ReturnRoutine(u, dest, dmgBack, sweep, o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>복귀 이동: 유닛 무시 · 복귀 중 피해(조운·손책 소패왕) · 쓸어 감(하후돈 = 경로 위 유닛을 끝까지 함께 밀고 감 · 아군 포함).</summary>
|
||||||
|
IEnumerator ReturnRoutine(Unit u, Vector2 dest, bool dmgBack, bool sweep, ActionOutcome o)
|
||||||
{
|
{
|
||||||
yield return Fx.Wait(0.1f);
|
yield return Fx.Wait(0.1f);
|
||||||
|
dest = battle.Board.ClampInside(dest, u.Radius);
|
||||||
Vector2 from = u.Position;
|
Vector2 from = u.Position;
|
||||||
DashBegin(u, dir); // 추가 관통 = 돌진 이펙트 다시
|
Vector2 rd = dest - from;
|
||||||
yield return CavalryRoutine(u, from, dir, dist, from + dir * dist, false, o, false, hit);
|
float len = rd.magnitude;
|
||||||
|
if (len < 0.05f) yield break;
|
||||||
|
Vector2 rdir = rd / len;
|
||||||
|
float travel = Combat.FreeTravel(u, from, rdir, len, false, false); // 벽·장애물에서 멈춤
|
||||||
|
var dists = new List<float>();
|
||||||
|
var onPath = Combat.UnitsOnPath(u, from, rdir, travel, dists);
|
||||||
|
Vector2 end = sweep ? from + rdir * travel : BackOffFromUnits(u, from, rdir, travel, true);
|
||||||
|
DashBegin(u, rdir);
|
||||||
|
Face(u, rdir);
|
||||||
|
int idx = 0;
|
||||||
|
yield return Fx.MoveAlong(u.transform, from, end, cfg.moveSpeed, t =>
|
||||||
|
{
|
||||||
|
while (idx < onPath.Count && t >= dists[idx] - 0.01f)
|
||||||
|
{
|
||||||
|
var e = onPath[idx++];
|
||||||
|
if (dmgBack && e.Faction != u.Faction) PierceHit(u, e, rdir, o, 1f);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
u.Position = end;
|
||||||
|
o.Path.Add(end);
|
||||||
|
o.Returned = true;
|
||||||
|
DashEnd(u);
|
||||||
|
if (sweep)
|
||||||
|
{
|
||||||
|
// 쓸어 감: 경로 위 유닛을 도착점 앞(복귀 방향)으로 줄지어 민다
|
||||||
|
float off = u.Radius;
|
||||||
|
foreach (var e in onPath)
|
||||||
|
{
|
||||||
|
if (e == null || !e.IsAlive) continue;
|
||||||
|
Vector2 eFrom = e.Position;
|
||||||
|
off += e.Radius + 0.05f;
|
||||||
|
Vector2 eTo = battle.Board.ClampInside(end + rdir * off, e.Radius);
|
||||||
|
off += e.Radius;
|
||||||
|
Fx.Run(Fx.MoveAlong(e.transform, eFrom, eTo, cfg.moveSpeed, null));
|
||||||
|
e.Position = eTo;
|
||||||
|
o.Displaced.Add(new Displacement { Unit = e, From = eFrom, To = eTo });
|
||||||
|
if (!o.Pushed.Contains(e)) o.Pushed.Add(e);
|
||||||
}
|
}
|
||||||
|
if (onPath.Count > 0) o.Notes.Add("복귀하며 쓸어 감");
|
||||||
|
}
|
||||||
|
battle.NotifyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>장료 요래: 관통으로 피해를 입힌 적을 출발 위치로 끌고 옴(가까운 적부터 출발점 · 뒤는 진행 방향으로 줄지음).</summary>
|
||||||
|
IEnumerator DragBack(Unit u, Vector2 origin, Vector2 dir, List<Unit> hit, ActionOutcome o)
|
||||||
|
{
|
||||||
|
var list = new List<Unit>();
|
||||||
|
foreach (var e in hit)
|
||||||
|
if (e != null && e.IsAlive && !list.Contains(e)) list.Add(e);
|
||||||
|
list.Sort((a, b) => Vector2.Distance(origin, a.Position).CompareTo(Vector2.Distance(origin, b.Position)));
|
||||||
|
float off = 0f;
|
||||||
|
bool any = false;
|
||||||
|
foreach (var e in list)
|
||||||
|
{
|
||||||
|
Vector2 from = e.Position;
|
||||||
|
Vector2 to = battle.Board.ClampInside(origin + dir * off, e.Radius);
|
||||||
|
off += e.Radius * 2f + 0.1f;
|
||||||
|
Fx.Run(Fx.MoveAlong(e.transform, from, to, cfg.moveSpeed * 1.2f, null));
|
||||||
|
e.Position = to;
|
||||||
|
o.Displaced.Add(new Displacement { Unit = e, From = from, To = to });
|
||||||
|
any = true;
|
||||||
|
}
|
||||||
|
if (any)
|
||||||
|
{
|
||||||
|
o.Notes.Add("끌고 옴");
|
||||||
|
yield return Fx.Wait(0.3f);
|
||||||
|
}
|
||||||
|
battle.NotifyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>동탁 폭정: p1 초 동안 이동 속도로 직진 · 전장 가장자리에서 핀볼처럼 튕김 · 닿은 유닛(아군 포함)을 옆으로 p2 강하게 밀침 · 적 = 돌진 피해(반격 받음) · 자신은 튕기지 않음.</summary>
|
||||||
|
IEnumerator RampageRoutine(Unit u, Vector2 origin, Vector2 dir, ActionOutcome o)
|
||||||
|
{
|
||||||
|
var e0 = SkillMods.First(u, SkillKeys.Rampage, true);
|
||||||
|
float total = cfg.moveSpeed * (e0 != null ? e0.P : 1f);
|
||||||
|
float force = e0 != null ? e0.Get(1, 1f) : 1f;
|
||||||
|
var touched = new HashSet<Unit>();
|
||||||
|
Vector2 start = origin;
|
||||||
|
Vector2 d = dir;
|
||||||
|
float left = total;
|
||||||
|
Face(u, d);
|
||||||
|
if (u.View != null) u.View.PlayAttack();
|
||||||
|
for (int seg = 0; seg < 8 && left > 0.05f && u.IsAlive; seg++)
|
||||||
|
{
|
||||||
|
Vector2 n;
|
||||||
|
float travel = Combat.BoardWallTravel(u, start, d, left, out n);
|
||||||
|
float gDist;
|
||||||
|
Vector2 gN;
|
||||||
|
var g = BlockAhead(u, start, d, left, travel + 0.05f, out gDist, out gN);
|
||||||
|
if (g != null)
|
||||||
|
{
|
||||||
|
travel = Mathf.Max(0f, gDist - Combat.Epsilon);
|
||||||
|
n = gN;
|
||||||
|
}
|
||||||
|
var dists = new List<float>();
|
||||||
|
var units = Combat.UnitsOnPath(u, start, d, travel, dists);
|
||||||
|
Vector2 end = start + d * travel;
|
||||||
|
int idx = 0;
|
||||||
|
var segDir = d;
|
||||||
|
yield return Fx.MoveAlong(u.transform, start, end, cfg.moveSpeed, t =>
|
||||||
|
{
|
||||||
|
while (idx < units.Count && t >= dists[idx] - 0.01f)
|
||||||
|
{
|
||||||
|
var x = units[idx++];
|
||||||
|
if (!x.IsAlive || !touched.Add(x)) continue;
|
||||||
|
RampageHit(u, x, segDir, force, o);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
u.Position = end;
|
||||||
|
o.Path.Add(end);
|
||||||
|
left -= travel;
|
||||||
|
if (n.sqrMagnitude < 0.5f || left <= 0.05f) break;
|
||||||
|
if (g != null)
|
||||||
|
{
|
||||||
|
var sk = SkillSystem.Current;
|
||||||
|
if (sk != null && sk.Ctx != null) sk.Ctx.HitTerrain = true;
|
||||||
|
}
|
||||||
|
d = Vector2.Reflect(d, n).normalized;
|
||||||
|
start = end;
|
||||||
|
o.Notes.Add("튕김");
|
||||||
|
DashBegin(u, d);
|
||||||
|
Face(u, d);
|
||||||
|
}
|
||||||
|
DashEnd(u);
|
||||||
|
u.Position = BackOffFromUnits(u, origin, d, 0f, true, u.Position);
|
||||||
|
if (touched.Count == 0) o.MovedOnly = true;
|
||||||
|
battle.NotifyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void RampageHit(Unit u, Unit x, Vector2 dir, float force, ActionOutcome o)
|
||||||
|
{
|
||||||
|
if (x.Faction != u.Faction)
|
||||||
|
{
|
||||||
|
ImpactExchange(u, x, o);
|
||||||
|
if (!u.IsAlive) return;
|
||||||
|
}
|
||||||
|
if (!x.IsAlive) return;
|
||||||
|
Vector2 side = new Vector2(-dir.y, dir.x);
|
||||||
|
float sign = Vector2.Dot(x.Position - u.Position, side) >= 0f ? 1f : -1f;
|
||||||
|
Vector2 from = x.Position;
|
||||||
|
Vector2 to = KnockTo(x, side * sign, force * SkillMods.KnockbackTakenMult(x, true));
|
||||||
|
Fx.Run(Fx.MoveAlong(x.transform, from, to, cfg.moveSpeed, null));
|
||||||
|
x.Position = to;
|
||||||
|
o.Displaced.Add(new Displacement { Unit = x, From = from, To = to });
|
||||||
|
if (!o.Pushed.Contains(x)) o.Pushed.Add(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PierceHit(Unit cav, Unit e, Vector2 dir, ActionOutcome o, float dmgMult)
|
void PierceHit(Unit cav, Unit e, Vector2 dir, ActionOutcome o, float dmgMult)
|
||||||
{
|
{
|
||||||
if (!e.IsAlive) return;
|
if (!e.IsAlive) return;
|
||||||
int dmg = SkillSystem.Deal(e, Combat.Damage(cfg, cav, e, false, rng, true, dmgMult));
|
int dmg = SkillSystem.Deal(e, Combat.Damage(cfg, cav, e, false, rng, true, dmgMult), cav, HitKind.Attack);
|
||||||
o.Attacked.Add(e);
|
o.Attacked.Add(e);
|
||||||
o.DamageDealt += dmg;
|
o.DamageDealt += dmg;
|
||||||
Fx.Popup(e.Position, dmg.ToString(), Color.white);
|
Fx.Popup(e.Position, dmg.ToString(), Color.white);
|
||||||
var skills = SkillSystem.Current;
|
// 옆으로 밀어내기(거구 = 밀리지 않음)
|
||||||
var cs = skills != null ? skills.Of(cav) : null;
|
|
||||||
if (cs != null) cs.PierceHits++; // 팔백 돌격(전투 중 누적)
|
|
||||||
// 옆으로 밀어내기(상산의 창·연진 돌격·백마의종 = 넉백 배율 · 거구 = 밀리는 거리 −)
|
|
||||||
Vector2 side = new Vector2(-dir.y, dir.x);
|
Vector2 side = new Vector2(-dir.y, dir.x);
|
||||||
float sign = Vector2.Dot(e.Position - cav.Position, side) >= 0f ? 1f : -1f;
|
float sign = Vector2.Dot(e.Position - cav.Position, side) >= 0f ? 1f : -1f;
|
||||||
if (e.IsAlive)
|
if (e.IsAlive)
|
||||||
{
|
{
|
||||||
float kb = cfg.cavalryKnockback * SkillMods.PierceKnockbackMult(cav, true) * SkillMods.KnockbackTakenMult(e, true);
|
float kb = cfg.cavalryKnockback * SkillMods.KnockbackTakenMult(e, true);
|
||||||
Vector2 from = e.Position;
|
Vector2 from = e.Position;
|
||||||
Vector2 to = KnockTo(e, side * sign, kb); // 늪 0 · 빙판 × · 장애물·함정에서 멈춤 · 다리 → 강(PD #1097)
|
Vector2 to = KnockTo(e, side * sign, kb); // 늪 0 · 빙판 × · 장애물·함정에서 멈춤 · 다리 → 강(PD #1097)
|
||||||
Fx.Run(Fx.MoveAlong(e.transform, e.Position, to, cfg.moveSpeed * 0.6f, null));
|
Fx.Run(Fx.MoveAlong(e.transform, e.Position, to, cfg.moveSpeed * 0.6f, null));
|
||||||
|
|
@ -517,16 +786,18 @@ namespace TKS
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>종착점이 적과 겹치면 경로를 되짚어 겹치지 않는 지점까지 물러선다.</summary>
|
/// <summary>종착점이 적(allies = 아군도)과 겹치면 경로를 되짚어 겹치지 않는 지점까지 물러선다 · at = 그 점에서 dir 반대로 되짚음.</summary>
|
||||||
Vector2 BackOffFromEnemies(Unit u, Vector2 origin, Vector2 dir, float travel)
|
Vector2 BackOffFromUnits(Unit u, Vector2 origin, Vector2 dir, float travel, bool allies, Vector2? at = null)
|
||||||
{
|
{
|
||||||
float t = travel;
|
Vector2 p0 = at ?? origin + dir * travel;
|
||||||
for (int i = 0; i < 40 && t > 0f; i++)
|
float maxBack = at.HasValue ? 4f : travel;
|
||||||
|
for (float b = 0f; b <= maxBack + 1e-3f; b += 0.1f)
|
||||||
{
|
{
|
||||||
Vector2 p = origin + dir * t;
|
Vector2 p = p0 - dir * b;
|
||||||
bool overlap = false;
|
bool overlap = false;
|
||||||
foreach (var e in battle.AliveUnits(u.Faction == Faction.Player ? Faction.Enemy : Faction.Player))
|
foreach (var e in battle.Units)
|
||||||
{
|
{
|
||||||
|
if (e == null || e == u || !e.IsAlive || (!allies && e.Faction == u.Faction)) continue;
|
||||||
if (Vector2.Distance(p, e.Position) < u.Radius + e.Radius - 0.01f)
|
if (Vector2.Distance(p, e.Position) < u.Radius + e.Radius - 0.01f)
|
||||||
{
|
{
|
||||||
overlap = true;
|
overlap = true;
|
||||||
|
|
@ -534,9 +805,8 @@ namespace TKS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!overlap) return p;
|
if (!overlap) return p;
|
||||||
t -= 0.1f;
|
|
||||||
}
|
}
|
||||||
return origin;
|
return at ?? origin;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 궁병 · 책사: 투사체 사격 (PD 2026-11-05 #1087 ⑤⑥) ----------
|
// ---------- 궁병 · 책사: 투사체 사격 (PD 2026-11-05 #1087 ⑤⑥) ----------
|
||||||
|
|
@ -576,13 +846,6 @@ namespace TKS
|
||||||
ShotPlan PlanShot(Unit u, Vector2 origin, Vector2 dir, Vector2 target)
|
ShotPlan PlanShot(Unit u, Vector2 origin, Vector2 dir, Vector2 target)
|
||||||
{
|
{
|
||||||
var p = new ShotPlan { From = origin, To = origin, Origin = origin, SplashRadius = SkillMods.SplashRadius(u, cfg) };
|
var p = new ShotPlan { From = origin, To = origin, Origin = origin, SplashRadius = SkillMods.SplashRadius(u, cfg) };
|
||||||
// 백기습영(teleport · 다음 행동): 사거리와 무관하게 목표점까지 이동한 뒤 그 자리에서 사격
|
|
||||||
if (SkillMods.Has(u, SkillKeys.Teleport))
|
|
||||||
{
|
|
||||||
p.Teleport = Combat.FreeTravel(u, origin, dir, Vector2.Distance(origin, target), true, true);
|
|
||||||
origin += dir * p.Teleport;
|
|
||||||
p.From = p.To = origin;
|
|
||||||
}
|
|
||||||
float range = SkillMods.Range(u);
|
float range = SkillMods.Range(u);
|
||||||
// 조준 방향으로 사거리 + 접근 허용 거리 안의 첫 적(타겟 선정 = #1080 그대로). hitDist = 그 적 가장자리에 닿기까지의 이동 거리(= 가장자리 간격).
|
// 조준 방향으로 사거리 + 접근 허용 거리 안의 첫 적(타겟 선정 = #1080 그대로). hitDist = 그 적 가장자리에 닿기까지의 이동 거리(= 가장자리 간격).
|
||||||
float hitDist;
|
float hitDist;
|
||||||
|
|
@ -609,16 +872,42 @@ namespace TKS
|
||||||
Vector2 aim = p.Target.Position - p.From;
|
Vector2 aim = p.Target.Position - p.From;
|
||||||
Vector2 adir = aim.sqrMagnitude > 1e-6f ? aim.normalized : dir;
|
Vector2 adir = aim.sqrMagnitude > 1e-6f ? aim.normalized : dir;
|
||||||
float travel;
|
float travel;
|
||||||
Faction? pass = SkillMods.Has(u, SkillKeys.ArrowPassAllies) ? u.Faction : (Faction?)null;
|
bool passObstacles;
|
||||||
|
Faction? pass = ArrowPass(u, out passObstacles);
|
||||||
|
Combat.ArrowIgnoreGimmicks = passObstacles;
|
||||||
|
try
|
||||||
|
{
|
||||||
p.Hit = Combat.FirstUnitOnArrow(u, p.From, adir, range + u.Radius, cfg.arrowRadius, pass, out travel, out p.HitGimmick);
|
p.Hit = Combat.FirstUnitOnArrow(u, p.From, adir, range + u.Radius, cfg.arrowRadius, pass, out travel, out p.HitGimmick);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
Combat.ArrowIgnoreGimmicks = false;
|
||||||
|
}
|
||||||
p.To = p.From + adir * (p.Hit != null ? travel + cfg.arrowRadius : travel);
|
p.To = p.From + adir * (p.Hit != null ? travel + cfg.arrowRadius : travel);
|
||||||
if (SkillMods.Has(u, SkillKeys.ArrowCritAlways)) p.Crit = p.Hit != null && p.Hit.Faction != u.Faction; // 백발백중·백기습영: 명중 여부와 무관하게 크리
|
p.Crit = p.Hit == p.Target && Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance) == p.Target; // 목표점 명중 크리(무조건 치명타 스킬 = 피해 계산에서)
|
||||||
else p.Crit = p.Hit == p.Target && Combat.EnemyAtPoint(u, target, cfg.finalHitTolerance) == p.Target;
|
if (u.Type == UnitType.Strategist && !SkillMods.Has(u, SkillKeys.ShotSingle)) AimSplash(u, p); // 장로 = 직선 투사체(광역 없음)
|
||||||
if (u.Type == UnitType.Strategist) AimSplash(u, p);
|
}
|
||||||
|
|
||||||
|
/// <summary>화살이 지나는 진영(황충 노익장·손권 강동의 주인 = 아군 통과 · p2 1 = 장애물도 통과).</summary>
|
||||||
|
static Faction? ArrowPass(Unit u, out bool obstacles)
|
||||||
|
{
|
||||||
|
obstacles = false;
|
||||||
|
bool any = false;
|
||||||
|
foreach (var h in SkillMods.Get(u, SkillKeys.ArrowPassAllies))
|
||||||
|
{
|
||||||
|
any = true;
|
||||||
|
if (h.E.Get(1) >= 0.5f) obstacles = true;
|
||||||
|
}
|
||||||
|
return any ? u.Faction : (Faction?)null;
|
||||||
}
|
}
|
||||||
|
|
||||||
IEnumerator ArcherRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
IEnumerator ArcherRoutine(Unit u, Vector2 origin, Vector2 dir, float dist, Vector2 target, bool moveOnly, ActionOutcome o)
|
||||||
{
|
{
|
||||||
|
if (!moveOnly && HasSpecialShot(u))
|
||||||
|
{
|
||||||
|
yield return SpecialShot(u, origin, dir, target, o); // 장착 스킬 특수 사격(ActionResolver.Skills.cs)
|
||||||
|
yield break;
|
||||||
|
}
|
||||||
var p = moveOnly ? null : PlanShot(u, origin, dir, target);
|
var p = moveOnly ? null : PlanShot(u, origin, dir, target);
|
||||||
if (p != null && p.Teleport > 0.01f)
|
if (p != null && p.Teleport > 0.01f)
|
||||||
{
|
{
|
||||||
|
|
@ -665,42 +954,70 @@ namespace TKS
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 center = p.HasSplash ? p.SplashCenter : p.To;
|
Vector2 center = p.HasSplash ? p.SplashCenter : p.To;
|
||||||
if (p.Hit != null) ShotDamage(u, p.Hit, Combat.Damage(cfg, u, p.Hit, p.Crit, rng, false), p.Crit, o);
|
if (p.Hit != null)
|
||||||
|
{
|
||||||
|
if (p.Hit.Faction == u.Faction && AllyHitSpecial(u, p.Hit, p.From, o))
|
||||||
|
{
|
||||||
|
// 아군 명중 = 회복·버프(순욱·소교·간옹·미축) · 감녕 = 아군 곁으로 이동 후 재공격 — 피해 없음
|
||||||
|
}
|
||||||
|
else ShotDamage(u, p.Hit, Combat.Damage(cfg, u, p.Hit, p.Crit, rng, false), p.Crit, o);
|
||||||
|
}
|
||||||
else yield return ArrowHitsGimmick(u, p, o); // 덤불·성문 막힘 · 수레 파괴(PD #1097)
|
else yield return ArrowHitsGimmick(u, p, o); // 덤불·성문 막힘 · 수레 파괴(PD #1097)
|
||||||
|
yield return ExtraArrows(u, p, o); // 하후연 삼연시(멀티샷) · 감녕 도탄
|
||||||
if (strategist && p.HasSplash)
|
if (strategist && p.HasSplash)
|
||||||
{
|
{
|
||||||
yield return SplashGimmicks(u, p, o); // 덤불 제거 · 수레 파괴 · 화공 불(PD #1097)
|
yield return SplashGimmicks(u, p, o); // 덤불 제거 · 수레 파괴 · 화공 불(PD #1097)
|
||||||
Fx.Burst(center, p.SplashRadius, Fx.SplashColor);
|
Fx.Burst(center, p.SplashRadius, Fx.SplashColor);
|
||||||
|
float heal = SkillMods.Sum(u, SkillKeys.SplashHeal, true);
|
||||||
|
if (heal > 0f)
|
||||||
|
{
|
||||||
|
// 손건 화친: 광역 = 피해 대신 회복(적 포함)
|
||||||
|
int amt = Mathf.RoundToInt(SkillSystem.AtkOf(u) * heal);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
foreach (var s in p.Splash)
|
||||||
|
if (s.IsAlive && sys != null) sys.Heal(s, amt, u);
|
||||||
|
if (p.Hit != null && p.Hit.IsAlive && p.Hit.Faction == u.Faction && sys != null) sys.Heal(p.Hit, amt, u);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
float splashRatio = SkillMods.SplashRatio(u, cfg, true);
|
float splashRatio = SkillMods.SplashRatio(u, cfg, true);
|
||||||
float allyRatio = SkillMods.SplashAllyRatio(u, cfg, true);
|
float allyRatio = SkillMods.SplashAllyRatio(u, cfg, true);
|
||||||
|
int splashTotal = 0;
|
||||||
foreach (var s in p.Splash)
|
foreach (var s in p.Splash)
|
||||||
{
|
{
|
||||||
if (!s.IsAlive) continue;
|
if (!s.IsAlive) continue;
|
||||||
|
if (s.Faction == u.Faction && AllyHitSpecial(u, s, p.From, o, true)) continue; // 보급·경국지색 = 광역에 든 아군도 회복
|
||||||
float ratio = splashRatio * (s.Faction == u.Faction ? allyRatio : 1f);
|
float ratio = splashRatio * (s.Faction == u.Faction ? allyRatio : 1f);
|
||||||
int dmg = Combat.SplashDamage(cfg, u, s, ratio, rng);
|
int dmg = Combat.SplashDamage(cfg, u, s, ratio, rng);
|
||||||
if (dmg > 0) ShotDamage(u, s, dmg, false, o);
|
if (dmg <= 0) continue;
|
||||||
|
int got = ShotDamage(u, s, dmg, false, o);
|
||||||
|
if (s.Faction != u.Faction) splashTotal += got;
|
||||||
|
}
|
||||||
|
SplashHealLowest(u, splashTotal); // 이각 약탈
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
battle.NotifyChanged();
|
battle.NotifyChanged();
|
||||||
yield return Fx.Wait(0.2f);
|
yield return Fx.Wait(0.2f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>사격 피해 적용 + 집계(상대 = Attacked · 같은 편 = FriendlyHit 오사).</summary>
|
/// <summary>사격 피해 적용 + 집계(상대 = Attacked · 같은 편 = FriendlyHit 오사). 반환 = 실제 피해.</summary>
|
||||||
void ShotDamage(Unit a, Unit d, int dmg, bool crit, ActionOutcome o)
|
int ShotDamage(Unit a, Unit d, int dmg, bool crit, ActionOutcome o, HitKind kind = HitKind.Attack)
|
||||||
{
|
{
|
||||||
dmg = SkillSystem.Deal(d, dmg);
|
crit |= Combat.LastCrit;
|
||||||
|
dmg = SkillSystem.Deal(d, dmg, a, kind);
|
||||||
if (d.Faction == a.Faction)
|
if (d.Faction == a.Faction)
|
||||||
{
|
{
|
||||||
o.FriendlyHit.Add(d);
|
o.FriendlyHit.Add(d);
|
||||||
o.FriendlyDamage += dmg;
|
o.FriendlyDamage += dmg;
|
||||||
Fx.Popup(d.Position, dmg + " 오사", Fx.FriendlyFireColor);
|
Fx.Popup(d.Position, dmg + " 오사", Fx.FriendlyFireColor);
|
||||||
return;
|
return dmg;
|
||||||
}
|
}
|
||||||
o.Attacked.Add(d);
|
o.Attacked.Add(d);
|
||||||
o.DamageDealt += dmg;
|
o.DamageDealt += dmg;
|
||||||
o.Crit |= crit;
|
o.Crit |= crit;
|
||||||
if (crit && a.Type == UnitType.Archer) BattleEvents.Raise(BattleEvents.ArcherCrits, a); // 보물 조건 효과(PD #1093 C)
|
if (crit && a.Type == UnitType.Archer) BattleEvents.Raise(BattleEvents.ArcherCrits, a); // 보물 조건 효과(PD #1093 C)
|
||||||
Fx.Popup(d.Position, crit ? dmg + "!" : dmg.ToString(), crit ? new Color(1f, 0.7f, 0.2f) : Color.white);
|
Fx.Popup(d.Position, crit ? dmg + "!" : dmg.ToString(), crit ? new Color(1f, 0.7f, 0.2f) : Color.white);
|
||||||
|
return dmg;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 공통 ----------
|
// ---------- 공통 ----------
|
||||||
|
|
@ -752,17 +1069,20 @@ namespace TKS
|
||||||
int ImpactExchange(Unit a, Unit d, ActionOutcome o)
|
int ImpactExchange(Unit a, Unit d, ActionOutcome o)
|
||||||
{
|
{
|
||||||
if (a.View != null) a.View.PlayAttack();
|
if (a.View != null) a.View.PlayAttack();
|
||||||
int dmg = SkillSystem.Deal(d, Combat.Damage(cfg, a, d, false, rng, true));
|
int raw = Combat.Damage(cfg, a, d, false, rng, true);
|
||||||
|
bool crit = Combat.LastCrit;
|
||||||
|
int dmg = SkillSystem.Deal(d, raw, a, HitKind.Attack);
|
||||||
o.Attacked.Add(d);
|
o.Attacked.Add(d);
|
||||||
o.DamageDealt += dmg;
|
o.DamageDealt += dmg;
|
||||||
Fx.Popup(d.Position, dmg.ToString(), Color.white);
|
o.Crit |= crit;
|
||||||
|
Fx.Popup(d.Position, crit ? dmg + "!" : dmg.ToString(), crit ? new Color(1f, 0.7f, 0.2f) : Color.white);
|
||||||
if (d.IsAlive && a.IsAlive)
|
if (d.IsAlive && a.IsAlive)
|
||||||
{
|
{
|
||||||
int c = Combat.CounterDamage(cfg, d, a, rng, true); // 부딪혀 밀리는 쪽의 반격(진중 조건)
|
int c = Combat.CounterDamage(cfg, d, a, rng, true); // 부딪혀 밀리는 쪽의 반격(받는 반격 % · 반격 피해 %)
|
||||||
if (c > 0)
|
if (c > 0)
|
||||||
{
|
{
|
||||||
if (d.View != null) d.View.PlayAttack();
|
if (d.View != null) d.View.PlayAttack();
|
||||||
c = SkillSystem.Deal(a, c);
|
c = SkillSystem.Deal(a, c, d, HitKind.Counter);
|
||||||
o.CounterTaken += c;
|
o.CounterTaken += c;
|
||||||
Fx.Popup(a.Position, c.ToString(), new Color(1f, 0.45f, 0.45f));
|
Fx.Popup(a.Position, c.ToString(), new Color(1f, 0.45f, 0.45f));
|
||||||
}
|
}
|
||||||
|
|
@ -783,21 +1103,10 @@ namespace TKS
|
||||||
if (struck != null && struck.IsAlive && force >= 0.01f)
|
if (struck != null && struck.IsAlive && force >= 0.01f)
|
||||||
{
|
{
|
||||||
struckDone = false;
|
struckDone = false;
|
||||||
float bonus = impactDmg > 0 ? SkillMods.PushCollisionBonus(striker) : 0f;
|
Fx.Run(Track(Push(struck, n, force, striker, o), () => struckDone = true));
|
||||||
System.Action<bool> onEnd = null;
|
|
||||||
if (bonus > 0f)
|
|
||||||
{
|
|
||||||
onEnd = blocked =>
|
|
||||||
{
|
|
||||||
if (!blocked || !struck.IsAlive) return;
|
|
||||||
int extra = SkillSystem.Deal(struck, Mathf.Max(1, Mathf.RoundToInt(impactDmg * bonus)));
|
|
||||||
o.DamageDealt += extra;
|
|
||||||
Fx.Popup(struck.Position, extra + " 충돌", new Color(1f, 0.75f, 0.3f));
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
Fx.Run(Track(Push(struck, n, force, striker, o, onEnd), () => struckDone = true));
|
// 자기 튕김 × 자기 배율(허저 ×2 · 전위·맹획 0) × 서황 대부(도끼가 도는 서황을 친 유닛 = ×p3)
|
||||||
}
|
float self = Reach(striker, striker.Position, -n, remaining * cfg.collisionSelfBounceRatio * SkillMods.SelfBounceMult(striker, true) * (striker.Faction != (struck != null ? struck.Faction : striker.Faction) ? SkillMods.AxeBounce(struck) : 1f));
|
||||||
float self = Reach(striker, striker.Position, -n, remaining * cfg.collisionSelfBounceRatio * SkillMods.SelfBounceMult(striker, true));
|
|
||||||
if (self >= 0.01f && striker.IsAlive && chain)
|
if (self >= 0.01f && striker.IsAlive && chain)
|
||||||
{
|
{
|
||||||
// 연쇄(PD #1098): 튕겨나는 길의 첫 유닛(벽·장애물보다 앞) — 적 = 피해·반격 교환 후 다시 튕김 · 아군 = 밀기만(밀린 아군은 규칙 ⑤)
|
// 연쇄(PD #1098): 튕겨나는 길의 첫 유닛(벽·장애물보다 앞) — 적 = 피해·반격 교환 후 다시 튕김 · 아군 = 밀기만(밀린 아군은 규칙 ⑤)
|
||||||
|
|
@ -881,31 +1190,31 @@ namespace TKS
|
||||||
{
|
{
|
||||||
if (ally == null || !ally.IsAlive) yield break;
|
if (ally == null || !ally.IsAlive) yield break;
|
||||||
if (Combat.TouchingOpponent(ally) == null) yield break;
|
if (Combat.TouchingOpponent(ally) == null) yield break;
|
||||||
int times = 1;
|
|
||||||
if (pusher != null)
|
|
||||||
{
|
|
||||||
times = Mathf.Max(1, SkillMods.Count(pusher, SkillKeys.PushedAllyAttackCount, 1));
|
|
||||||
times += Mathf.Max(0, SkillMods.Count(pusher, SkillKeys.PushedAllyAttackOnPush, 0, true));
|
|
||||||
}
|
|
||||||
for (int i = 0; i < times; i++)
|
|
||||||
{
|
|
||||||
if (!ally.IsAlive) yield break;
|
|
||||||
Unit foe = Combat.TouchingOpponent(ally);
|
Unit foe = Combat.TouchingOpponent(ally);
|
||||||
if (foe == null) yield break;
|
if (!ally.IsAlive || foe == null) yield break;
|
||||||
yield return Fx.Wait(0.1f);
|
yield return Fx.Wait(0.1f);
|
||||||
Face(ally, foe.Position - ally.Position);
|
Face(ally, foe.Position - ally.Position);
|
||||||
yield return PushedAttack(ally, foe, o);
|
yield return PushedAttack(ally, foe, o, pusher);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>밀린 아군의 근접 공격 1회(반격 받음 · 결과 요약에는 집계하지 않고 Notes 에 기록 · 도원의 맹약 = 피해 +%). 원거리 병종도 근접 패널티를 받는다.</summary>
|
/// <summary>
|
||||||
IEnumerator PushedAttack(Unit a, Unit d, ActionOutcome o)
|
/// 밀린 유닛의 근접 공격 1회(반격 받음 · Notes 에 기록) — 장합 교변: 민 장합의 공격력 × p1 을 더함 · 장합 자신이 밀려 친 공격 = × p1 ·
|
||||||
|
/// 고순 함진영: 민 고순의 「명중 기절」이 이 공격에도(간접 명중). 원거리 병종도 근접 패널티를 받는다.
|
||||||
|
/// </summary>
|
||||||
|
IEnumerator PushedAttack(Unit a, Unit d, ActionOutcome o, Unit pusher = null)
|
||||||
{
|
{
|
||||||
if (a.View != null) a.View.PlayAttack();
|
if (a.View != null) a.View.PlayAttack();
|
||||||
yield return Fx.Lunge(a, d.Position);
|
yield return Fx.Lunge(a, d.Position);
|
||||||
|
|
||||||
float scale = 1f + SkillMods.Sum(a, SkillKeys.PushedAllyAttackPct, true);
|
float scale = SkillMods.Product(a, SkillKeys.SelfPushedMult, true);
|
||||||
int dmg = SkillSystem.Deal(d, Combat.Damage(cfg, a, d, false, rng, true, scale));
|
if (pusher != null && pusher != a && pusher.Faction == a.Faction && a.Atk > 0)
|
||||||
|
{
|
||||||
|
float add = SkillMods.Sum(pusher, SkillKeys.PushedAllyAtkAdd, true);
|
||||||
|
if (add > 0f) scale *= 1f + add * SkillSystem.AtkOf(pusher) / Mathf.Max(1f, SkillSystem.AtkOf(a));
|
||||||
|
}
|
||||||
|
int dmg = SkillSystem.Deal(d, Combat.Damage(cfg, a, d, false, rng, true, scale), a, HitKind.Attack);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
if (sys != null && pusher != null && pusher != a && pusher.Faction == a.Faction) sys.ApplyHitStatuses(pusher, d, false, HitKind.Indirect);
|
||||||
Fx.Popup(d.Position, dmg.ToString(), Color.white);
|
Fx.Popup(d.Position, dmg.ToString(), Color.white);
|
||||||
battle.NotifyChanged();
|
battle.NotifyChanged();
|
||||||
yield return Fx.Wait(0.2f);
|
yield return Fx.Wait(0.2f);
|
||||||
|
|
@ -918,7 +1227,7 @@ namespace TKS
|
||||||
{
|
{
|
||||||
if (d.View != null) d.View.PlayAttack();
|
if (d.View != null) d.View.PlayAttack();
|
||||||
yield return Fx.Lunge(d, a.Position);
|
yield return Fx.Lunge(d, a.Position);
|
||||||
counter = SkillSystem.Deal(a, counter);
|
counter = SkillSystem.Deal(a, counter, d, HitKind.Counter);
|
||||||
Fx.Popup(a.Position, counter.ToString(), new Color(1f, 0.45f, 0.45f));
|
Fx.Popup(a.Position, counter.ToString(), new Color(1f, 0.45f, 0.45f));
|
||||||
battle.NotifyChanged();
|
battle.NotifyChanged();
|
||||||
yield return Fx.Wait(0.2f);
|
yield return Fx.Wait(0.2f);
|
||||||
|
|
|
||||||
|
|
@ -254,7 +254,9 @@ namespace TKS
|
||||||
var u = SpawnUnit(h.type, Faction.Player, Config.playerSpawnPoints[h.slot], h.name, h.hpMult, h.atkMult, false, h.art);
|
var u = SpawnUnit(h.type, Faction.Player, Config.playerSpawnPoints[h.slot], h.name, h.hpMult, h.atkMult, false, h.art);
|
||||||
u.Def = Mathf.Max(0, Mathf.RoundToInt(Config.GetStats(h.type).def * h.defMult));
|
u.Def = Mathf.Max(0, Mathf.RoundToInt(Config.GetStats(h.type).def * h.defMult));
|
||||||
var hd = SkillCatalog.Hero(h.id);
|
var hd = SkillCatalog.Hero(h.id);
|
||||||
u.Camp = hd != null && hd.faction != null ? hd.faction : ""; // 진영별 이펙트(skill_vfx.csv s_camp)
|
u.Camp = hd != null && hd.faction != null ? hd.faction : ""; // 진영별 이펙트(skill_vfx.csv s_camp) · 스킬 진영 대상
|
||||||
|
u.Female = hd != null && hd.Female; // 스킬 조건 남성·여성(2026-11-10 #1112)
|
||||||
|
u.HeroId = h.id ?? "";
|
||||||
Skills.AttachLineup(u, h); // 액티브·패시브(레벨 · ★5) · 산채 체력은 여기서 최대 HP 반영
|
Skills.AttachLineup(u, h); // 액티브·패시브(레벨 · ★5) · 산채 체력은 여기서 최대 HP 반영
|
||||||
deployed.Add(h);
|
deployed.Add(h);
|
||||||
}
|
}
|
||||||
|
|
@ -349,8 +351,13 @@ namespace TKS
|
||||||
yield return Resolver.Resolve(u, target, moveOnly, o);
|
yield return Resolver.Resolve(u, target, moveOnly, o);
|
||||||
yield return ApplyOverwatch(u, o);
|
yield return ApplyOverwatch(u, o);
|
||||||
|
|
||||||
// 추가 이동 = 첫 명중 n 회(인덕 extra_move_count) · 추가 이동 뒤에는 남은 횟수만
|
// 추가 이동(보병 목표점 명중) → 끝나면 스킬 재행동(이도류·자오곡·백의도강·파로·군령 …) → 행동 끝
|
||||||
if (u.IsAlive && Skills.NextBonus(u, wasBonus, o)) u.GrantBonusMove();
|
if (u.IsAlive && Skills.NextBonus(u, wasBonus, o)) u.GrantBonusMove();
|
||||||
|
else if (u.IsAlive && Skills.TakeReaction(u))
|
||||||
|
{
|
||||||
|
Skills.ClearBonus(u);
|
||||||
|
u.GrantReAction();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
u.MarkActed();
|
u.MarkActed();
|
||||||
|
|
@ -439,9 +446,11 @@ namespace TKS
|
||||||
foreach (var e in AliveUnits(Faction.Enemy)) e.ResetForNewTurn();
|
foreach (var e in AliveUnits(Faction.Enemy)) e.ResetForNewTurn();
|
||||||
if (Gimmicks != null)
|
if (Gimmicks != null)
|
||||||
{
|
{
|
||||||
yield return Gimmicks.TurnStart(Faction.Enemy); // 군기 · 중독 · 불 · 행동 불가(PD #1097)
|
yield return Gimmicks.TurnStart(Faction.Enemy); // 군기 · 중독 · 불 · 행동 불가(PD #1097) · 스킬 기절
|
||||||
if (CheckBattleEnd()) yield break;
|
if (CheckBattleEnd()) yield break;
|
||||||
}
|
}
|
||||||
|
yield return Skills.TurnBegin(Faction.Enemy); // 스킬 라운드(아군 턴 끝 회복·정화 · 시한 효과 · 중독·화상 · 지대 · 2026-11-10 #1112)
|
||||||
|
if (CheckBattleEnd()) yield break;
|
||||||
yield return Fx.Wait(0.4f);
|
yield return Fx.Wait(0.4f);
|
||||||
|
|
||||||
float accuracy = Stage.aiAccuracy;
|
float accuracy = Stage.aiAccuracy;
|
||||||
|
|
@ -497,6 +506,7 @@ namespace TKS
|
||||||
LastMessage = Describe(e, o, false);
|
LastMessage = Describe(e, o, false);
|
||||||
NotifyChanged();
|
NotifyChanged();
|
||||||
acted++;
|
acted++;
|
||||||
|
if (e.IsAlive && Skills.TakeReaction(e)) pending.Add(e); // 적 스킬 재행동
|
||||||
if (CheckBattleEnd()) yield break;
|
if (CheckBattleEnd()) yield break;
|
||||||
yield return Fx.Wait(0.25f);
|
yield return Fx.Wait(0.25f);
|
||||||
}
|
}
|
||||||
|
|
@ -513,9 +523,11 @@ namespace TKS
|
||||||
TurnNumber++;
|
TurnNumber++;
|
||||||
if (Gimmicks != null)
|
if (Gimmicks != null)
|
||||||
{
|
{
|
||||||
yield return Gimmicks.TurnStart(Faction.Player); // 군기 · 불 지속 · 중독 · 불 · 행동 불가(PD #1097)
|
yield return Gimmicks.TurnStart(Faction.Player); // 군기 · 불 지속 · 중독 · 불 · 행동 불가(PD #1097) · 스킬 기절
|
||||||
if (CheckBattleEnd()) yield break;
|
if (CheckBattleEnd()) yield break;
|
||||||
}
|
}
|
||||||
|
yield return Skills.TurnBegin(Faction.Player); // 스킬 라운드(적 턴 끝 처리 · 시한 효과 · 중독·화상 · 회복 · 지대 · 망령 소멸)
|
||||||
|
if (CheckBattleEnd()) yield break;
|
||||||
LastMessage = TurnNumber + "번째 턴 — 아군 차례" + (acted == 0 ? " (적은 교전 범위 밖에서 대기)" : "");
|
LastMessage = TurnNumber + "번째 턴 — 아군 차례" + (acted == 0 ? " (적은 교전 범위 밖에서 대기)" : "");
|
||||||
if (AliveCount(Faction.Player) > 0 && AllPlayersActed())
|
if (AliveCount(Faction.Player) > 0 && AllPlayersActed())
|
||||||
{
|
{
|
||||||
|
|
@ -551,8 +563,8 @@ namespace TKS
|
||||||
{
|
{
|
||||||
if (spear.Type != UnitType.Spear || !spear.Overwatch) continue;
|
if (spear.Type != UnitType.Spear || !spear.Overwatch) continue;
|
||||||
if (!mover.IsAlive && o.Displaced.Count == 0) yield break;
|
if (!mover.IsAlive && o.Displaced.Count == 0) yield break;
|
||||||
float radius = SkillMods.OverwatchRadius(spear);
|
float radius = spear.AttackRange; // 옛 견제(대기 유닛 없음 · 스킬 확장 제거 2026-11-10 #1112)
|
||||||
bool unlimited = SkillMods.OverwatchUnlimited(spear);
|
bool unlimited = false;
|
||||||
var victims = new List<Unit>();
|
var victims = new List<Unit>();
|
||||||
int passes = 0;
|
int passes = 0;
|
||||||
for (int i = 0; path != null && i < path.Count - 1; i++)
|
for (int i = 0; path != null && i < path.Count - 1; i++)
|
||||||
|
|
@ -575,7 +587,7 @@ namespace TKS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (victims.Count == 0) continue;
|
if (victims.Count == 0) continue;
|
||||||
int strikes = Mathf.Max(1, SkillMods.Count(spear, SkillKeys.OverwatchCount, 1, true));
|
const int strikes = 1;
|
||||||
foreach (var v in victims)
|
foreach (var v in victims)
|
||||||
{
|
{
|
||||||
for (int k = 0; k < strikes; k++)
|
for (int k = 0; k < strikes; k++)
|
||||||
|
|
@ -584,9 +596,8 @@ namespace TKS
|
||||||
int dmg = Combat.OverwatchDamage(Config, spear, v, Rng);
|
int dmg = Combat.OverwatchDamage(Config, spear, v, Rng);
|
||||||
if (spear.View != null) spear.View.PlayAttack();
|
if (spear.View != null) spear.View.PlayAttack();
|
||||||
yield return Fx.Lunge(spear, v.Position);
|
yield return Fx.Lunge(spear, v.Position);
|
||||||
dmg = SkillSystem.Deal(v, dmg);
|
dmg = SkillSystem.Deal(v, dmg, spear, HitKind.Attack);
|
||||||
Fx.Popup(v.Position, dmg + " 견제", new Color(0.6f, 0.85f, 1f));
|
Fx.Popup(v.Position, dmg + " 견제", new Color(0.6f, 0.85f, 1f));
|
||||||
if (!v.IsAlive) HealOnOverwatchKill(spear);
|
|
||||||
NotifyChanged();
|
NotifyChanged();
|
||||||
yield return Fx.Wait(0.2f);
|
yield return Fx.Wait(0.2f);
|
||||||
}
|
}
|
||||||
|
|
@ -594,23 +605,29 @@ namespace TKS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>하북 명장: 견제로 적을 쓰러뜨리면 최대 HP × p 회복.</summary>
|
// ---------- 승패 ----------
|
||||||
void HealOnOverwatchKill(Unit spear)
|
|
||||||
|
/// <summary>스킬 소환(장로 망령)을 뺀 생존 수 — 승패 판정용.</summary>
|
||||||
|
int RealAlive(Faction f)
|
||||||
{
|
{
|
||||||
float pct = SkillMods.Sum(spear, SkillKeys.HealOnOverwatchKill, true);
|
int n = 0;
|
||||||
if (pct <= 0f || !spear.IsAlive) return;
|
foreach (var u in units)
|
||||||
int heal = Mathf.Max(1, Mathf.RoundToInt(spear.MaxHp * pct));
|
if (u != null && u.IsAlive && u.Faction == f && !SkillSystem.IsSummon(u)) n++;
|
||||||
int before = spear.Hp;
|
return n;
|
||||||
spear.Hp = Mathf.Min(spear.MaxHp, spear.Hp + heal);
|
|
||||||
if (spear.View != null) spear.View.Refresh();
|
|
||||||
if (spear.Hp > before) Fx.Popup(spear.Position, "+" + (spear.Hp - before), SkillFx.HealColor);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 승패 ----------
|
/// <summary>유닛 제거(소환 소멸 · 콜라이더 즉시 제거).</summary>
|
||||||
|
public void RemoveUnit(Unit u)
|
||||||
|
{
|
||||||
|
if (u == null) return;
|
||||||
|
units.Remove(u);
|
||||||
|
u.gameObject.SetActive(false);
|
||||||
|
Destroy(u.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
public bool CheckBattleEnd()
|
public bool CheckBattleEnd()
|
||||||
{
|
{
|
||||||
if (AliveCount(Faction.Enemy) == 0)
|
if (RealAlive(Faction.Enemy) == 0)
|
||||||
{
|
{
|
||||||
LastMessage = "승리 — 전장의 적을 모두 처치했습니다";
|
LastMessage = "승리 — 전장의 적을 모두 처치했습니다";
|
||||||
foreach (var p in AliveUnits(Faction.Player))
|
foreach (var p in AliveUnits(Faction.Player))
|
||||||
|
|
@ -620,7 +637,7 @@ namespace TKS
|
||||||
SetPhase(BattlePhase.StageClear);
|
SetPhase(BattlePhase.StageClear);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (AliveCount(Faction.Player) == 0)
|
if (RealAlive(Faction.Player) == 0)
|
||||||
{
|
{
|
||||||
LastMessage = "패배 — 아군이 전멸했습니다";
|
LastMessage = "패배 — 아군이 전멸했습니다";
|
||||||
SetPhase(BattlePhase.Defeat);
|
SetPhase(BattlePhase.Defeat);
|
||||||
|
|
|
||||||
|
|
@ -22,9 +22,30 @@ namespace TKS
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int Damage(BalanceConfig cfg, Unit attacker, Unit defender, bool crit, System.Random rng, bool melee, float scale = 1f)
|
public static int Damage(BalanceConfig cfg, Unit attacker, Unit defender, bool crit, System.Random rng, bool melee, float scale = 1f)
|
||||||
{
|
{
|
||||||
|
return DamageCore(cfg, attacker, defender, crit, rng, melee, scale, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>마지막 피해 계산이 치명타였나(명중 상태 이상 「치명타 시」 조건 · SkillSystem.Hit 가 읽음 · 2026-11-10 #1112).</summary>
|
||||||
|
public static bool LastCrit;
|
||||||
|
|
||||||
|
/// <summary>스킬 피해 「공격력의 ratio 배」(원거리 판정 · 반격 없음 · 치명타 판정 있음).</summary>
|
||||||
|
public static int SkillDamage(BalanceConfig cfg, Unit attacker, Unit defender, float ratio, System.Random rng, bool melee = false)
|
||||||
|
{
|
||||||
|
if (ratio <= 0f) return 0;
|
||||||
|
return DamageCore(cfg, attacker, defender, false, rng, melee, 1f, ratio);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 피해 = (공격 × 공격 배율(스킬) × atkRatio × 상성(불리 무시 하한) × 치명타 − 방어 × defenseFactor) × 근접 배율 × scale × 주는·받는 피해 배율 × 난수.
|
||||||
|
/// 치명타 = 호출 쪽 지정(궁병 목표점 명중) 또는 스킬 「무조건 치명타」·「치명타 확률」 판정(가상 실행 = 확률 판정 없음) · 배율 = critMultiplier × (1 + 치명타 피해 %).
|
||||||
|
/// </summary>
|
||||||
|
static int DamageCore(BalanceConfig cfg, Unit attacker, Unit defender, bool crit, System.Random rng, bool melee, float scale, float atkRatio)
|
||||||
|
{
|
||||||
|
if (!crit) crit = SkillMods.CritAlways(attacker, defender) || RollCrit(attacker, defender, rng);
|
||||||
|
LastCrit = crit;
|
||||||
float mult = Mathf.Max(cfg.Matchup(attacker.Type, defender.Type), SkillMods.MatchupFloor(attacker, true));
|
float mult = Mathf.Max(cfg.Matchup(attacker.Type, defender.Type), SkillMods.MatchupFloor(attacker, true));
|
||||||
float atk = attacker.Atk * SkillMods.AttackMult(attacker, true) * GimmickLayer.AttackMultOf(attacker); // 군기 점령(PD #1097)
|
float atk = attacker.Atk * SkillMods.AttackMult(attacker, true, defender) * atkRatio * GimmickLayer.AttackMultOf(attacker); // 군기 점령(PD #1097)
|
||||||
float raw = atk * mult * (crit ? SkillMods.CritMult(attacker, cfg) : 1f) - defender.Def * cfg.defenseFactor; // 보물 크리 % 포함(PD #1093 C)
|
float raw = atk * mult * (crit ? SkillMods.CritMult(attacker, cfg, defender) : 1f) - defender.Def * cfg.defenseFactor; // 보물 크리 % 포함(PD #1093 C)
|
||||||
if (melee)
|
if (melee)
|
||||||
{
|
{
|
||||||
if (UnitTypeInfo.IsRanged(attacker.Type) && UnitTypeInfo.IsMelee(defender.Type) && !SkillMods.Has(attacker, SkillKeys.MeleePenaltyOff, true)) raw *= cfg.archerMeleeDealtRatio;
|
if (UnitTypeInfo.IsRanged(attacker.Type) && UnitTypeInfo.IsMelee(defender.Type) && !SkillMods.Has(attacker, SkillKeys.MeleePenaltyOff, true)) raw *= cfg.archerMeleeDealtRatio;
|
||||||
|
|
@ -32,10 +53,17 @@ namespace TKS
|
||||||
raw *= GimmickLayer.MeleeTakenMultOf(attacker, defender); // 고지 위 = 아래에서 오는 근접 피해 −(PD #1097)
|
raw *= GimmickLayer.MeleeTakenMultOf(attacker, defender); // 고지 위 = 아래에서 오는 근접 피해 −(PD #1097)
|
||||||
}
|
}
|
||||||
else raw *= GimmickLayer.RangedTakenMultOf(defender); // 안개 안 = 받는 원거리 피해 ×p1(화살 · 책사 중심·광역 · PD 2026-11-06 개정)
|
else raw *= GimmickLayer.RangedTakenMultOf(defender); // 안개 안 = 받는 원거리 피해 ×p1(화살 · 책사 중심·광역 · PD 2026-11-06 개정)
|
||||||
raw *= scale * SkillMods.DealtMult(attacker, defender, true) * SkillMods.TakenMult(defender, attacker, true);
|
raw *= scale * SkillMods.DealtMult(attacker, defender, true) * SkillMods.TakenMult(defender, attacker, true, !melee);
|
||||||
return Mathf.Max(1, Mathf.RoundToInt(raw * Variance(cfg, rng))); // 가상 실행 = 분산 0(PD #1098)
|
return Mathf.Max(1, Mathf.RoundToInt(raw * Variance(cfg, rng))); // 가상 실행 = 분산 0(PD #1098)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool RollCrit(Unit a, Unit d, System.Random rng)
|
||||||
|
{
|
||||||
|
float c = SkillMods.CritChance(a, d);
|
||||||
|
if (c <= 0f || rng == null || Sim.Active) return false;
|
||||||
|
return rng.NextDouble() < c;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>책사 광역 피해(PD 2026-11-05 #1087 ⑤) = 대상별 사격 피해(상성 적용 · 크리 없음) × ratio · ratio ≤ 0 이면 0.</summary>
|
/// <summary>책사 광역 피해(PD 2026-11-05 #1087 ⑤) = 대상별 사격 피해(상성 적용 · 크리 없음) × ratio · ratio ≤ 0 이면 0.</summary>
|
||||||
public static int SplashDamage(BalanceConfig cfg, Unit attacker, Unit defender, float ratio, System.Random rng)
|
public static int SplashDamage(BalanceConfig cfg, Unit attacker, Unit defender, float ratio, System.Random rng)
|
||||||
{
|
{
|
||||||
|
|
@ -50,18 +78,18 @@ namespace TKS
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static int CounterDamage(BalanceConfig cfg, Unit defender, Unit attacker, System.Random rng, bool defenderPushed = false)
|
public static int CounterDamage(BalanceConfig cfg, Unit defender, Unit attacker, System.Random rng, bool defenderPushed = false)
|
||||||
{
|
{
|
||||||
float taken = SkillMods.PctProduct(attacker, SkillKeys.CounterTakenPct, true);
|
float taken = SkillMods.CounterTakenMult(attacker, defender, true); // 받는 반격 %(조건 = 반격하는 상대 · 2026-11-10 #1112)
|
||||||
if (taken <= 0f) return 0;
|
if (taken <= 0f) return 0;
|
||||||
float ratio = SkillMods.CounterRatio(defender, cfg.counterRatio, defenderPushed, true);
|
float ratio = cfg.counterRatio * SkillMods.CounterDmgMult(defender, true); // 반격 피해 +%
|
||||||
int baseDmg = Damage(cfg, defender, attacker, false, rng, true);
|
int baseDmg = Damage(cfg, defender, attacker, false, rng, true);
|
||||||
return Mathf.Max(1, Mathf.RoundToInt(baseDmg * ratio * taken));
|
return Mathf.Max(1, Mathf.RoundToInt(baseDmg * ratio * taken));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>견제 피해 = 근접 피해 × overwatchDamageRatio × 교변(overwatch_dmg_mult).</summary>
|
/// <summary>견제 피해(옛 창병 대기 · 대기 유닛이 없어 쓰이지 않음) = 근접 피해 × overwatchDamageRatio.</summary>
|
||||||
public static int OverwatchDamage(BalanceConfig cfg, Unit spear, Unit mover, System.Random rng)
|
public static int OverwatchDamage(BalanceConfig cfg, Unit spear, Unit mover, System.Random rng)
|
||||||
{
|
{
|
||||||
int baseDmg = Damage(cfg, spear, mover, false, rng, true);
|
int baseDmg = Damage(cfg, spear, mover, false, rng, true);
|
||||||
return Mathf.Max(1, Mathf.RoundToInt(baseDmg * cfg.overwatchDamageRatio * SkillMods.Product(spear, SkillKeys.OverwatchDmgMult, true)));
|
return Mathf.Max(1, Mathf.RoundToInt(baseDmg * cfg.overwatchDamageRatio));
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 기하 질의 ----------
|
// ---------- 기하 질의 ----------
|
||||||
|
|
@ -93,6 +121,39 @@ namespace TKS
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>경로 위 모든 유닛(아군·적군 · 가까운 순 · 접촉 거리) — 스킬 관통(손견·안량·동탁 · 아군 통과 시).</summary>
|
||||||
|
public static List<Unit> UnitsOnPath(Unit actor, Vector2 origin, Vector2 dir, float dist, List<float> distances, float radius = -1f)
|
||||||
|
{
|
||||||
|
var list = new List<Unit>();
|
||||||
|
distances.Clear();
|
||||||
|
Physics2D.SyncTransforms();
|
||||||
|
var hits = Physics2D.CircleCastAll(origin, radius > 0f ? radius : actor.Radius * CastRadiusScale, dir, dist);
|
||||||
|
System.Array.Sort(hits, (a, b) => a.distance.CompareTo(b.distance));
|
||||||
|
foreach (var h in hits)
|
||||||
|
{
|
||||||
|
var u = UnitOf(h.collider);
|
||||||
|
if (u == null || u == actor || !u.IsAlive || list.Contains(u)) continue;
|
||||||
|
list.Add(u);
|
||||||
|
distances.Add(h.distance);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>전장 벽까지 나아갈 수 있는 거리와 그 벽의 면 방향(핀볼 튕김 · 손책·동탁). 벽에 안 닿으면 normal = 0.</summary>
|
||||||
|
public static float BoardWallTravel(Unit actor, Vector2 origin, Vector2 dir, float dist, out Vector2 normal)
|
||||||
|
{
|
||||||
|
normal = Vector2.zero;
|
||||||
|
Physics2D.SyncTransforms();
|
||||||
|
float best = dist;
|
||||||
|
foreach (var h in Physics2D.CircleCastAll(origin, actor.Radius * CastRadiusScale, dir, dist))
|
||||||
|
{
|
||||||
|
if (h.collider.GetComponent<BoardWall>() == null || h.distance >= best) continue;
|
||||||
|
best = h.distance;
|
||||||
|
normal = h.normal;
|
||||||
|
}
|
||||||
|
return Mathf.Max(0f, best - Epsilon);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>경로(원 캐스트) 위 가장 가까운 유닛(아군·적군 불문). hitDist = 닿기까지 이동 거리. 벽은 무시한다(호출 쪽에서 FreeTravel 과 비교).</summary>
|
/// <summary>경로(원 캐스트) 위 가장 가까운 유닛(아군·적군 불문). hitDist = 닿기까지 이동 거리. 벽은 무시한다(호출 쪽에서 FreeTravel 과 비교).</summary>
|
||||||
public static Unit FirstUnitOnPath(Unit actor, Vector2 origin, Vector2 dir, float dist, out float hitDist)
|
public static Unit FirstUnitOnPath(Unit actor, Vector2 origin, Vector2 dir, float dist, out float hitDist)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -29,18 +29,49 @@ namespace TKS
|
||||||
/// <summary>근접 병종 슬링샷 도달점(늪 ×p1 · 고지 오르기 ×p3) — Resolve·조준 미리보기 공용. 원거리 병종은 접근·이동 단계에서 따로 계산.</summary>
|
/// <summary>근접 병종 슬링샷 도달점(늪 ×p1 · 고지 오르기 ×p3) — Resolve·조준 미리보기 공용. 원거리 병종은 접근·이동 단계에서 따로 계산.</summary>
|
||||||
public Vector2 ReachPoint(Unit u, Vector2 target)
|
public Vector2 ReachPoint(Unit u, Vector2 target)
|
||||||
{
|
{
|
||||||
if (!Gim || u == null || UnitTypeInfo.IsRanged(u.Type)) return target;
|
if (u == null || UnitTypeInfo.IsRanged(u.Type)) return target;
|
||||||
Vector2 d = target - u.Position;
|
Vector2 d = target - u.Position;
|
||||||
float dist = d.magnitude;
|
float dist = d.magnitude;
|
||||||
if (dist < 0.001f) return target;
|
if (dist < 0.001f) return target;
|
||||||
return u.Position + d / dist * Layer.CostReach(u, u.Position, d / dist, dist);
|
float reach = SlowZoneReach(u, u.Position, d / dist, dist); // 조인 철벽(상대 반경 안 이동 비용 +)
|
||||||
|
if (Gim && !SkillMods.Has(u, SkillKeys.TerrainImmune)) reach = Layer.CostReach(u, u.Position, d / dist, reach); // 축융 = 지형 이동 감소 무시
|
||||||
|
return u.Position + d / dist * reach;
|
||||||
}
|
}
|
||||||
|
|
||||||
static float Reach(Unit u, Vector2 origin, Vector2 dir, float budget)
|
static float Reach(Unit u, Vector2 origin, Vector2 dir, float budget)
|
||||||
{
|
{
|
||||||
|
if (u != null && SkillMods.Has(u, SkillKeys.TerrainImmune)) return budget;
|
||||||
return Gim ? Layer.CostReach(u, origin, dir, budget) : budget;
|
return Gim ? Layer.CostReach(u, origin, dir, budget) : budget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>조인 철벽(zone_slow): 상대 시전자의 행동 반경(기본 슬링 거리) 안을 지나는 구간 = 이동 비용 × (1 + p1).</summary>
|
||||||
|
static float SlowZoneReach(Unit u, Vector2 origin, Vector2 dir, float budget)
|
||||||
|
{
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
if (sys == null) return budget;
|
||||||
|
List<KeyValuePair<Unit, float>> zones = null;
|
||||||
|
foreach (var t in sys.TimedEffects)
|
||||||
|
{
|
||||||
|
if (t.E.Key != SkillKeys.ZoneSlow || t.Faction == u.Faction || t.Owner == null || t.Owner.Unit == null || !t.Owner.Unit.IsAlive) continue;
|
||||||
|
if (zones == null) zones = new List<KeyValuePair<Unit, float>>();
|
||||||
|
zones.Add(new KeyValuePair<Unit, float>(t.Owner.Unit, t.E.P));
|
||||||
|
}
|
||||||
|
if (zones == null) return budget;
|
||||||
|
const float step = 0.05f;
|
||||||
|
float spent = 0f, moved = 0f;
|
||||||
|
while (moved < budget + 10f)
|
||||||
|
{
|
||||||
|
Vector2 p = origin + dir * (moved + step * 0.5f);
|
||||||
|
float cost = 1f;
|
||||||
|
foreach (var z in zones)
|
||||||
|
if (Vector2.Distance(z.Key.Position, p) <= SkillMods.BaseReach(z.Key)) cost = Mathf.Max(cost, 1f + z.Value);
|
||||||
|
if (spent + step * cost > budget) return moved + (budget - spent) / cost;
|
||||||
|
spent += step * cost;
|
||||||
|
moved += step;
|
||||||
|
}
|
||||||
|
return budget;
|
||||||
|
}
|
||||||
|
|
||||||
static void NoteForced(Unit u, Vector2 from, Vector2 to)
|
static void NoteForced(Unit u, Vector2 from, Vector2 to)
|
||||||
{
|
{
|
||||||
if (Gim) Layer.NoteForced(u, from, to);
|
if (Gim) Layer.NoteForced(u, from, to);
|
||||||
|
|
@ -91,18 +122,22 @@ namespace TKS
|
||||||
o.MovedOnly = true;
|
o.MovedOnly = true;
|
||||||
float remaining = Mathf.Max(0f, dist - hitDist);
|
float remaining = Mathf.Max(0f, dist - hitDist);
|
||||||
var d = g.Def;
|
var d = g.Def;
|
||||||
|
var sk = SkillSystem.Current;
|
||||||
|
if (sk != null && sk.Ctx != null && sk.Ctx.Actor == u) sk.Ctx.HitTerrain = true; // 맹획 남만왕: 지형물에 닿음 = 재행동
|
||||||
|
float gm = SkillMods.GimmickMult(u); // 전위 고지악래: 지형 오브젝트 밀기 거리 ×
|
||||||
bool pusher = u.Type == UnitType.Infantry || u.Type == UnitType.Spear;
|
bool pusher = u.Type == UnitType.Infantry || u.Type == UnitType.Spear;
|
||||||
if (pusher && g.Rolls)
|
if (pusher && g.Rolls)
|
||||||
{
|
{
|
||||||
yield return RollBoulder(u, g, dir, remaining * d.spearPush, o); // 보·창 = 바위를 남은 거리 × spear_push 만큼 굴린다 · 자신은 정지
|
yield return RollBoulder(u, g, dir, remaining * d.spearPush * gm, o); // 보·창 = 바위를 남은 거리 × spear_push 만큼 굴린다 · 자신은 정지
|
||||||
|
PushContinue(u, remaining); // 고순: 오브젝트를 밀친 뒤 남은 이동력으로 재행동
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
int dmg = d.DamageBy(u.Type);
|
int dmg = d.DamageBy(u.Type);
|
||||||
if (dmg > 0) yield return DamageGimmick(g, dmg, u, o);
|
if (dmg > 0) yield return DamageGimmick(g, Mathf.RoundToInt(dmg * gm), u, o);
|
||||||
if (!u.IsAlive) yield break;
|
if (!u.IsAlive) yield break;
|
||||||
if (u.Type == UnitType.Spear && dmg > 0)
|
if (u.Type == UnitType.Spear && dmg > 0)
|
||||||
{
|
{
|
||||||
if (d.spearPush > 0.01f && g.BlocksMove) yield return ShoveGimmick(g, dir, d.spearPush, u, o); // 창 = spear_push 만큼 밀고 정지(튕기지 않음)
|
if (d.spearPush > 0.01f && g.BlocksMove) yield return ShoveGimmick(g, dir, d.spearPush * gm, u, o); // 창 = spear_push 만큼 밀고 정지(튕기지 않음)
|
||||||
yield break;
|
yield break;
|
||||||
}
|
}
|
||||||
if (d.bouncePct > 0f) o.Notes.Add(u.DisplayName + " 자기 −" + Layer.PctDamage(u, d.bouncePct, d.nameKo));
|
if (d.bouncePct > 0f) o.Notes.Add(u.DisplayName + " 자기 −" + Layer.PctDamage(u, d.bouncePct, d.nameKo));
|
||||||
|
|
@ -159,7 +194,7 @@ namespace TKS
|
||||||
foreach (var v in victims)
|
foreach (var v in victims)
|
||||||
{
|
{
|
||||||
if (!v.IsAlive) continue;
|
if (!v.IsAlive) continue;
|
||||||
int dmg = SkillSystem.Deal(v, Combat.GimmickDamage(cfg, by, v, mult, rng));
|
int dmg = SkillSystem.Deal(v, Mathf.RoundToInt(Combat.GimmickDamage(cfg, by, v, mult, rng) * SkillMods.GimmickMult(by)), by, HitKind.Gimmick); // 전위 고지악래 ×
|
||||||
if (v.Faction == by.Faction)
|
if (v.Faction == by.Faction)
|
||||||
{
|
{
|
||||||
o.FriendlyHit.Add(v);
|
o.FriendlyHit.Add(v);
|
||||||
|
|
@ -228,7 +263,7 @@ namespace TKS
|
||||||
}
|
}
|
||||||
if (victim != null && victim.Faction != pusher.Faction)
|
if (victim != null && victim.Faction != pusher.Faction)
|
||||||
{
|
{
|
||||||
int dmg = SkillSystem.Deal(victim, Combat.GimmickDamage(cfg, pusher, victim, g.P(1, 1f), rng));
|
int dmg = SkillSystem.Deal(victim, Mathf.RoundToInt(Combat.GimmickDamage(cfg, pusher, victim, g.P(1, 1f), rng) * SkillMods.GimmickMult(pusher)), pusher, HitKind.Gimmick);
|
||||||
o.Attacked.Add(victim);
|
o.Attacked.Add(victim);
|
||||||
o.DamageDealt += dmg;
|
o.DamageDealt += dmg;
|
||||||
if (!Sim.Active) Fx.Popup(victim.Position, dmg + " 바위", new Color(1f, 0.75f, 0.4f));
|
if (!Sim.Active) Fx.Popup(victim.Position, dmg + " 바위", new Color(1f, 0.75f, 0.4f));
|
||||||
|
|
@ -435,7 +470,7 @@ namespace TKS
|
||||||
yield return SpearRoutine(u, from, dir, s, target, moveOnly, o);
|
yield return SpearRoutine(u, from, dir, s, target, moveOnly, o);
|
||||||
break;
|
break;
|
||||||
case UnitType.Cavalry:
|
case UnitType.Cavalry:
|
||||||
yield return CavalryRoutine(u, from, dir, s, target, moveOnly, o, false, new List<Unit>(o.Attacked));
|
yield return CavalryRoutine(u, from, dir, s, target, moveOnly, o, new List<Unit>(o.Attacked));
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
yield return MoveOrHit(u, from, dir, s, o);
|
yield return MoveOrHit(u, from, dir, s, o);
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ namespace TKS
|
||||||
/// <summary>지형 기믹 질의(PD #1097) — 장애물 콜라이더 · 투사체 pass/block/destroy · 안개 · 기믹 피해 공식.</summary>
|
/// <summary>지형 기믹 질의(PD #1097) — 장애물 콜라이더 · 투사체 pass/block/destroy · 안개 · 기믹 피해 공식.</summary>
|
||||||
public static partial class Combat
|
public static partial class Combat
|
||||||
{
|
{
|
||||||
|
/// <summary>true 인 동안 투사체가 장애물 기믹을 통과(황충 노익장 · 스킬 관통 사격 · 호출 쪽이 켜고 끔).</summary>
|
||||||
|
public static bool ArrowIgnoreGimmicks;
|
||||||
|
|
||||||
/// <summary>전장 가장자리 벽까지만(기믹·유닛 무시) 나아갈 수 있는 거리.</summary>
|
/// <summary>전장 가장자리 벽까지만(기믹·유닛 무시) 나아갈 수 있는 거리.</summary>
|
||||||
public static float WallTravel(Unit actor, Vector2 origin, Vector2 dir, float dist)
|
public static float WallTravel(Unit actor, Vector2 origin, Vector2 dir, float dist)
|
||||||
{
|
{
|
||||||
|
|
@ -55,7 +58,7 @@ namespace TKS
|
||||||
var g = GimmickLayer.InstOf(h.collider);
|
var g = GimmickLayer.InstOf(h.collider);
|
||||||
if (g != null)
|
if (g != null)
|
||||||
{
|
{
|
||||||
if (!g.BlocksMove || g.Def.projectile == GimmickProjectile.Pass) continue;
|
if (!g.BlocksMove || g.Def.projectile == GimmickProjectile.Pass || ArrowIgnoreGimmicks) continue; // 황충 노익장 = 장애물 통과(2026-11-10 #1112)
|
||||||
travel = h.distance;
|
travel = h.distance;
|
||||||
gimmick = g;
|
gimmick = g;
|
||||||
return null;
|
return null;
|
||||||
|
|
|
||||||
|
|
@ -430,5 +430,7 @@ namespace TKS
|
||||||
public const string Bridge = "bridge";
|
public const string Bridge = "bridge";
|
||||||
/// <summary>폭약 수레 폭발 · 책사 광역(spawn_on_splash)이 만드는 불 지대의 정의.</summary>
|
/// <summary>폭약 수레 폭발 · 책사 광역(spawn_on_splash)이 만드는 불 지대의 정의.</summary>
|
||||||
public const string BurningDebris = "burning_debris";
|
public const string BurningDebris = "burning_debris";
|
||||||
|
/// <summary>목책(노숙 녹각진이 세우는 장애물 · 2026-11-10 #1112).</summary>
|
||||||
|
public const string Palisade = "palisade";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -635,6 +635,19 @@ namespace TKS
|
||||||
RefreshBadge(u);
|
RefreshBadge(u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>스킬 정화(제갈량·방통·간옹 · 2026-11-10 #1112): 중독·행동 불가 해제. 반환 = 지운 것이 있었다.</summary>
|
||||||
|
public bool Cleanse(Unit u)
|
||||||
|
{
|
||||||
|
var s = StateOf(u);
|
||||||
|
if (s == null) return false;
|
||||||
|
bool any = s.PoisonTurns > 0 || s.Stun > 0 || s.Skip;
|
||||||
|
s.PoisonTurns = 0;
|
||||||
|
s.Stun = 0;
|
||||||
|
s.Skip = false;
|
||||||
|
RefreshBadge(u);
|
||||||
|
return any;
|
||||||
|
}
|
||||||
|
|
||||||
public void Stun(Unit u, int turns, string why)
|
public void Stun(Unit u, int turns, string why)
|
||||||
{
|
{
|
||||||
if (u == null || turns <= 0) return;
|
if (u == null || turns <= 0) return;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using TKS.OutGame;
|
using TKS.OutGame;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
|
||||||
|
|
@ -14,7 +15,43 @@ namespace TKS
|
||||||
Always
|
Always
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>스킬 효과 1개(effect_key 1개 + 값). 키가 하나뿐인 행은 p2·p3 = 그 키의 조건·보조값.</summary>
|
/// <summary>효과가 걸리는 대상(skill_effects.csv target 칸).</summary>
|
||||||
|
public enum SkillScope
|
||||||
|
{
|
||||||
|
/// <summary>빈칸 = 효과 주인 자신.</summary>
|
||||||
|
Self,
|
||||||
|
/// <summary>allies = 같은 편 전원(자신 포함).</summary>
|
||||||
|
Allies,
|
||||||
|
/// <summary>camp:<진영>[:<병종>] = 같은 편 · 그 진영(heroes.csv faction) · (그 병종).</summary>
|
||||||
|
Camp,
|
||||||
|
/// <summary>camp_male:<진영> = 같은 편 · 그 진영 · 남성.</summary>
|
||||||
|
CampMale,
|
||||||
|
/// <summary>radius:<r> = 자신 주위 r 안 같은 편(자신 제외 · 매번 다시 봄).</summary>
|
||||||
|
Radius,
|
||||||
|
/// <summary>range = 시전 순간 자신 슬링 최대 거리 안 같은 편(자신 포함 · 대상 고정 · 즉시형 지속 효과).</summary>
|
||||||
|
Range,
|
||||||
|
/// <summary>aura = 지금 자신 슬링 최대 거리 안 같은 편(자신 포함 · 매번 다시 봄).</summary>
|
||||||
|
Aura
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>효과 발동 조건(skill_effects.csv cond 칸) — 상대(공격 대상·공격자)·치명타·턴.</summary>
|
||||||
|
public enum SkillCondKind
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Crit,
|
||||||
|
VsStatus,
|
||||||
|
VsBleed,
|
||||||
|
VsConfused,
|
||||||
|
VsStunned,
|
||||||
|
VsType,
|
||||||
|
VsMale,
|
||||||
|
VsMaleHero,
|
||||||
|
VsFemaleHero,
|
||||||
|
TurnLe,
|
||||||
|
Armed
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>스킬 효과 1줄(skill_effects.csv) — 키 · 대상 · 조건 · 지속 · p1~p3.</summary>
|
||||||
public class SkillEffect
|
public class SkillEffect
|
||||||
{
|
{
|
||||||
public string Key;
|
public string Key;
|
||||||
|
|
@ -23,24 +60,101 @@ namespace TKS
|
||||||
public float A3;
|
public float A3;
|
||||||
public bool HasA2;
|
public bool HasA2;
|
||||||
public bool HasA3;
|
public bool HasA3;
|
||||||
|
public SkillScope Scope;
|
||||||
|
public string Camp = "";
|
||||||
|
public bool HasType;
|
||||||
|
public UnitType Type;
|
||||||
|
public float Radius;
|
||||||
|
public SkillCondKind Cond;
|
||||||
|
public UnitType CondType;
|
||||||
|
public int CondN;
|
||||||
|
/// <summary>0 = 상시·장착 중·1회 · n = n 라운드(시한 효과).</summary>
|
||||||
|
public int Dur;
|
||||||
|
|
||||||
|
public float Get(int i, float fallback = 0f)
|
||||||
|
{
|
||||||
|
if (i == 0) return P;
|
||||||
|
if (i == 1) return HasA2 ? A2 : fallback;
|
||||||
|
return HasA3 ? A3 : fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkillEffect Clone()
|
||||||
|
{
|
||||||
|
return (SkillEffect)MemberwiseClone();
|
||||||
|
}
|
||||||
|
|
||||||
public SkillEffect Scaled(float factor)
|
public SkillEffect Scaled(float factor)
|
||||||
{
|
{
|
||||||
SkillKeys.Info info;
|
SkillKeys.Info info;
|
||||||
if (!SkillKeys.All.TryGetValue(Key, out info)) return this;
|
var c = Clone();
|
||||||
return new SkillEffect
|
if (!SkillKeys.All.TryGetValue(Key, out info) || info.Mode == SkillScale.Fixed) return c;
|
||||||
|
if (info.Idx == 0 || info.Both) c.P = SkillKeys.Apply(info.Mode, P, factor);
|
||||||
|
if ((info.Idx == 1 || info.Both) && HasA2) c.A2 = SkillKeys.Apply(info.Mode, A2, factor);
|
||||||
|
if (info.Idx == 2 && HasA3) c.A3 = SkillKeys.Apply(info.Mode, A3, factor);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>target 칸 해석(오류면 메시지 · 빈칸 = 자신).</summary>
|
||||||
|
public static string ParseTarget(string s, SkillEffect e)
|
||||||
{
|
{
|
||||||
Key = Key,
|
e.Scope = SkillScope.Self;
|
||||||
P = SkillKeys.Apply(info.Main, P, factor),
|
if (string.IsNullOrEmpty(s)) return null;
|
||||||
A2 = info.ScaleA2 && HasA2 ? A2 * factor : A2,
|
var parts = s.Split(':');
|
||||||
A3 = A3,
|
switch (parts[0])
|
||||||
HasA2 = HasA2,
|
{
|
||||||
HasA3 = HasA3,
|
case "allies": e.Scope = SkillScope.Allies; return null;
|
||||||
};
|
case "range": e.Scope = SkillScope.Range; return null;
|
||||||
|
case "aura": e.Scope = SkillScope.Aura; return null;
|
||||||
|
case "radius":
|
||||||
|
e.Scope = SkillScope.Radius;
|
||||||
|
if (parts.Length < 2 || !float.TryParse(parts[1], NumberStyles.Float, CultureInfo.InvariantCulture, out e.Radius)) return "radius 값 없음";
|
||||||
|
return null;
|
||||||
|
case "camp":
|
||||||
|
case "camp_male":
|
||||||
|
e.Scope = parts[0] == "camp" ? SkillScope.Camp : SkillScope.CampMale;
|
||||||
|
if (parts.Length < 2 || parts[1].Length == 0) return "진영 없음";
|
||||||
|
e.Camp = parts[1];
|
||||||
|
if (parts.Length >= 3)
|
||||||
|
{
|
||||||
|
if (!UnitTypeInfo.TryParseKey(parts[2], out e.Type)) return "병종 「" + parts[2] + "」 모름";
|
||||||
|
e.HasType = true;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return "대상 「" + s + "」 모름";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>cond 칸 해석(오류면 메시지 · 빈칸 = 조건 없음).</summary>
|
||||||
|
public static string ParseCond(string s, SkillEffect e)
|
||||||
|
{
|
||||||
|
e.Cond = SkillCondKind.None;
|
||||||
|
if (string.IsNullOrEmpty(s)) return null;
|
||||||
|
var parts = s.Split(':');
|
||||||
|
switch (parts[0])
|
||||||
|
{
|
||||||
|
case "crit": e.Cond = SkillCondKind.Crit; return null;
|
||||||
|
case "vs_status": e.Cond = SkillCondKind.VsStatus; return null;
|
||||||
|
case "vs_bleed": e.Cond = SkillCondKind.VsBleed; return null;
|
||||||
|
case "vs_confused": e.Cond = SkillCondKind.VsConfused; return null;
|
||||||
|
case "vs_stunned": e.Cond = SkillCondKind.VsStunned; return null;
|
||||||
|
case "vs_male": e.Cond = SkillCondKind.VsMale; return null;
|
||||||
|
case "vs_male_hero": e.Cond = SkillCondKind.VsMaleHero; return null;
|
||||||
|
case "vs_female_hero": e.Cond = SkillCondKind.VsFemaleHero; return null;
|
||||||
|
case "armed": e.Cond = SkillCondKind.Armed; return null;
|
||||||
|
case "vs_type":
|
||||||
|
e.Cond = SkillCondKind.VsType;
|
||||||
|
if (parts.Length < 2 || !UnitTypeInfo.TryParseKey(parts[1], out e.CondType)) return "vs_type 병종 모름";
|
||||||
|
return null;
|
||||||
|
case "turn_le":
|
||||||
|
e.Cond = SkillCondKind.TurnLe;
|
||||||
|
if (parts.Length < 2 || !int.TryParse(parts[1], out e.CondN)) return "turn_le 턴 없음";
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return "조건 「" + s + "」 모름";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>skills.csv 1행의 전투용 정의(SkillInfoDb.Skill → effect_key 를 키 규약대로 풀어 둠).</summary>
|
/// <summary>skills.csv 1행 + skill_effects.csv 효과 줄의 전투용 정의.</summary>
|
||||||
public class SkillDef
|
public class SkillDef
|
||||||
{
|
{
|
||||||
public string Id;
|
public string Id;
|
||||||
|
|
@ -54,7 +168,7 @@ namespace TKS
|
||||||
public bool NeedsImpl;
|
public bool NeedsImpl;
|
||||||
public readonly List<SkillEffect> Effects = new List<SkillEffect>();
|
public readonly List<SkillEffect> Effects = new List<SkillEffect>();
|
||||||
|
|
||||||
public static SkillDef From(SkillInfoDb.Skill s)
|
public static SkillDef From(SkillInfoDb.Skill s, SkillInfoDb db)
|
||||||
{
|
{
|
||||||
var d = new SkillDef
|
var d = new SkillDef
|
||||||
{
|
{
|
||||||
|
|
@ -68,11 +182,14 @@ namespace TKS
|
||||||
LvScale = s.lvScale,
|
LvScale = s.lvScale,
|
||||||
NeedsImpl = s.needsImpl,
|
NeedsImpl = s.needsImpl,
|
||||||
};
|
};
|
||||||
var keys = s.EffectKeys;
|
if (db != null)
|
||||||
for (int i = 0; i < keys.Length; i++)
|
foreach (var r in db.effects)
|
||||||
{
|
{
|
||||||
if (keys.Length > 1) d.Effects.Add(new SkillEffect { Key = keys[i], P = s.P(i) });
|
if (r.skillId != s.id) continue;
|
||||||
else d.Effects.Add(new SkillEffect { Key = keys[i], P = s.p1, A2 = s.p2, A3 = s.p3, HasA2 = s.HasP(1), HasA3 = s.HasP(2) });
|
var e = new SkillEffect { Key = r.key, P = r.p1, A2 = r.p2, A3 = r.p3, HasA2 = r.HasP(1), HasA3 = r.HasP(2), Dur = r.dur };
|
||||||
|
SkillEffect.ParseTarget(r.target, e);
|
||||||
|
SkillEffect.ParseCond(r.cond, e);
|
||||||
|
d.Effects.Add(e);
|
||||||
}
|
}
|
||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
@ -98,6 +215,11 @@ namespace TKS
|
||||||
if (e.Key == key) return e;
|
if (e.Key == key) return e;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool Has(string key)
|
||||||
|
{
|
||||||
|
return First(key) != null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>스킬·영웅 표 조회(Resources/OutGame 의 SkillInfoDb · HeroDb = 임포트 자산 · 전투 시작마다 다시 읽음).</summary>
|
/// <summary>스킬·영웅 표 조회(Resources/OutGame 의 SkillInfoDb · HeroDb = 임포트 자산 · 전투 시작마다 다시 읽음).</summary>
|
||||||
|
|
@ -116,15 +238,16 @@ namespace TKS
|
||||||
{
|
{
|
||||||
var db = OutGameData.Default;
|
var db = OutGameData.Default;
|
||||||
var s = db != null ? db.Skills : null;
|
var s = db != null ? db.Skills : null;
|
||||||
if (defs != null && s == source && s != null && s.skills.Count == sourceCount) return;
|
int n = s != null ? s.skills.Count + s.effects.Count * 1000 : 0;
|
||||||
|
if (defs != null && s == source && s != null && n == sourceCount) return;
|
||||||
defs = new Dictionary<string, SkillDef>();
|
defs = new Dictionary<string, SkillDef>();
|
||||||
source = s;
|
source = s;
|
||||||
sourceCount = s != null ? s.skills.Count : 0;
|
sourceCount = n;
|
||||||
if (s == null) return;
|
if (s == null) return;
|
||||||
foreach (var row in s.skills)
|
foreach (var row in s.skills)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(row.id) || defs.ContainsKey(row.id)) continue;
|
if (string.IsNullOrEmpty(row.id) || defs.ContainsKey(row.id)) continue;
|
||||||
defs[row.id] = SkillDef.From(row);
|
defs[row.id] = SkillDef.From(row, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,152 +5,205 @@ namespace TKS
|
||||||
/// <summary>스킬 수치가 레벨(lv_scale)·★5(패시브 배율)로 커지는 방식.</summary>
|
/// <summary>스킬 수치가 레벨(lv_scale)·★5(패시브 배율)로 커지는 방식.</summary>
|
||||||
public enum SkillScale
|
public enum SkillScale
|
||||||
{
|
{
|
||||||
/// <summary>값 × 계수(비율·가산·거리·반경 · 기획서 §5 「수치 × (1 + lv_scale·(lv−1))」).</summary>
|
/// <summary>값 × 계수(비율·가산 · 기획서 §5 「수치 × (1 + lv_scale·(lv−1))」).</summary>
|
||||||
Value,
|
Value,
|
||||||
/// <summary>배율(×k)은 1 을 넘는 몫만 키운다: 1 + (k − 1) × 계수 (×2 가 Lv5 에 ×2.8 이 되지 않게).</summary>
|
/// <summary>배율(×k)은 1 을 넘는 몫만 키운다: 1 + (k − 1) × 계수 (×2 가 Lv5 에 ×2.8 이 되지 않게).</summary>
|
||||||
Mult,
|
Mult,
|
||||||
/// <summary>횟수 = 반올림(값 × 계수).</summary>
|
/// <summary>횟수 = 반올림(값 × 계수).</summary>
|
||||||
Count,
|
Count,
|
||||||
/// <summary>켜고 끄는 값·치환 값(커지지 않음).</summary>
|
/// <summary>켜고 끄는 값·턴·반경·확률(커지지 않음).</summary>
|
||||||
Fixed
|
Fixed
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// effect_key 55종 처리기 목록(시스템 기획 §5 · skills.csv · 51 + 영웅 52명 확장 4 · PD #1096) — 키 이름 · 레벨 계수 적용 방식 · 같은 편 전원에게 걸리는가.
|
/// effect_key 처리기 목록(2026-11-10 #1112 · PD 엑셀 「장수 목록」 새 기획 · 표 = skill_effects.csv 효과 1줄 = 키 · 대상 · 조건 · 지속 · p1~p3).
|
||||||
/// 기령 제외로 pushed_ally_attack_on_push(대장군)는 표에서 쓰지 않지만 처리기는 남긴다.
|
/// 이 표는 「처리기의 뜻 + 레벨 계수가 붙는 칸」이고 수치는 전부 skill_effects.csv(SkillInfoDb)에서 온다(C45). 표에 없는 키 = 임포트 오류.
|
||||||
/// 이 표는 「처리기의 뜻」이고 수치는 전부 skills.csv(SkillInfoDb)에서 온다(C45). 표에 없는 키는 임포터가 오류로 막는다.
|
/// 보물 진영 효과(range_mult · splash_radius_mult · side_crit_pct)도 같은 질의(SkillMods)를 탄다.
|
||||||
/// p 규약: 키가 여럿이면 p1~p3 = 키 순서 · 키 하나면 p1 = 값 · p2/p3 = 그 키의 조건·보조값(desc_ko [ ] 메모).
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SkillKeys
|
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 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 PushMult = "push_mult";
|
||||||
public const string SelfBounceMult = "self_bounce_mult";
|
public const string SelfBounceMult = "self_bounce_mult";
|
||||||
public const string ExtraMoveCount = "extra_move_count";
|
public const string KillStackAtk = "kill_stack_atk";
|
||||||
public const string ExtraMoveAttack = "extra_move_attack";
|
public const string AtkMissingHp = "atk_missing_hp";
|
||||||
public const string OverwatchRadiusMult = "overwatch_radius_mult";
|
public const string AtkPerAllyRange = "atk_per_ally_range";
|
||||||
public const string OverwatchUnlimited = "overwatch_unlimited";
|
|
||||||
public const string OverwatchRadiusAdd = "overwatch_radius_add";
|
|
||||||
public const string OverwatchCount = "overwatch_count";
|
|
||||||
public const string OverwatchDmgMult = "overwatch_dmg_mult";
|
|
||||||
public const string CounterRatio = "counter_ratio";
|
|
||||||
public const string CounterTakenPct = "counter_taken_pct";
|
|
||||||
public const string PierceNoCounter = "pierce_no_counter";
|
|
||||||
public const string PierceReturnOnMiss = "pierce_return_on_miss";
|
|
||||||
public const string PierceKnockbackMult = "pierce_knockback_mult";
|
|
||||||
public const string PierceDmgPctPerHit = "pierce_dmg_pct_per_hit";
|
|
||||||
public const string PierceRepeat = "pierce_repeat";
|
|
||||||
public const string PierceDmgMult = "pierce_dmg_mult";
|
|
||||||
public const string ArrowCritAlways = "arrow_crit_always";
|
|
||||||
public const string ArrowPassAllies = "arrow_pass_allies";
|
|
||||||
public const string RangeMult = "range_mult";
|
|
||||||
public const string RangeAdd = "range_add";
|
|
||||||
public const string MeleePenaltyOff = "melee_penalty_off";
|
|
||||||
public const string SplashRadiusMult = "splash_radius_mult";
|
|
||||||
public const string SplashRatio = "splash_ratio";
|
|
||||||
public const string SplashAllyRatio = "splash_ally_ratio";
|
|
||||||
public const string MatchupFloor = "matchup_floor";
|
|
||||||
public const string DmgPct = "dmg_pct";
|
|
||||||
public const string DmgPctHpBelow = "dmg_pct_hp_below";
|
|
||||||
public const string DmgPctTurn1 = "dmg_pct_turn1";
|
|
||||||
public const string DmgVsGeneralMult = "dmg_vs_general_mult";
|
|
||||||
public const string DmgTakenPct = "dmg_taken_pct";
|
|
||||||
public const string DmgTakenAura = "dmg_taken_aura";
|
|
||||||
public const string DmgTakenFromArcherPct = "dmg_taken_from_archer_pct";
|
|
||||||
public const string PushedAllyAttackPct = "pushed_ally_attack_pct";
|
|
||||||
public const string PushedAllyAttackCount = "pushed_ally_attack_count";
|
|
||||||
public const string KnockbackTakenPct = "knockback_taken_pct";
|
|
||||||
public const string HpMaxPct = "hp_max_pct";
|
|
||||||
public const string HealOnOverwatchKill = "heal_on_overwatch_kill";
|
|
||||||
public const string HpCostAtk = "hp_cost_atk";
|
|
||||||
public const string AoePushFear = "aoe_push_fear";
|
|
||||||
public const string EnemyAggroAdd = "enemy_aggro_add";
|
|
||||||
public const string EnemyAggroOffTurn = "enemy_aggro_off_turn";
|
|
||||||
public const string TeamDmgTakenPctTurn = "team_dmg_taken_pct_turn";
|
|
||||||
public const string GuardShare = "guard_share";
|
|
||||||
public const string Teleport = "teleport";
|
|
||||||
public const string OnAllyDeathAtk = "on_ally_death_atk";
|
public const string OnAllyDeathAtk = "on_ally_death_atk";
|
||||||
public const string TeamAtkPctFull = "team_atk_pct_full";
|
public const string DashDmgPct = "dash_dmg_pct";
|
||||||
public const string SpearHitTwo = "spear_hit_two";
|
public const string DmgPerHit = "dmg_per_hit";
|
||||||
public const string PushedAllyAttackOnPush = "pushed_ally_attack_on_push";
|
public const string FirstHitBonus = "first_hit_bonus";
|
||||||
public const string TeamRangeAdd = "team_range_add";
|
public const string SkillPowerLowHp = "skill_power_low_hp";
|
||||||
// 영웅 52명 확장(PD #1096 · 기획서 §5 끝) — 신규 4종
|
public const string SkillHpCost = "skill_hp_cost";
|
||||||
public const string TeamDmgPctTurn = "team_dmg_pct_turn";
|
public const string LowHpTaken = "low_hp_taken";
|
||||||
public const string CooldownAdd = "cooldown_add";
|
public const string UnusedMoveDr = "unused_move_dr";
|
||||||
public const string Charm = "charm";
|
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 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 HealSelfPct = "heal_self_pct";
|
||||||
|
public const string Cleanse = "cleanse";
|
||||||
|
public const string Teleport = "teleport";
|
||||||
|
public const string GrantReaction = "grant_reaction";
|
||||||
|
|
||||||
public class Info
|
public class Info
|
||||||
{
|
{
|
||||||
public SkillScale Main;
|
public SkillScale Mode;
|
||||||
/// <summary>p2(키 하나인 행의 보조값)도 같은 계수로 키우는가(절대 가산 · 최대치 · 충돌 추가 피해).</summary>
|
/// <summary>레벨·★ 계수가 붙는 칸(0 = p1 · 1 = p2 · 2 = p3).</summary>
|
||||||
public bool ScaleA2;
|
public int Idx;
|
||||||
/// <summary>같은 편 전원에게 걸리는 효과(주인 = 같은 편 · 살아 있을 때).</summary>
|
/// <summary>p2 도 같은 계수(이동 % · 공격 % 두 값).</summary>
|
||||||
public bool Team;
|
public bool Both;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Info I(SkillScale m, bool a2 = false, bool team = false)
|
static Info I(SkillScale m, int idx = 0, bool both = false)
|
||||||
{
|
{
|
||||||
return new Info { Main = m, ScaleA2 = a2, Team = team };
|
return new Info { Mode = m, Idx = idx, Both = both };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static readonly Info Fix = new Info { Mode = SkillScale.Fixed };
|
||||||
|
|
||||||
public static readonly Dictionary<string, Info> All = new Dictionary<string, Info>
|
public static readonly Dictionary<string, Info> All = new Dictionary<string, Info>
|
||||||
{
|
{
|
||||||
{ SlingDistPct, I(SkillScale.Value, true) }, // p1 비율 · p2 절대 가산 · p3 1 = 아군 전원
|
{ AtkPct, I(SkillScale.Value) }, { AtkRatio, I(SkillScale.Mult) }, { DmgPct, I(SkillScale.Value) },
|
||||||
{ PushMult, I(SkillScale.Mult, true) }, // p2 = 밀린 적이 벽·유닛에 닿으면 추가 피해 비율(고순)
|
{ DmgTakenPct, I(SkillScale.Value) }, { DmgTakenRangedPct, I(SkillScale.Value) }, { DmgTakenFromArcherPct, I(SkillScale.Value) },
|
||||||
{ SelfBounceMult, I(SkillScale.Value) },
|
{ CritChance, I(SkillScale.Value) }, { CritDmgPct, I(SkillScale.Value) }, { CritAlways, Fix },
|
||||||
{ ExtraMoveCount, I(SkillScale.Count) },
|
{ CounterTakenPct, I(SkillScale.Value) }, { CounterDmgPct, I(SkillScale.Value) }, { CounterLifesteal, I(SkillScale.Value) },
|
||||||
{ ExtraMoveAttack, I(SkillScale.Fixed) },
|
{ Lifesteal, I(SkillScale.Value) }, { HpMaxPct, I(SkillScale.Value) }, { SlingDistPct, I(SkillScale.Value) },
|
||||||
{ OverwatchRadiusMult, I(SkillScale.Mult) },
|
{ RangeMult, I(SkillScale.Mult) }, { SplashRadiusMult, I(SkillScale.Mult) }, { SplashAllyRatio, Fix },
|
||||||
{ OverwatchUnlimited, I(SkillScale.Fixed) },
|
{ MeleePenaltyOff, Fix }, { MatchupFloor, Fix }, { KnockbackTakenPct, Fix }, { PushMult, I(SkillScale.Mult) },
|
||||||
{ OverwatchRadiusAdd, I(SkillScale.Value) },
|
{ SelfBounceMult, Fix }, { KillStackAtk, I(SkillScale.Value) }, { AtkMissingHp, I(SkillScale.Value) },
|
||||||
{ OverwatchCount, I(SkillScale.Count) },
|
{ AtkPerAllyRange, I(SkillScale.Value) }, { OnAllyDeathAtk, I(SkillScale.Value) }, { DashDmgPct, I(SkillScale.Value) },
|
||||||
{ OverwatchDmgMult, I(SkillScale.Mult) },
|
{ DmgPerHit, I(SkillScale.Value) }, { FirstHitBonus, I(SkillScale.Value) }, { SkillPowerLowHp, I(SkillScale.Value) },
|
||||||
{ CounterRatio, I(SkillScale.Value) }, // p2 1 = 밀렸을 때만
|
{ SkillHpCost, Fix }, { LowHpTaken, Fix }, { UnusedMoveDr, I(SkillScale.Value) }, { RegenPct, I(SkillScale.Value) },
|
||||||
{ CounterTakenPct, I(SkillScale.Value) },
|
{ HealOnActionEnd, I(SkillScale.Value) }, { HealOnKill, I(SkillScale.Value) }, { KillReaction, Fix },
|
||||||
{ PierceNoCounter, I(SkillScale.Fixed) },
|
{ ReactionAtkStack, I(SkillScale.Value) }, { MoveBank, Fix }, { TerrainImmune, Fix }, { Revive, Fix }, { NegateHits, Fix },
|
||||||
{ PierceReturnOnMiss, I(SkillScale.Fixed) },
|
{ DrainAura, I(SkillScale.Value) }, { Guard, Fix }, { GuardCounter, Fix }, { GuardRadiusMult, I(SkillScale.Mult) },
|
||||||
{ PierceKnockbackMult, I(SkillScale.Mult) },
|
{ GuardDr, I(SkillScale.Value) }, { AxeAura, I(SkillScale.Value, 1) }, { ZoneSlow, I(SkillScale.Value) },
|
||||||
{ PierceDmgPctPerHit, I(SkillScale.Value, true) }, // p2 = 최대
|
{ OnAttackedStun, Fix }, { OnAttackedConfuse, Fix }, { MultiMaleStun, Fix }, { TurnEndHeal, I(SkillScale.Value, 1) },
|
||||||
{ PierceRepeat, I(SkillScale.Count) },
|
{ TurnEndHealLowest, I(SkillScale.Value, 1) }, { TurnEndCleanse, Fix }, { TurnStartHealRange, I(SkillScale.Value) },
|
||||||
{ PierceDmgMult, I(SkillScale.Mult) }, // p2 = 최소 관통 수(키 하나일 때)
|
{ DeathBanner, Fix }, { Hot, I(SkillScale.Value) }, { HealCleanse, Fix }, { AllyHitHeal, I(SkillScale.Value) },
|
||||||
{ ArrowCritAlways, I(SkillScale.Fixed) },
|
{ AllyHitBuff, I(SkillScale.Value, 0, true) }, { AllyHitWarp, Fix }, { AllyHitReaction, Fix }, { ActionEndSacrifice, Fix },
|
||||||
{ ArrowPassAllies, I(SkillScale.Fixed) },
|
{ PushContinue, Fix }, { PushedAllyAtkAdd, I(SkillScale.Value) }, { SelfPushedMult, I(SkillScale.Mult) },
|
||||||
{ RangeMult, I(SkillScale.Mult) },
|
{ GimmickMult, I(SkillScale.Mult) }, { Zombie, Fix }, { DoubleHitStun, Fix }, { KillInRangeReward, Fix },
|
||||||
{ RangeAdd, I(SkillScale.Value) },
|
{ Invuln, Fix }, { DamageToAtk, Fix },
|
||||||
{ MeleePenaltyOff, I(SkillScale.Fixed) },
|
{ HitStun, Fix }, { HitBleed, I(SkillScale.Value, 1) }, { HitPoison, I(SkillScale.Value, 1) }, { PoisonStack, Fix },
|
||||||
{ SplashRadiusMult, I(SkillScale.Mult) },
|
{ HitBurn, I(SkillScale.Value, 1) }, { HitConfuse, Fix }, { HitPush, I(SkillScale.Value) },
|
||||||
{ SplashRatio, I(SkillScale.Value) },
|
{ ExtraAction, Fix }, { ReactionOnHit, Fix }, { ReactionOnKill, Fix }, { ReactionOnTerrain, Fix }, { PassAllies, Fix },
|
||||||
{ SplashAllyRatio, I(SkillScale.Value) },
|
{ PierceThrough, Fix }, { SwapOnHit, Fix }, { DragBack, Fix }, { AimPush, Fix }, { ReturnOnHit, Fix }, { ReturnAlways, Fix },
|
||||||
{ MatchupFloor, I(SkillScale.Fixed) },
|
{ ReturnAim, Fix }, { WallBounce, Fix }, { Rampage, Fix }, { RepeatAction, Fix },
|
||||||
{ DmgPct, I(SkillScale.Value) },
|
{ ArrowPassAllies, Fix }, { ShotGiant, I(SkillScale.Mult, 1) }, { ShotPierce, I(SkillScale.Mult) }, { ShotBlast, I(SkillScale.Mult, 1) },
|
||||||
{ DmgPctHpBelow, I(SkillScale.Value) }, // p2 = HP 임계
|
{ ShotFan, Fix }, { Multishot, Fix }, { ShotRicochet, I(SkillScale.Value, 1) }, { ShotSingle, Fix }, { ShotWhirl, Fix },
|
||||||
{ DmgPctTurn1, I(SkillScale.Value) },
|
{ ShotBarricade, Fix }, { ShotFirewall, I(SkillScale.Mult, 2) }, { ShotAsArcher, Fix }, { FireZone, Fix }, { PoisonZone, Fix },
|
||||||
{ DmgVsGeneralMult, I(SkillScale.Mult) }, // p2 1 = 보스만 · 0 = 무장 전체
|
{ SplashHeal, I(SkillScale.Value) }, { SplashHealLowest, I(SkillScale.Value) },
|
||||||
{ DmgTakenPct, I(SkillScale.Value) },
|
{ AoeStrike, I(SkillScale.Mult, 1) }, { AoeDrain, I(SkillScale.Mult) }, { Volley, I(SkillScale.Mult) }, { CharmPull, Fix },
|
||||||
{ DmgTakenAura, I(SkillScale.Value, false, true) }, // p2 = 반경
|
{ HealAtk, I(SkillScale.Value) }, { HealSelfPct, I(SkillScale.Value) }, { Cleanse, Fix }, { Teleport, Fix }, { GrantReaction, Fix },
|
||||||
{ DmgTakenFromArcherPct, I(SkillScale.Value) },
|
|
||||||
{ PushedAllyAttackPct, I(SkillScale.Value, false, true) },
|
|
||||||
{ PushedAllyAttackCount, I(SkillScale.Count) },
|
|
||||||
{ KnockbackTakenPct, I(SkillScale.Value) },
|
|
||||||
{ HpMaxPct, I(SkillScale.Value) },
|
|
||||||
{ HealOnOverwatchKill, I(SkillScale.Value) },
|
|
||||||
{ HpCostAtk, I(SkillScale.Value) }, // p2 = HP 소모(커지지 않음)
|
|
||||||
{ AoePushFear, I(SkillScale.Value) }, // p2 = 반경
|
|
||||||
{ EnemyAggroAdd, I(SkillScale.Value) },
|
|
||||||
{ EnemyAggroOffTurn, I(SkillScale.Fixed) },
|
|
||||||
{ TeamDmgTakenPctTurn, I(SkillScale.Value, false, true) },
|
|
||||||
{ GuardShare, I(SkillScale.Value) }, // p2 = 반경
|
|
||||||
{ Teleport, I(SkillScale.Value) }, // p1 = 반경(0 = 슬링 목표점)
|
|
||||||
{ OnAllyDeathAtk, I(SkillScale.Value) },
|
|
||||||
{ TeamAtkPctFull, I(SkillScale.Value, false, true) }, // p2 = 출전 인원 조건
|
|
||||||
{ SpearHitTwo, I(SkillScale.Count) },
|
|
||||||
{ PushedAllyAttackOnPush, I(SkillScale.Count) },
|
|
||||||
{ TeamRangeAdd, I(SkillScale.Value, false, true) }, // 대상 = 궁병·책사
|
|
||||||
{ TeamDmgPctTurn, I(SkillScale.Value, false, true) }, // 즉시 = 이번 아군 턴 아군 전원 가하는 피해 %
|
|
||||||
{ CooldownAdd, I(SkillScale.Count) }, // p2 1 = 아군 전원 · 0 = 자기만 · 쿨 최소 1
|
|
||||||
{ Charm, I(SkillScale.Fixed) }, // p1 반경 · p2 적 턴 수 · 대상 = 무장 우선
|
|
||||||
{ HealSelfPct, I(SkillScale.Value) },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
public static bool Known(string key)
|
public static bool Known(string key)
|
||||||
|
|
@ -164,7 +217,7 @@ namespace TKS
|
||||||
switch (mode)
|
switch (mode)
|
||||||
{
|
{
|
||||||
case SkillScale.Value: return p * factor;
|
case SkillScale.Value: return p * factor;
|
||||||
case SkillScale.Mult: return 1f + (p - 1f) * factor;
|
case SkillScale.Mult: return p > 0f ? 1f + (p - 1f) * factor : p;
|
||||||
case SkillScale.Count: return UnityEngine.Mathf.Floor(p * factor + 0.5f);
|
case SkillScale.Count: return UnityEngine.Mathf.Floor(p * factor + 0.5f);
|
||||||
}
|
}
|
||||||
return p;
|
return p;
|
||||||
|
|
|
||||||
|
|
@ -4,16 +4,16 @@ using UnityEngine;
|
||||||
namespace TKS
|
namespace TKS
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 스킬 수치 질의 — ActionResolver · Combat · EnemyAI · BattleManager 가 수치를 읽을 때 부른다(시스템 기획 §5 · PD #1093 B).
|
/// 스킬 수치 질의(2026-11-10 #1112 · 효과 표 skill_effects.csv) — ActionResolver · Combat · EnemyAI · BattleManager · SlingshotInput 이 수치를 읽을 때 부른다.
|
||||||
/// Get(unit, key) = 그 유닛에게 지금 걸린 효과(자기 패시브 · 같은 편 패시브(팀 키) · 장착된 예약 액티브 · 시한 효과).
|
/// Get(unit, key, other, crit) = 그 유닛에게 지금 걸린 효과: 패시브(대상 칸 = 자신 · 같은 편 · 진영 · 반경 · 슬링 범위) · 장착된 예약 액티브 · 시한 효과 · 보물 진영 효과.
|
||||||
/// 스킬 시스템이 없거나 효과가 없으면 중립값(합 0 · 곱 1)이라 기존 규칙 그대로다.
|
/// 조건 칸(치명타 · 상대 상태 이상 · 병종 · 성별 · 턴 · 장착 중)은 other(공격 대상 또는 공격자)·crit 으로 판정한다. 효과가 없으면 중립값(합 0 · 곱 1).
|
||||||
/// note = 실제 적용(피해·밀기) 때만 true → 패시브 발동 HUD 한 줄.
|
/// note = 실제 적용 때만 true → 패시브 발동 HUD 한 줄.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class SkillMods
|
public static class SkillMods
|
||||||
{
|
{
|
||||||
static readonly List<SkillHit> Empty = new List<SkillHit>();
|
static readonly List<SkillHit> Empty = new List<SkillHit>();
|
||||||
|
|
||||||
public static List<SkillHit> Get(Unit u, string key)
|
public static List<SkillHit> Get(Unit u, string key, Unit other = null, bool crit = false)
|
||||||
{
|
{
|
||||||
var sys = SkillSystem.Current;
|
var sys = SkillSystem.Current;
|
||||||
if (sys == null || u == null) return Empty;
|
if (sys == null || u == null) return Empty;
|
||||||
|
|
@ -22,20 +22,21 @@ namespace TKS
|
||||||
{
|
{
|
||||||
var src = s.Unit;
|
var src = s.Unit;
|
||||||
if (src == null) continue;
|
if (src == null) continue;
|
||||||
if (s.PassiveEffects != null && src.IsAlive)
|
if (s.PassiveEffects != null && (src.IsAlive || s.BannerLeft > 0))
|
||||||
{
|
{
|
||||||
foreach (var e in s.PassiveEffects)
|
foreach (var e in s.PassiveEffects)
|
||||||
{
|
{
|
||||||
if (e.Key != key || !Applies(src, u, e)) continue;
|
if (e.Key != key || !Applies(s, src, u, e, false) || !CondOk(sys, s, e, u, other, crit)) continue;
|
||||||
if (list == null) list = new List<SkillHit>();
|
if (list == null) list = new List<SkillHit>();
|
||||||
list.Add(new SkillHit { Owner = s, Def = s.Passive, E = e, Passive = true });
|
list.Add(new SkillHit { Owner = s, Def = s.Passive, E = e, Passive = true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (s.Armed && src == u && s.ActiveEffects != null)
|
if ((s.Armed || s.Casting) && src == u && s.ActiveEffects != null)
|
||||||
{
|
{
|
||||||
foreach (var e in s.ActiveEffects)
|
foreach (var e in s.ActiveEffects)
|
||||||
{
|
{
|
||||||
if (e.Key != key) continue;
|
if (e.Key != key || e.Key == SkillKeys.Invuln || !CondOk(sys, s, e, u, other, crit)) continue;
|
||||||
|
if (e.Scope != SkillScope.Self && e.Dur > 0) continue; // 대상이 다른 지속 효과 = 시한 효과로만
|
||||||
if (list == null) list = new List<SkillHit>();
|
if (list == null) list = new List<SkillHit>();
|
||||||
list.Add(new SkillHit { Owner = s, Def = s.Active, E = e, Passive = false });
|
list.Add(new SkillHit { Owner = s, Def = s.Active, E = e, Passive = false });
|
||||||
}
|
}
|
||||||
|
|
@ -43,9 +44,9 @@ namespace TKS
|
||||||
}
|
}
|
||||||
foreach (var t in sys.TimedEffects)
|
foreach (var t in sys.TimedEffects)
|
||||||
{
|
{
|
||||||
if (t.E.Key != key || !sys.WindowActive(t) || !TimedApplies(t, u)) continue;
|
if (t.E.Key != key || !TimedApplies(t, u) || !CondOk(sys, t.Owner, t.E, u, other, crit)) continue;
|
||||||
if (list == null) list = new List<SkillHit>();
|
if (list == null) list = new List<SkillHit>();
|
||||||
list.Add(new SkillHit { Owner = t.Owner, Def = t.Def, E = t.E, Passive = false });
|
list.Add(new SkillHit { Owner = t.Owner, Def = t.Def, E = t.E, Passive = false, Timed = t });
|
||||||
}
|
}
|
||||||
// 진영 상시 효과(보물 수집품 crit·range·splash · PD #1093 C) — 주인 없음 · HUD 한 줄 없음
|
// 진영 상시 효과(보물 수집품 crit·range·splash · PD #1093 C) — 주인 없음 · HUD 한 줄 없음
|
||||||
var side = sys.Battle != null ? sys.Battle.SideEffects(u.Faction) : null;
|
var side = sys.Battle != null ? sys.Battle.SideEffects(u.Faction) : null;
|
||||||
|
|
@ -59,34 +60,37 @@ namespace TKS
|
||||||
return list ?? Empty;
|
return list ?? Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>보물 전용 크리 피해 % 키(skills.csv 키 아님 · SkillKeys.All 에 넣지 않음 · PD #1093 C).</summary>
|
/// <summary>보물 전용 크리 피해 % 키(skills 표 키 아님 · SkillKeys.All 에 넣지 않음 · PD #1093 C).</summary>
|
||||||
public const string SideCritPct = "side_crit_pct";
|
public const string SideCritPct = "side_crit_pct";
|
||||||
|
|
||||||
/// <summary>크리 배율 = 자산 critMultiplier × (1 + 보물 크리 %).</summary>
|
/// <summary>「행동 반경」 = 주인의 기본 슬링 최대 거리(효과로 늘어난 값이 아님 — 질의가 서로 부르지 않게).</summary>
|
||||||
public static float CritMult(Unit a, BalanceConfig cfg)
|
public static float BaseReach(Unit owner)
|
||||||
{
|
{
|
||||||
return cfg.critMultiplier * (1f + Sum(a, SideCritPct));
|
return owner != null ? owner.SlingMaxDistance : 0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsTeam(SkillEffect e)
|
public static bool Within(Unit owner, Unit u, float r)
|
||||||
{
|
{
|
||||||
SkillKeys.Info info;
|
return owner != null && u != null && Vector2.Distance(owner.Position, u.Position) <= r + u.Radius;
|
||||||
if (e.Key == SkillKeys.SlingDistPct) return e.HasA3 && e.A3 >= 0.5f; // p3 1 = 아군 전원
|
|
||||||
// 영웅 52명 확장(PD #1096): 기존 키의 조건 열로 아군 전원 — 원소 받는 피해(p3 출전 인원) · 순욱 반격(p3 1) · 순욱 최대 HP(p2 1)
|
|
||||||
if (e.Key == SkillKeys.DmgTakenPct) return e.HasA3 && e.A3 > 0f;
|
|
||||||
if (e.Key == SkillKeys.CounterRatio) return e.HasA3 && e.A3 >= 0.5f;
|
|
||||||
if (e.Key == SkillKeys.HpMaxPct) return e.HasA2 && e.A2 >= 0.5f;
|
|
||||||
if (e.Key == SkillKeys.CooldownAdd) return e.HasA2 && e.A2 >= 0.5f;
|
|
||||||
return SkillKeys.All.TryGetValue(e.Key, out info) && info.Team;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool Applies(Unit src, Unit u, SkillEffect e)
|
/// <summary>대상 칸 판정(dynamic = 매번 · Range 는 시한 효과에서 고정 대상으로 처리).</summary>
|
||||||
|
public static bool Applies(UnitSkills s, Unit src, Unit u, SkillEffect e, bool timed)
|
||||||
{
|
{
|
||||||
if (!IsTeam(e)) return src == u;
|
if (e.Scope == SkillScope.Self) return src == u;
|
||||||
if (src.Faction != u.Faction) return false;
|
if (src.Faction != u.Faction) return false;
|
||||||
if (e.Key == SkillKeys.TeamRangeAdd) return UnitTypeInfo.IsRanged(u.Type);
|
switch (e.Scope)
|
||||||
if (e.Key == SkillKeys.DmgTakenAura) return Vector2.Distance(src.Position, u.Position) <= (e.HasA2 ? e.A2 : 0f) + u.Radius;
|
{
|
||||||
return true;
|
case SkillScope.Allies: return true;
|
||||||
|
case SkillScope.Camp: return u.Camp == e.Camp && (!e.HasType || u.Type == e.Type);
|
||||||
|
case SkillScope.CampMale: return u.Camp == e.Camp && !u.Female;
|
||||||
|
case SkillScope.Radius: return u != src && Within(src, u, e.Radius);
|
||||||
|
case SkillScope.Range:
|
||||||
|
case SkillScope.Aura:
|
||||||
|
if (!src.IsAlive && s != null && s.BannerLeft > 0) return Vector2.Distance(s.BannerPos, u.Position) <= BaseReach(src) + u.Radius;
|
||||||
|
return Within(src, u, BaseReach(src));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool TimedApplies(SkillSystem.Timed t, Unit u)
|
static bool TimedApplies(SkillSystem.Timed t, Unit u)
|
||||||
|
|
@ -94,11 +98,33 @@ namespace TKS
|
||||||
if (t.Targets != null) return t.Targets.Contains(u);
|
if (t.Targets != null) return t.Targets.Contains(u);
|
||||||
var src = t.Owner != null ? t.Owner.Unit : null;
|
var src = t.Owner != null ? t.Owner.Unit : null;
|
||||||
if (src == null) return false;
|
if (src == null) return false;
|
||||||
if (!IsTeam(t.E)) return src == u;
|
if (t.E.Scope != SkillScope.Self && !src.IsAlive) return false;
|
||||||
if (u.Faction != t.Faction) return false;
|
return Applies(t.Owner, src, u, t.E, true);
|
||||||
if (t.E.Key == SkillKeys.DmgTakenAura)
|
}
|
||||||
return src.IsAlive && Vector2.Distance(src.Position, u.Position) <= (t.E.HasA2 ? t.E.A2 : 0f) + u.Radius;
|
|
||||||
return true;
|
/// <summary>조건 칸 — self = 효과를 받는 유닛 · other = 상대(공격 대상 또는 공격자).</summary>
|
||||||
|
static bool CondOk(SkillSystem sys, UnitSkills owner, SkillEffect e, Unit self, Unit other, bool crit)
|
||||||
|
{
|
||||||
|
switch (e.Cond)
|
||||||
|
{
|
||||||
|
case SkillCondKind.None: return true;
|
||||||
|
case SkillCondKind.Crit: return crit;
|
||||||
|
case SkillCondKind.TurnLe: return sys.Battle != null && sys.Battle.TurnNumber <= e.CondN;
|
||||||
|
case SkillCondKind.Armed: return owner != null && (owner.Armed || owner.Casting);
|
||||||
|
}
|
||||||
|
if (other == null) return false;
|
||||||
|
switch (e.Cond)
|
||||||
|
{
|
||||||
|
case SkillCondKind.VsStatus: return SkillStatus.HasAny(other);
|
||||||
|
case SkillCondKind.VsBleed: return SkillStatus.Bleeding(other);
|
||||||
|
case SkillCondKind.VsConfused: return SkillStatus.Confused(other);
|
||||||
|
case SkillCondKind.VsStunned: return SkillStatus.Stunned(other);
|
||||||
|
case SkillCondKind.VsType: return other.Type == e.CondType;
|
||||||
|
case SkillCondKind.VsMale: return !other.Female;
|
||||||
|
case SkillCondKind.VsMaleHero: return !other.Female && sys.IsGeneral(other);
|
||||||
|
case SkillCondKind.VsFemaleHero: return other.Female && sys.IsGeneral(other);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void Note(SkillHit h, bool note)
|
static void Note(SkillHit h, bool note)
|
||||||
|
|
@ -109,10 +135,10 @@ namespace TKS
|
||||||
|
|
||||||
// ---------- 일반 ----------
|
// ---------- 일반 ----------
|
||||||
|
|
||||||
public static float Sum(Unit u, string key, bool note = false)
|
public static float Sum(Unit u, string key, bool note = false, Unit other = null, bool crit = false)
|
||||||
{
|
{
|
||||||
float s = 0f;
|
float s = 0f;
|
||||||
foreach (var h in Get(u, key))
|
foreach (var h in Get(u, key, other, crit))
|
||||||
{
|
{
|
||||||
s += h.E.P;
|
s += h.E.P;
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
|
|
@ -120,10 +146,10 @@ namespace TKS
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float Product(Unit u, string key, bool note = false)
|
public static float Product(Unit u, string key, bool note = false, Unit other = null)
|
||||||
{
|
{
|
||||||
float p = 1f;
|
float p = 1f;
|
||||||
foreach (var h in Get(u, key))
|
foreach (var h in Get(u, key, other))
|
||||||
{
|
{
|
||||||
p *= h.E.P;
|
p *= h.E.P;
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
|
|
@ -132,10 +158,10 @@ namespace TKS
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Π(1 + 값) · 0 아래로 내려가지 않음(받는 피해 −% 류).</summary>
|
/// <summary>Π(1 + 값) · 0 아래로 내려가지 않음(받는 피해 −% 류).</summary>
|
||||||
public static float PctProduct(Unit u, string key, bool note = false)
|
public static float PctProduct(Unit u, string key, bool note = false, Unit other = null)
|
||||||
{
|
{
|
||||||
float p = 1f;
|
float p = 1f;
|
||||||
foreach (var h in Get(u, key))
|
foreach (var h in Get(u, key, other))
|
||||||
{
|
{
|
||||||
p *= Mathf.Max(0f, 1f + h.E.P);
|
p *= Mathf.Max(0f, 1f + h.E.P);
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
|
|
@ -143,49 +169,45 @@ namespace TKS
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool Has(Unit u, string key, bool note = false)
|
public static bool Has(Unit u, string key, bool note = false, Unit other = null, bool crit = false)
|
||||||
{
|
{
|
||||||
var l = Get(u, key);
|
var l = Get(u, key, other, crit);
|
||||||
foreach (var h in l) Note(h, note);
|
foreach (var h in l) Note(h, note);
|
||||||
return l.Count > 0;
|
return l.Count > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>횟수 = 걸린 효과 중 최댓값 · 없으면 fallback.</summary>
|
/// <summary>첫 효과(수치 칸을 그대로 쓰는 처리기) · 없으면 null.</summary>
|
||||||
public static int Count(Unit u, string key, int fallback, bool note = false)
|
public static SkillEffect First(Unit u, string key, bool note = false)
|
||||||
{
|
{
|
||||||
var l = Get(u, key);
|
var l = Get(u, key);
|
||||||
if (l.Count == 0) return fallback;
|
if (l.Count == 0) return null;
|
||||||
int n = int.MinValue;
|
Note(l[0], note);
|
||||||
foreach (var h in l)
|
return l[0].E;
|
||||||
{
|
|
||||||
n = Mathf.Max(n, Mathf.RoundToInt(h.E.P));
|
|
||||||
Note(h, note);
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 슬링 거리 · 사거리 · 광역 ----------
|
// ---------- 슬링 거리 · 사거리 · 광역 ----------
|
||||||
|
|
||||||
/// <summary>슬링 최대 거리 = 기본 × (1 + 비율 합) + 절대 가산(적토마 p2) — 동남풍(이번 턴 아군 전원) 포함.</summary>
|
/// <summary>
|
||||||
|
/// 슬링 최대 거리 = 기본 × (1 + 이동 % 합) × (1 + 진중 축적) × 혼란 배율 · 재행동 남은 거리 상한(감녕·고순) — 조준 · 실제 이동 · 적 AI 공용.
|
||||||
|
/// </summary>
|
||||||
public static float SlingMax(Unit u, bool note = false)
|
public static float SlingMax(Unit u, bool note = false)
|
||||||
{
|
{
|
||||||
if (u == null) return 0f;
|
if (u == null) return 0f;
|
||||||
float pct = 0f;
|
float pct = Sum(u, SkillKeys.SlingDistPct, note);
|
||||||
float add = 0f;
|
var sys = SkillSystem.Current;
|
||||||
foreach (var h in Get(u, SkillKeys.SlingDistPct))
|
var s = sys != null ? sys.Of(u) : null;
|
||||||
{
|
float bank = s != null ? s.Bank : 0f;
|
||||||
pct += h.E.P;
|
float v = u.SlingMaxDistance * Mathf.Max(0.1f, 1f + pct) * (1f + bank) * SkillStatus.ConfuseMult(u);
|
||||||
if (h.E.HasA2) add += h.E.A2;
|
if (s != null && s.ReactLimit > 0f) v = Mathf.Min(v, s.ReactLimit);
|
||||||
Note(h, note);
|
return Mathf.Max(0.1f, v);
|
||||||
}
|
|
||||||
return Mathf.Max(0.1f, u.SlingMaxDistance * (1f + pct) + add);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>궁병·책사 사거리 = (기본 + range_add + 대도독 team_range_add + 고지 p1(지형 기믹 PD #1097)) × range_mult.</summary>
|
/// <summary>궁병·책사 사거리 = (기본 + 고지 p1(PD #1097)) × range_mult(대상·조건 칸 · 보물) · 근접 병종이 원거리로 쏠 때(축융 비도·주태 검기) = 슬링 거리 기준.</summary>
|
||||||
public static float Range(Unit u, bool note = false)
|
public static float Range(Unit u, bool note = false)
|
||||||
{
|
{
|
||||||
if (u == null) return 0f;
|
if (u == null) return 0f;
|
||||||
float r = u.AttackRange + Sum(u, SkillKeys.RangeAdd, note) + Sum(u, SkillKeys.TeamRangeAdd, note) + GimmickLayer.RangeBonusOf(u);
|
float b = UnitTypeInfo.IsRanged(u.Type) ? u.AttackRange : u.SlingMaxDistance;
|
||||||
|
float r = b + GimmickLayer.RangeBonusOf(u);
|
||||||
return Mathf.Max(0f, r * Product(u, SkillKeys.RangeMult, note));
|
return Mathf.Max(0f, r * Product(u, SkillKeys.RangeMult, note));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -194,21 +216,12 @@ namespace TKS
|
||||||
return cfg.strategistSplashRadius * Product(u, SkillKeys.SplashRadiusMult, note);
|
return cfg.strategistSplashRadius * Product(u, SkillKeys.SplashRadiusMult, note);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>광역 비율 = 화공 치환값(여럿이면 큰 값) · 없으면 자산 값.</summary>
|
|
||||||
public static float SplashRatio(Unit u, BalanceConfig cfg, bool note = false)
|
public static float SplashRatio(Unit u, BalanceConfig cfg, bool note = false)
|
||||||
{
|
{
|
||||||
var l = Get(u, SkillKeys.SplashRatio);
|
return cfg.strategistSplashRatio;
|
||||||
if (l.Count == 0) return cfg.strategistSplashRatio;
|
|
||||||
float r = float.MinValue;
|
|
||||||
foreach (var h in l)
|
|
||||||
{
|
|
||||||
r = Mathf.Max(r, h.E.P);
|
|
||||||
Note(h, note);
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>광역 아군 비율 = 팔문 간파 치환값(여럿이면 작은 값) · 없으면 자산 값.</summary>
|
/// <summary>광역 아군 비율 = 서서·제갈근 치환값(여럿이면 작은 값) · 없으면 자산 값.</summary>
|
||||||
public static float SplashAllyRatio(Unit u, BalanceConfig cfg, bool note = false)
|
public static float SplashAllyRatio(Unit u, BalanceConfig cfg, bool note = false)
|
||||||
{
|
{
|
||||||
var l = Get(u, SkillKeys.SplashAllyRatio);
|
var l = Get(u, SkillKeys.SplashAllyRatio);
|
||||||
|
|
@ -224,7 +237,7 @@ namespace TKS
|
||||||
|
|
||||||
// ---------- 피해 ----------
|
// ---------- 피해 ----------
|
||||||
|
|
||||||
/// <summary>상성 하한(대부 · 0 = 없음).</summary>
|
/// <summary>상성 하한(불리 상성 무시 = 1 · 0 = 없음).</summary>
|
||||||
public static float MatchupFloor(Unit a, bool note = false)
|
public static float MatchupFloor(Unit a, bool note = false)
|
||||||
{
|
{
|
||||||
float f = 0f;
|
float f = 0f;
|
||||||
|
|
@ -236,21 +249,38 @@ namespace TKS
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>공격력 배율 = 1 + 「공 +%」 합(노익장·맹장 HP 조건 · 선봉 1턴 · 고육계 · 복수 · 패자 · 팔백 돌격).</summary>
|
/// <summary>
|
||||||
public static float AttackMult(Unit a, bool note = false)
|
/// 공격력 배율 = (1 + 공 % 합(조건 = 상대 d) + 처치 누적 + 잃은 HP + 범위 안 아군 + 아군 전사 + 돌진 거리 + 재행동 누적 + 고육계 이월) × 「공격력의 X %」(atk_ratio) × 저HP 스킬 위력(주유).
|
||||||
|
/// </summary>
|
||||||
|
public static float AttackMult(Unit a, bool note = false, Unit d = null)
|
||||||
{
|
{
|
||||||
var sys = SkillSystem.Current;
|
var sys = SkillSystem.Current;
|
||||||
if (sys == null || a == null) return 1f;
|
if (sys == null || a == null) return 1f;
|
||||||
float pct = 0f;
|
var s = sys.Of(a);
|
||||||
foreach (var h in Get(a, SkillKeys.DmgPctHpBelow))
|
float pct = Sum(a, SkillKeys.AtkPct, note, d);
|
||||||
|
foreach (var h in Get(a, SkillKeys.KillStackAtk))
|
||||||
{
|
{
|
||||||
float th = h.E.HasA2 ? h.E.A2 : 0f;
|
int kills = s != null ? s.Kills : 0;
|
||||||
if (a.Hp > a.MaxHp * th) continue;
|
if (kills <= 0) continue;
|
||||||
pct += h.E.P;
|
pct += Mathf.Min(h.E.HasA2 ? h.E.A2 : float.MaxValue, h.E.P * kills);
|
||||||
|
Note(h, note);
|
||||||
|
}
|
||||||
|
float missing = a.MaxHp > 0 ? Mathf.Clamp01(1f - (float)a.Hp / a.MaxHp) : 0f;
|
||||||
|
foreach (var h in Get(a, SkillKeys.AtkMissingHp))
|
||||||
|
{
|
||||||
|
if (missing <= 0f) continue;
|
||||||
|
pct += Mathf.Min(h.E.HasA2 && h.E.A2 > 0f ? h.E.A2 : float.MaxValue, h.E.P * missing);
|
||||||
|
Note(h, note);
|
||||||
|
}
|
||||||
|
foreach (var h in Get(a, SkillKeys.AtkPerAllyRange))
|
||||||
|
{
|
||||||
|
int n = 0;
|
||||||
|
foreach (var o in sys.Battle.AliveUnits(a.Faction))
|
||||||
|
if (o != a && Within(a, o, BaseReach(a))) n++;
|
||||||
|
if (n == 0) continue;
|
||||||
|
pct += h.E.P * n;
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
}
|
}
|
||||||
if (sys.Battle.TurnNumber == 1) pct += Sum(a, SkillKeys.DmgPctTurn1, note);
|
|
||||||
pct += Sum(a, SkillKeys.HpCostAtk, note);
|
|
||||||
var deaths = Get(a, SkillKeys.OnAllyDeathAtk);
|
var deaths = Get(a, SkillKeys.OnAllyDeathAtk);
|
||||||
if (deaths.Count > 0)
|
if (deaths.Count > 0)
|
||||||
{
|
{
|
||||||
|
|
@ -262,166 +292,152 @@ namespace TKS
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
foreach (var h in Get(a, SkillKeys.TeamAtkPctFull))
|
var ctx = sys.Ctx;
|
||||||
|
if (ctx != null && ctx.Actor == a && ctx.SlingRatio > 0f)
|
||||||
|
foreach (var h in Get(a, SkillKeys.DashDmgPct))
|
||||||
{
|
{
|
||||||
int need = h.E.HasA2 ? Mathf.RoundToInt(h.E.A2) : 0;
|
pct += h.E.P * ctx.SlingRatio;
|
||||||
if (sys.StartCount(a.Faction) < need) continue;
|
|
||||||
pct += h.E.P;
|
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
}
|
}
|
||||||
foreach (var h in Get(a, SkillKeys.PierceDmgPctPerHit))
|
if (s != null && s.ReactsThisTurn > 0)
|
||||||
|
foreach (var h in Get(a, SkillKeys.ReactionAtkStack))
|
||||||
{
|
{
|
||||||
var s = sys.Of(a);
|
pct += h.E.P * Mathf.Pow(2f, s.ReactsThisTurn - 1);
|
||||||
int hits = s != null ? s.PierceHits : 0;
|
|
||||||
if (hits <= 0) continue;
|
|
||||||
float v = h.E.P * hits;
|
|
||||||
if (h.E.HasA2) v = Mathf.Min(v, h.E.A2);
|
|
||||||
pct += v;
|
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
}
|
}
|
||||||
return Mathf.Max(0f, 1f + pct);
|
if (s != null && s.AtkFlat > 0f && a.Atk > 0) pct += s.AtkFlat / a.Atk; // 고육계: 지난 턴 받은 피해 → 이번 턴 공격력
|
||||||
|
float m = Mathf.Max(0f, 1f + pct);
|
||||||
|
m *= Product(a, SkillKeys.AtkRatio, note);
|
||||||
|
if (s != null && (s.Armed || s.Casting))
|
||||||
|
foreach (var h in Get(a, SkillKeys.SkillPowerLowHp))
|
||||||
|
{
|
||||||
|
float step = h.E.HasA2 && h.E.A2 > 0f ? h.E.A2 : 0.1f;
|
||||||
|
int k = Mathf.FloorToInt(missing / step + 1e-4f);
|
||||||
|
if (k <= 0) continue;
|
||||||
|
m *= 1f + h.E.P * k;
|
||||||
|
Note(h, note);
|
||||||
|
}
|
||||||
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int DeadAllies(Unit a)
|
public static int DeadAllies(Unit a)
|
||||||
{
|
{
|
||||||
var sys = SkillSystem.Current;
|
var sys = SkillSystem.Current;
|
||||||
if (sys == null) return 0;
|
if (sys == null) return 0;
|
||||||
int n = 0;
|
int n = 0;
|
||||||
foreach (var u in sys.Battle.Units)
|
foreach (var u in sys.Battle.Units)
|
||||||
if (u != null && u != a && u.Faction == a.Faction && !u.IsAlive) n++;
|
if (u != null && u != a && u.Faction == a.Faction && !u.IsAlive && !SkillSystem.IsSummon(u)) n++;
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>주는 피해 배율 = (1 + 알까기 피해 %) × 무장 대상 배율(참장 = 영웅·보스 · 파로 = 보스만).</summary>
|
/// <summary>주는 피해 배율 = 1 + 피해 % 합(조건 = 상대 d) + 마초 「맞힌 적 1기당」.</summary>
|
||||||
public static float DealtMult(Unit a, Unit d, bool note = false)
|
public static float DealtMult(Unit a, Unit d, bool note = false)
|
||||||
{
|
{
|
||||||
var sys = SkillSystem.Current;
|
var sys = SkillSystem.Current;
|
||||||
float m = 1f + Sum(a, SkillKeys.TeamDmgPctTurn, note); // 십승십패·사세삼공(이번 턴 아군 전원)
|
float m = 1f + Sum(a, SkillKeys.DmgPct, note, d);
|
||||||
foreach (var h in Get(a, SkillKeys.DmgPct))
|
var ctx = sys != null ? sys.Ctx : null;
|
||||||
|
if (ctx != null && ctx.Actor == a && ctx.HitCount > 0)
|
||||||
|
foreach (var h in Get(a, SkillKeys.DmgPerHit))
|
||||||
{
|
{
|
||||||
// 키 하나인 행의 p2 = 발동 턴(이상 · 여몽 5턴째부터)
|
m += h.E.P * ctx.HitCount;
|
||||||
if (h.E.HasA2 && h.E.A2 >= 1f && sys != null && sys.Battle.TurnNumber < Mathf.RoundToInt(h.E.A2)) continue;
|
|
||||||
m += h.E.P;
|
|
||||||
Note(h, note);
|
|
||||||
}
|
|
||||||
foreach (var h in Get(a, SkillKeys.DmgVsGeneralMult))
|
|
||||||
{
|
|
||||||
bool bossOnly = h.E.HasA2 && h.E.A2 >= 0.5f;
|
|
||||||
bool ok = bossOnly ? d.IsBoss : (sys != null && sys.IsGeneral(d));
|
|
||||||
if (!ok) continue;
|
|
||||||
m *= h.E.P;
|
|
||||||
Note(h, note);
|
Note(h, note);
|
||||||
}
|
}
|
||||||
return Mathf.Max(0f, m);
|
return Mathf.Max(0f, m);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>받는 피해 배율 = 수성·노장 × 간웅(이번 적 턴) × 철벽(반경) × 적 궁병 피해 감소.</summary>
|
/// <summary>받는 피해 배율 = 받는 피해 %(조건 = 공격자 a) × 원거리 받는 피해 × 적 궁병 피해 × 수성(남은 이동 거리) × 노장(잃은 HP).</summary>
|
||||||
public static float TakenMult(Unit d, Unit a, bool note = false)
|
public static float TakenMult(Unit d, Unit a, bool note = false, bool ranged = false)
|
||||||
{
|
{
|
||||||
float m = DmgTakenPct(d, a, note) * PctProduct(d, SkillKeys.TeamDmgTakenPctTurn, note) * PctProduct(d, SkillKeys.DmgTakenAura, note);
|
float m = PctProduct(d, SkillKeys.DmgTakenPct, note, a);
|
||||||
if (a != null && a.Type == UnitType.Archer) m *= PctProduct(d, SkillKeys.DmgTakenFromArcherPct, note);
|
if (ranged) m *= PctProduct(d, SkillKeys.DmgTakenRangedPct, note, a);
|
||||||
|
if (a != null && a.Type == UnitType.Archer) m *= PctProduct(d, SkillKeys.DmgTakenFromArcherPct, note, a);
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
var s = sys != null ? sys.Of(d) : null;
|
||||||
|
if (s != null && s.UnusedMove > 0f)
|
||||||
|
foreach (var h in Get(d, SkillKeys.UnusedMoveDr))
|
||||||
|
{
|
||||||
|
m *= Mathf.Max(0f, 1f - h.E.P * s.UnusedMove);
|
||||||
|
Note(h, note);
|
||||||
|
}
|
||||||
|
float lost = d.MaxHp > 0 ? Mathf.Clamp01(1f - (float)d.Hp / d.MaxHp) * 100f : 0f;
|
||||||
|
if (lost > 0f)
|
||||||
|
foreach (var h in Get(d, SkillKeys.LowHpTaken))
|
||||||
|
{
|
||||||
|
m *= 1f + h.E.P * lost;
|
||||||
|
Note(h, note);
|
||||||
|
}
|
||||||
return m;
|
return m;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
// ---------- 치명타 ----------
|
||||||
/// 받는 피해 % (dmg_taken_pct) — 키 하나인 행의 조건(영웅 52명 확장 · PD #1096): p2 0~1 = HP 그 비율 이하일 때(위연) · p2 2 = 보스에게서만(원술) ·
|
|
||||||
/// p2 3 = 근접 유닛에게서만(초선) · p3 n = 출전 n 인 이상일 때 아군 전원(원소). 조건 열이 없으면 기존 그대로(조인·황개).
|
/// <summary>크리 배율 = 자산 critMultiplier × (1 + 보물 크리 %) × (1 + 치명타 피해 % 합).</summary>
|
||||||
/// </summary>
|
public static float CritMult(Unit a, BalanceConfig cfg, Unit d = null)
|
||||||
static float DmgTakenPct(Unit d, Unit a, bool note)
|
|
||||||
{
|
{
|
||||||
var sys = SkillSystem.Current;
|
return cfg.critMultiplier * (1f + Sum(a, SideCritPct)) * (1f + Sum(a, SkillKeys.CritDmgPct, true, d));
|
||||||
float p = 1f;
|
|
||||||
foreach (var h in Get(d, SkillKeys.DmgTakenPct))
|
|
||||||
{
|
|
||||||
if (h.E.HasA2 && h.E.A2 > 0f)
|
|
||||||
{
|
|
||||||
float c = h.E.A2;
|
|
||||||
if (c < 1f && d.Hp > d.MaxHp * c) continue;
|
|
||||||
if (Mathf.Approximately(c, 2f) && (a == null || !a.IsBoss)) continue;
|
|
||||||
if (Mathf.Approximately(c, 3f) && (a == null || !UnitTypeInfo.IsMelee(a.Type))) continue;
|
|
||||||
}
|
|
||||||
if (h.E.HasA3 && h.E.A3 > 0f && (sys == null || sys.StartCount(d.Faction) < Mathf.RoundToInt(h.E.A3))) continue;
|
|
||||||
p *= Mathf.Max(0f, 1f + h.E.P);
|
|
||||||
Note(h, note);
|
|
||||||
}
|
|
||||||
return p;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>반격 배율 = 무성·무언의 규율(치환) · 진중(p2 1 = 밀렸을 때만).</summary>
|
public static float CritChance(Unit a, Unit d)
|
||||||
public static float CounterRatio(Unit counterer, float baseRatio, bool pushed, bool note = false)
|
|
||||||
{
|
{
|
||||||
float r = baseRatio;
|
return Mathf.Clamp01(Sum(a, SkillKeys.CritChance, false, d));
|
||||||
foreach (var h in Get(counterer, SkillKeys.CounterRatio))
|
|
||||||
{
|
|
||||||
if (h.E.HasA2 && h.E.A2 >= 0.5f && !pushed) continue;
|
|
||||||
if (h.E.P <= r) continue;
|
|
||||||
r = h.E.P;
|
|
||||||
Note(h, note);
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 밀기 · 튕김 · 관통 ----------
|
public static bool CritAlways(Unit a, Unit d)
|
||||||
|
{
|
||||||
|
return Has(a, SkillKeys.CritAlways, true, d);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 반격 ----------
|
||||||
|
|
||||||
|
/// <summary>받는 반격 피해 배율(공격한 유닛 a 기준 · 조건 = 반격하는 상대) · 0 = 반격 안 받음.</summary>
|
||||||
|
public static float CounterTakenMult(Unit a, Unit counterer, bool note = false)
|
||||||
|
{
|
||||||
|
return PctProduct(a, SkillKeys.CounterTakenPct, note, counterer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>주는 반격 피해 배율 = 1 + 반격 피해 % 합 + 노장(잃은 HP 1 %당 p2).</summary>
|
||||||
|
public static float CounterDmgMult(Unit counterer, bool note = false)
|
||||||
|
{
|
||||||
|
float m = 1f + Sum(counterer, SkillKeys.CounterDmgPct, note);
|
||||||
|
float lost = counterer.MaxHp > 0 ? Mathf.Clamp01(1f - (float)counterer.Hp / counterer.MaxHp) * 100f : 0f;
|
||||||
|
if (lost > 0f)
|
||||||
|
foreach (var h in Get(counterer, SkillKeys.LowHpTaken))
|
||||||
|
if (h.E.HasA2) m += h.E.A2 * lost;
|
||||||
|
return Mathf.Max(0f, m);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 밀기 · 튕김 ----------
|
||||||
|
|
||||||
public static float PushMult(Unit actor, bool note = false)
|
public static float PushMult(Unit actor, bool note = false)
|
||||||
{
|
{
|
||||||
return Mathf.Max(0f, Product(actor, SkillKeys.PushMult, note));
|
return Mathf.Max(0f, Product(actor, SkillKeys.PushMult, note));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>함진영: 밀린 적이 벽·유닛에 닿으면 추가 피해 비율(push_mult 행의 p2).</summary>
|
|
||||||
public static float PushCollisionBonus(Unit actor)
|
|
||||||
{
|
|
||||||
float b = 0f;
|
|
||||||
foreach (var h in Get(actor, SkillKeys.PushMult))
|
|
||||||
if (h.E.HasA2) b += h.E.A2;
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static float SelfBounceMult(Unit actor, bool note = false)
|
public static float SelfBounceMult(Unit actor, bool note = false)
|
||||||
{
|
{
|
||||||
return Mathf.Max(0f, Product(actor, SkillKeys.SelfBounceMult, note));
|
return Mathf.Max(0f, Product(actor, SkillKeys.SelfBounceMult, note));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>밀리는 거리 배율(거구 −50 %).</summary>
|
/// <summary>밀리는 거리 배율(거구 −100 % = 안 밀림).</summary>
|
||||||
public static float KnockbackTakenMult(Unit target, bool note = false)
|
public static float KnockbackTakenMult(Unit target, bool note = false)
|
||||||
{
|
{
|
||||||
return PctProduct(target, SkillKeys.KnockbackTakenPct, note);
|
return PctProduct(target, SkillKeys.KnockbackTakenPct, note);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static float PierceKnockbackMult(Unit actor, bool note = false)
|
/// <summary>서황 대부: 도끼가 도는 동안 자신을 공격한 유닛은 × p3 멀리 튕긴다.</summary>
|
||||||
{
|
public static float AxeBounce(Unit struck)
|
||||||
return Mathf.Max(0f, Product(actor, SkillKeys.PierceKnockbackMult, note));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>관통 피해 배율 · 키 하나인 행의 p2 = 최소 관통 수(만인지적 2기 이상).</summary>
|
|
||||||
public static float PierceDmgMult(Unit actor, int pierced, bool note = false)
|
|
||||||
{
|
{
|
||||||
|
if (struck == null) return 1f;
|
||||||
float m = 1f;
|
float m = 1f;
|
||||||
foreach (var h in Get(actor, SkillKeys.PierceDmgMult))
|
foreach (var h in Get(struck, SkillKeys.AxeAura))
|
||||||
{
|
if (h.E.HasA3) m = Mathf.Max(m, h.E.A3);
|
||||||
if (h.E.HasA2 && pierced < Mathf.RoundToInt(h.E.A2)) continue;
|
return m;
|
||||||
m *= h.E.P;
|
|
||||||
Note(h, note);
|
|
||||||
}
|
|
||||||
return Mathf.Max(0f, m);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------- 견제 ----------
|
/// <summary>전위 고지악래: 지형 오브젝트 밀기 거리·그것으로 준 피해 배율.</summary>
|
||||||
|
public static float GimmickMult(Unit u)
|
||||||
/// <summary>견제 반경 = (기본 + 후계 등 패시브 가산 + 이번 대기의 액티브 가산) × 액티브 배율(청룡언월).</summary>
|
|
||||||
public static float OverwatchRadius(Unit spear, bool note = false)
|
|
||||||
{
|
{
|
||||||
if (spear == null) return 0f;
|
return u != null ? Mathf.Max(0f, Product(u, SkillKeys.GimmickMult, true)) : 1f;
|
||||||
var s = SkillSystem.Current != null ? SkillSystem.Current.Of(spear) : null;
|
|
||||||
float add = Sum(spear, SkillKeys.OverwatchRadiusAdd, note) + (s != null ? s.OwAdd : 0f);
|
|
||||||
float mult = s != null ? s.OwMult : 1f;
|
|
||||||
return Mathf.Max(0f, (spear.AttackRange + add) * mult);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool OverwatchUnlimited(Unit spear)
|
|
||||||
{
|
|
||||||
var s = SkillSystem.Current != null ? SkillSystem.Current.Of(spear) : null;
|
|
||||||
return (s != null && s.OwUnlimited) || Has(spear, SkillKeys.OverwatchUnlimited);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace TKS
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 스킬 상태 이상 1기분(2026-11-10 #1112 · PD 기획 정의):
|
||||||
|
/// 혼란 = 그 유닛 차례 동안 이동 거리 × 배율(장료·육손·손권 「이동력 50 % 감소」) · 출혈 = 움직인 거리 비례 최대 HP 피해(서황 「이동 거리의 50 %에 비례」) ·
|
||||||
|
/// 중독 = 그 진영 턴 시작 + 자기 행동 끝마다 최대 HP %(가후·축융 · 가후 패시브 = 중첩) · 화상(불태우기) = 그 진영 턴 시작마다 최대 HP %(서서).
|
||||||
|
/// 기절 = 기존 지형 기믹 「행동 불가」(GimmickLayer.Stun · 다음 자기 턴을 쉼)를 그대로 쓴다.
|
||||||
|
/// 턴 수 = 그 유닛 진영의 턴 기준(자기 턴 중에 걸리면 다음 턴부터 셈).
|
||||||
|
/// </summary>
|
||||||
|
public class UnitStatus
|
||||||
|
{
|
||||||
|
public int ConfuseTurns;
|
||||||
|
public float ConfuseMult = 1f;
|
||||||
|
public int BleedTurns;
|
||||||
|
public float BleedPct;
|
||||||
|
public int PoisonTurns;
|
||||||
|
public float PoisonPct;
|
||||||
|
public int PoisonStacks;
|
||||||
|
public int BurnTurns;
|
||||||
|
public float BurnPct;
|
||||||
|
public TextMesh Badge;
|
||||||
|
|
||||||
|
public UnitStatus Copy()
|
||||||
|
{
|
||||||
|
return (UnitStatus)MemberwiseClone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Any
|
||||||
|
{
|
||||||
|
get { return ConfuseTurns > 0 || BleedTurns > 0 || PoisonTurns > 0 || BurnTurns > 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Clear()
|
||||||
|
{
|
||||||
|
ConfuseTurns = BleedTurns = PoisonTurns = BurnTurns = PoisonStacks = 0;
|
||||||
|
ConfuseMult = 1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SkillStatus
|
||||||
|
{
|
||||||
|
public static readonly Color TextColor = new Color(0.95f, 0.55f, 1f);
|
||||||
|
|
||||||
|
static UnitStatus Of(Unit u)
|
||||||
|
{
|
||||||
|
var sys = SkillSystem.Current;
|
||||||
|
return sys != null ? sys.StatusOf(u) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Confused(Unit u)
|
||||||
|
{
|
||||||
|
var s = Of(u);
|
||||||
|
return s != null && s.ConfuseTurns > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float ConfuseMult(Unit u)
|
||||||
|
{
|
||||||
|
var s = Of(u);
|
||||||
|
return s != null && s.ConfuseTurns > 0 ? Mathf.Clamp(s.ConfuseMult, 0.05f, 1f) : 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Bleeding(Unit u)
|
||||||
|
{
|
||||||
|
var s = Of(u);
|
||||||
|
return s != null && s.BleedTurns > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Poisoned(Unit u)
|
||||||
|
{
|
||||||
|
var s = Of(u);
|
||||||
|
if (s != null && s.PoisonTurns > 0) return true;
|
||||||
|
var g = GimmickLayer.Current != null ? GimmickLayer.Current.StateOf(u) : null;
|
||||||
|
return g != null && g.PoisonTurns > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool Stunned(Unit u)
|
||||||
|
{
|
||||||
|
var g = GimmickLayer.Current != null ? GimmickLayer.Current.StateOf(u) : null;
|
||||||
|
return g != null && (g.Stun > 0 || g.Skip);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>상태 이상 아무것이나(스킬 상태 + 지형 기믹 중독·행동 불가).</summary>
|
||||||
|
public static bool HasAny(Unit u)
|
||||||
|
{
|
||||||
|
var s = Of(u);
|
||||||
|
return (s != null && s.Any) || Poisoned(u) || Stunned(u);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string BadgeText(UnitStatus s)
|
||||||
|
{
|
||||||
|
if (s == null) return "";
|
||||||
|
var parts = new List<string>();
|
||||||
|
if (s.ConfuseTurns > 0) parts.Add("혼란" + s.ConfuseTurns);
|
||||||
|
if (s.BleedTurns > 0) parts.Add("출혈" + s.BleedTurns);
|
||||||
|
if (s.PoisonTurns > 0) parts.Add("독" + s.PoisonTurns + (s.PoisonStacks > 1 ? "×" + s.PoisonStacks : ""));
|
||||||
|
if (s.BurnTurns > 0) parts.Add("화상" + s.BurnTurns);
|
||||||
|
return string.Join(" ", parts.ToArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
fileFormatVersion: 2
|
||||||
|
guid: 9893a7a221db90d46a1bf76b1e18e7f0
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -195,12 +195,13 @@ namespace TKS
|
||||||
void RefreshTeleportArea()
|
void RefreshTeleportArea()
|
||||||
{
|
{
|
||||||
var sys = battle.Skills;
|
var sys = battle.Skills;
|
||||||
var u = sys != null ? sys.PendingTeleport : null;
|
var u = sys != null ? sys.PendingUnit : null;
|
||||||
bool on = u != null && u.IsAlive;
|
bool on = u != null && u.IsAlive && sys.PendingRadius > 0f; // 변진 목표 · 맹장 복귀 지점(조조 방향 탭 = 반경 없음)
|
||||||
if (on)
|
if (on)
|
||||||
{
|
{
|
||||||
float d = SkillSystem.TeleportRadius(sys.Of(u)) * 2f;
|
float d = sys.PendingRadius * 2f;
|
||||||
teleportArea.transform.position = new Vector3(u.Position.x, u.Position.y, 0f);
|
Vector2 c = sys.PendingKind == SkillSystem.TapKind.Teleport ? u.Position : sys.PendingCenter;
|
||||||
|
teleportArea.transform.position = new Vector3(c.x, c.y, 0f);
|
||||||
teleportArea.transform.localScale = new Vector3(d, d, 1f);
|
teleportArea.transform.localScale = new Vector3(d, d, 1f);
|
||||||
}
|
}
|
||||||
if (teleportArea.gameObject.activeSelf != on) teleportArea.gameObject.SetActive(on);
|
if (teleportArea.gameObject.activeSelf != on) teleportArea.gameObject.SetActive(on);
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,10 @@ namespace TKS
|
||||||
|
|
||||||
/// <summary>진영(heroes.csv faction = shu · wei · wu · other) — 출전 영웅만 · 그 밖(적 병사 등) = 빈칸(이펙트 표에서는 other 로 봄 · 2026-11-09 진영별 돌진 이펙트).</summary>
|
/// <summary>진영(heroes.csv faction = shu · wei · wu · other) — 출전 영웅만 · 그 밖(적 병사 등) = 빈칸(이펙트 표에서는 other 로 봄 · 2026-11-09 진영별 돌진 이펙트).</summary>
|
||||||
public string Camp = "";
|
public string Camp = "";
|
||||||
|
/// <summary>여성 영웅(heroes.csv gender f) — 스킬 조건 남성·여성(2026-11-10 #1112) · 그 밖(적 병사 포함) = 남성.</summary>
|
||||||
|
public bool Female;
|
||||||
|
/// <summary>heroes.csv 영웅 id(출전 영웅 · 영웅과 연결된 적 무장) · 그 밖 = 빈칸.</summary>
|
||||||
|
public string HeroId = "";
|
||||||
|
|
||||||
public void TakeDamage(int dmg)
|
public void TakeDamage(int dmg)
|
||||||
{
|
{
|
||||||
|
|
@ -135,6 +139,14 @@ namespace TKS
|
||||||
if (View != null) View.Refresh();
|
if (View != null) View.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>재행동(스킬) — 다시 일반 행동 1회(공격 가능).</summary>
|
||||||
|
public void GrantReAction()
|
||||||
|
{
|
||||||
|
ActedThisTurn = false;
|
||||||
|
BonusMoveAvailable = false;
|
||||||
|
if (View != null) View.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
public void GrantBonusMove()
|
public void GrantBonusMove()
|
||||||
{
|
{
|
||||||
ActedThisTurn = false;
|
ActedThisTurn = false;
|
||||||
|
|
|
||||||
|
|
@ -251,7 +251,7 @@ namespace TKS
|
||||||
bool ow = unit.Overwatch && unit.Type == UnitType.Spear && unit.IsAlive;
|
bool ow = unit.Overwatch && unit.Type == UnitType.Spear && unit.IsAlive;
|
||||||
if (ow)
|
if (ow)
|
||||||
{
|
{
|
||||||
float d = SkillMods.OverwatchRadius(unit) * 2f; // 후계·백마의 창·청룡언월 반영(PD #1093 B)
|
float d = unit.AttackRange * 2f; // 옛 견제 반경(대기 유닛 없음 · 2026-11-10 #1112 스킬 확장 제거)
|
||||||
overwatchArea.transform.localScale = new Vector3(d, d, 1f);
|
overwatchArea.transform.localScale = new Vector3(d, d, 1f);
|
||||||
}
|
}
|
||||||
if (overwatchArea.gameObject.activeSelf != ow) overwatchArea.gameObject.SetActive(ow);
|
if (overwatchArea.gameObject.activeSelf != ow) overwatchArea.gameObject.SetActive(ow);
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -45,9 +45,9 @@ Unity.exe -projectPath <proj> -executeMethod TKS.EditorTools.ProjectSetup.AutoPl
|
||||||
|
|
||||||
- 전투 수치 SOT = `Assets/Resources/BalanceConfig.asset`(Inspector 조정 · 최초 시드만 `Assets/Scripts/Editor/BalanceDefaults.cs`). 코드·테스트에 수치를 직접 적지 않는다(C45).
|
- 전투 수치 SOT = `Assets/Resources/BalanceConfig.asset`(Inspector 조정 · 최초 시드만 `Assets/Scripts/Editor/BalanceDefaults.cs`). 코드·테스트에 수치를 직접 적지 않는다(C45).
|
||||||
- **아웃게임 SOT = `Assets/Data/OutGame/*.csv`(12장 · 기획 `공유/기획팀_산출물/삼국지슬링샷/tables/` 사본 · utf-8-sig · 2026-11-05 #1093 A)** — 영웅·병사(`heroes.csv` · 구 `BalanceConfig.heroes[]` 대체) · 레벨·승급·진화·장비·뽑기·보상·재화·신규 유저 초기값(`economy.csv` start_*). 고친 뒤 메뉴 `TKS/Import OutGame Data (아웃게임 데이터 임포트)`(`Editor/DataImporter` · 오류 시 자산 불변) → `Resources/OutGame/` 9 자산. 표를 바꾸면 기획 `tables/` 도 같이.
|
- **아웃게임 SOT = `Assets/Data/OutGame/*.csv`(12장 · 기획 `공유/기획팀_산출물/삼국지슬링샷/tables/` 사본 · utf-8-sig · 2026-11-05 #1093 A)** — 영웅·병사(`heroes.csv` · 구 `BalanceConfig.heroes[]` 대체) · 레벨·승급·진화·장비·뽑기·보상·재화·신규 유저 초기값(`economy.csv` start_*). 고친 뒤 메뉴 `TKS/Import OutGame Data (아웃게임 데이터 임포트)`(`Editor/DataImporter` · 오류 시 자산 불변) → `Resources/OutGame/` 9 자산. 표를 바꾸면 기획 `tables/` 도 같이.
|
||||||
- **스킬(2026-11-05 #1093 B)** — SOT = `Assets/Data/OutGame/skills.csv`(effect_key `;` 복수 · p1~p3 · lv_scale · needs_impl) + `skill_levels.csv` → 임포트 자산 `SkillInfoDb`(effect 열 포함 · 표시 + 전투 공용). 처리기 = `Runtime/Skills/`: `SkillKeys`(51종 · 레벨 계수 방식 · 팀 키 · 표에 없는 키 = 임포트 오류) · `SkillSystem`(유닛별 쿨·예약 · 즉시형 시한 효과 · 행동 훅 · 호위 피해 분담 · HUD 한 줄) · `SkillMods`(전투 코드가 수치 질의) · `SkillRules`(상한 = 등급 + ★3 · ★5 패시브 배율 = evolve.csv skill_unlock · 레벨업 · 스킬서 판매). 전투 코드에 스킬 수치를 직접 쓰지 않고 `SkillMods` 로 읽는다. 테스트 = `Tests/PlayMode/SkillTests`. 캡처 = `-executeMethod TKS.EditorTools.DataImporter.CaptureSkills -skillDir <dir>`.
|
- **스킬(2026-11-10 #1112 전면 개편 · PD 엑셀 「장수 목록」 새 기획 144개)** — 수치 SOT = **`Assets/Data/OutGame/skill_effects.csv`(효과 1줄 = `skill_id · key · target · cond · dur · p1~p3` + 한글 주석 열 `effect_ko · p1_ko~p3_ko` = 밸런싱용 · 게임은 안 읽음)** · `skills.csv` = 이름·발동(next_action|instant|always)·쿨·설명 · `effect_key` = 효과 키 목록(p 칸 비움) · `heroes.csv gender`(m|f = 남성·여성 조건). 원본 = 기획 `tables/`(생성기 `_tables_t1.py` SKILL_EFFECTS · KEY_DOC 주석 사전 · 이름·설명 = `영웅_리스트_스킬.md` · 실행 뒤 `economy.csv`·`gacha.csv` 는 git 되돌림 = 생성기보다 손으로 늘린 판이 최신). 처리기 = `Runtime/Skills/`: `SkillKeys`(121종 · 레벨 계수 칸) · `SkillMods`(대상 칸 자신/allies/camp/radius/range/aura · 조건 칸 crit/vs_*/turn_le/armed · 공격·피해·치명타·반격·슬링 질의) · `SkillSystem`(쿨·예약 · 시한 효과 = 라운드(주인 진영 턴 시작마다 −1) · 피해 적용 `Hit(d, dmg, by, HitKind)` = 무적→무효화→호위→부활→흡혈·명중 상태 이상→처치 보상 · 재행동 `ReactCredits` · 즉시형 · `TurnBegin(side)` = 상대 턴 끝 회복·정화 + 중독·화상·회복·지대 · 행동 중 탭 `WaitTap`(조조·하후돈 · 테스트 `AutoTap`) · 지대 `SkillZone`) · `SkillStatus`(혼란·출혈·중독·화상 · 기절 = 기믹 `GimmickLayer.Stun` 재사용) · 특수 이동 = `ActionResolver`(관통·핀볼·복귀·교환·끌어오기·난동·반복) · 특수 사격 = `ActionResolver.Skills.cs`. 가상 실행 = `SkillSystem.TakeState/RestoreState`(Sim). 「1 m」 = 전장 1 칸 · 「행동 반경」 = 기본 슬링 거리(PD 확인 대상). 테스트 = `Tests/PlayMode/SkillTests`(효과 묶음 19). 캡처 = `-executeMethod TKS.EditorTools.DataImporter.CaptureSkills -skillDir <dir>`.
|
||||||
- **아웃게임 2차(2026-11-05~06 #1093 C)** — 표 10장 추가(`training` · `training_ranks` · `patrol` · `dev_tree` · `treasures` · `treasure_sets` · `chests`(+ iron/buy 열) · `tower` · `dungeons`(+ reward_amount) · `arena`(+ 상점 키)) + `economy.csv` C 키 24 → 임포트 자산 18(A 9 + C 9 · `OutGameTables.C.cs` 검증). 코드 = `OutGame/Domestic/`(`TrainingRules` 훈련 · `PatrolRules` 순찰 · `DevRules` 개발 효과 키 → 적용 지점 · `DomesticScreen`) · `OutGame/Treasure/`(`TreasureRules` 보유·세트·강화·조건 카운터·상자 · `TreasureScreen`) · `OutGame/Arena/`(`ArenaRules` 탑·던전·투기장·적장 · `ArenaScreen`). 스탯 = §3-2 × (1 + 훈련(병종·항목)) × (1 + 보물(항목)) = `HeroRules.Compute(…, save)` · 보물 crit/range/splash = `BattleManager.SideEffectsProvider` → `SkillMods.Get` 진영 효과(`side_crit_pct` · `range_mult` · `splash_radius_mult`) · 조건 효과 카운터 = `Runtime/BattleEvents.Raise`(전투 코드 1줄씩 · 아군만) · 연무장 전투 = `BattleManager.StartMode(BattleMode)`(적 편성 · 제한 턴 · 결과 탭 = `onExit`) → `OutGameSession.OnModeEnd`. 테스트 = `Tests/PlayMode/OutGameCTests`. 캡처 = `-executeMethod TKS.EditorTools.DataImporter.CaptureC -cDir <dir>`(사본 · 10장).
|
- **아웃게임 2차(2026-11-05~06 #1093 C)** — 표 10장 추가(`training` · `training_ranks` · `patrol` · `dev_tree` · `treasures` · `treasure_sets` · `chests`(+ iron/buy 열) · `tower` · `dungeons`(+ reward_amount) · `arena`(+ 상점 키)) + `economy.csv` C 키 24 → 임포트 자산 18(A 9 + C 9 · `OutGameTables.C.cs` 검증). 코드 = `OutGame/Domestic/`(`TrainingRules` 훈련 · `PatrolRules` 순찰 · `DevRules` 개발 효과 키 → 적용 지점 · `DomesticScreen`) · `OutGame/Treasure/`(`TreasureRules` 보유·세트·강화·조건 카운터·상자 · `TreasureScreen`) · `OutGame/Arena/`(`ArenaRules` 탑·던전·투기장·적장 · `ArenaScreen`). 스탯 = §3-2 × (1 + 훈련(병종·항목)) × (1 + 보물(항목)) = `HeroRules.Compute(…, save)` · 보물 crit/range/splash = `BattleManager.SideEffectsProvider` → `SkillMods.Get` 진영 효과(`side_crit_pct` · `range_mult` · `splash_radius_mult`) · 조건 효과 카운터 = `Runtime/BattleEvents.Raise`(전투 코드 1줄씩 · 아군만) · 연무장 전투 = `BattleManager.StartMode(BattleMode)`(적 편성 · 제한 턴 · 결과 탭 = `onExit`) → `OutGameSession.OnModeEnd`. 테스트 = `Tests/PlayMode/OutGameCTests`. 캡처 = `-executeMethod TKS.EditorTools.DataImporter.CaptureC -cDir <dir>`(사본 · 10장).
|
||||||
- **영웅 56명(PD #1096 · 2026-11-06)** — heroes 71행(영웅 56 + 병사 15) · skills 112 · hero_gear 1344 · 신규 effect_key 4(`team_dmg_pct_turn` · `cooldown_add` · `charm` · `heal_self_pct`) + 기존 키 조건 열(받는 피해 p2 = HP 임계/2 보스/3 근접 · p3 = 출전 인원 아군 전원 · 최대 HP·반격 아군 전원 · 피해 % 발동 턴) = `SkillMods`·`SkillSystem` · 대장군 `pushed_ally_attack_on_push` 는 표에서 미사용(처리기 유지).
|
- **영웅 72명(PD #1108 · 2026-11-10)** — heroes 87행(영웅 72 + 병사 15) · skills 144 · skill_effects 288 · 옛 키 체계(51+4종 · 견제·관통 배율 등)는 #1112 에서 폐기.
|
||||||
- **지형 기믹 레이어(PD #1097 · 2026-11-06 세션 D)** — SOT = `Assets/Data/Gimmicks/{gimmicks,stage_gimmicks,tutorial_tips}.csv`(기획 `tables/` 사본 · utf-8-sig) → 메뉴 `TKS/Import Gimmicks (기믹 임포트)`(`Editor/GimmickImporter` · 오류 시 자산 불변 · `ai_weights.csv` 도 함께) → `Resources/Gimmicks/GimmickDb`·`StageGimmickTable` · `Resources/AI/AiWeights`. 전투 시작 = `GimmickLayer.Build(stage)`(장-번호 배치 · 배경 위 · 유닛 아래 · 장애물 = 콜라이더 + `GimmickBody` · 지대 = 모양 스윕 · 강 = 늪처럼 통과하는 지대(강 안 ×p3 · 다리 구간 = 효과 없음 · 강가·다리에서 밀려 강물에 들면 빠짐 · PD 2026-11-06 개정) · 겹친 유닛은 빈 자리로). 전투 코드는 질의만: `Combat.FreeTravel`(장애물 = 벽 · 숨은 함정 정지) · `FirstUnitOnArrow`(pass/block/destroy) · `Combat.Damage`(군기 공 · 고지 근접 피격 · 안개 원거리 피격 ×p1 · 책사 광역이 그 반경 안개를 걷음) · `SkillMods.Range`(고지 사거리) · `BattleManager` 턴 시작 `TurnStart`(군기·중독·불·행동 불가). 충돌 반응·폭발·굴림·미끄러짐·지대 = `Gimmicks/ActionResolver.Gimmicks.cs`(partial). 배치 도구 = `TKS/Gimmick Painter (기믹 배치)`. 테스트 = `Tests/PlayMode/GimmickTests` · 캡처 = `-executeMethod TKS.EditorTools.GimmickImporter.Capture -gimmickDir <dir>`(사본).
|
- **지형 기믹 레이어(PD #1097 · 2026-11-06 세션 D)** — SOT = `Assets/Data/Gimmicks/{gimmicks,stage_gimmicks,tutorial_tips}.csv`(기획 `tables/` 사본 · utf-8-sig) → 메뉴 `TKS/Import Gimmicks (기믹 임포트)`(`Editor/GimmickImporter` · 오류 시 자산 불변 · `ai_weights.csv` 도 함께) → `Resources/Gimmicks/GimmickDb`·`StageGimmickTable` · `Resources/AI/AiWeights`. 전투 시작 = `GimmickLayer.Build(stage)`(장-번호 배치 · 배경 위 · 유닛 아래 · 장애물 = 콜라이더 + `GimmickBody` · 지대 = 모양 스윕 · 강 = 늪처럼 통과하는 지대(강 안 ×p3 · 다리 구간 = 효과 없음 · 강가·다리에서 밀려 강물에 들면 빠짐 · PD 2026-11-06 개정) · 겹친 유닛은 빈 자리로). 전투 코드는 질의만: `Combat.FreeTravel`(장애물 = 벽 · 숨은 함정 정지) · `FirstUnitOnArrow`(pass/block/destroy) · `Combat.Damage`(군기 공 · 고지 근접 피격 · 안개 원거리 피격 ×p1 · 책사 광역이 그 반경 안개를 걷음) · `SkillMods.Range`(고지 사거리) · `BattleManager` 턴 시작 `TurnStart`(군기·중독·불·행동 불가). 충돌 반응·폭발·굴림·미끄러짐·지대 = `Gimmicks/ActionResolver.Gimmicks.cs`(partial). 배치 도구 = `TKS/Gimmick Painter (기믹 배치)`. 테스트 = `Tests/PlayMode/GimmickTests` · 캡처 = `-executeMethod TKS.EditorTools.GimmickImporter.Capture -gimmickDir <dir>`(사본).
|
||||||
- **병종 규칙 개정 + 예측 AI(PD #1098 · 2026-11-06)** — 보병 = 적과 싸운 뒤 튕겨나는 길의 다른 적과 연쇄 교환 1회(`ActionOutcome.Chains`) · 창병 = 상대만 밀리고 자신은 × `spearRecoilRatio` 반대로(밀며 전진 폐기). 적 턴 = `Runtime/AI/TacticalAI`(후보 방위 × 파워 3 × 액티브 → `Sim` 가상 실행(연출 없음 · 분산 0 · 상태 복원) → `ai_weights.csv` 점수 · 서포터 → 돌격 → 사격 · 1기마다 재평가) · 가중치 자산이 없으면 `EnemyAI.Decide`(기존 조준) · 적 액티브 = 1부 보스만(`boss_active`). 가상 실행 중(`Sim.Active`)에는 `Fx`·`UnitView`·`BattleEvents`·HUD 갱신이 꺼진다 — 새 연출 코드도 `Sim.Active` 면 건너뛸 것. 조준 미리보기도 같은 가상 실행(`GimmickPreview` · 숨은 함정 모름). 테스트 = `Tests/PlayMode/TacticalAITests`.
|
- **병종 규칙 개정 + 예측 AI(PD #1098 · 2026-11-06)** — 보병 = 적과 싸운 뒤 튕겨나는 길의 다른 적과 연쇄 교환 1회(`ActionOutcome.Chains`) · 창병 = 상대만 밀리고 자신은 × `spearRecoilRatio` 반대로(밀며 전진 폐기). 적 턴 = `Runtime/AI/TacticalAI`(후보 방위 × 파워 3 × 액티브 → `Sim` 가상 실행(연출 없음 · 분산 0 · 상태 복원) → `ai_weights.csv` 점수 · 서포터 → 돌격 → 사격 · 1기마다 재평가) · 가중치 자산이 없으면 `EnemyAI.Decide`(기존 조준) · 적 액티브 = 1부 보스만(`boss_active`). 가상 실행 중(`Sim.Active`)에는 `Fx`·`UnitView`·`BattleEvents`·HUD 갱신이 꺼진다 — 새 연출 코드도 `Sim.Active` 면 건너뛸 것. 조준 미리보기도 같은 가상 실행(`GimmickPreview` · 숨은 함정 모름). 테스트 = `Tests/PlayMode/TacticalAITests`.
|
||||||
- 아웃게임 코드 = `Assets/Scripts/OutGame/**`(asmref → TKS.Runtime) — `OutGameSession`(표 + 세이브 · 바꾸는 동작은 즉시 저장) · 규칙 `HeroRules`·`EconomyRules`·`GachaRules` · 화면 `UI/`(로비·영웅·편성·거래·결과 패널 · `OutGameRoot` 가 전환). 세이브 = `persistentDataPath/save.json`(테스트·검증 캡처는 `SaveStore.UseMemory` / SessionState `TKS_SAVE_MEMORY` 로 메모리 전용). 출전 = `BattleManager.LineupProvider`(편성 프리셋 → `playerSpawnPoints` 자리 0~2 전열 · 3~4 후열). 화면 캡처 = `Unity.exe -projectPath <사본> -executeMethod TKS.EditorTools.DataImporter.CaptureOutGame -outgameDir <dir>`. **UI 수정 검증은 고친 화면만 찍는다**(PD 2026-11-09 「매번 모든 화면 체크 = 시간·토큰 낭비」): `Tools/mirror_verify.py` → `Unity.exe -projectPath E:/Nerdnavis/_tks_verify -force-d3d11 -executeMethod TKS.EditorTools.DataImporter.CaptureUI -uiDir <dir> -uiOnly hero_list,hero_detail*`(이름 = 정확히 또는 「접두*」 · 다 찍으면 바로 종료 · 1장 ≈ 15초 · 전체 21장 ≈ 3분 · 이름 목록 = `UICaptureDriver` 의 Shot 이름).
|
- 아웃게임 코드 = `Assets/Scripts/OutGame/**`(asmref → TKS.Runtime) — `OutGameSession`(표 + 세이브 · 바꾸는 동작은 즉시 저장) · 규칙 `HeroRules`·`EconomyRules`·`GachaRules` · 화면 `UI/`(로비·영웅·편성·거래·결과 패널 · `OutGameRoot` 가 전환). 세이브 = `persistentDataPath/save.json`(테스트·검증 캡처는 `SaveStore.UseMemory` / SessionState `TKS_SAVE_MEMORY` 로 메모리 전용). 출전 = `BattleManager.LineupProvider`(편성 프리셋 → `playerSpawnPoints` 자리 0~2 전열 · 3~4 후열). 화면 캡처 = `Unity.exe -projectPath <사본> -executeMethod TKS.EditorTools.DataImporter.CaptureOutGame -outgameDir <dir>`. **UI 수정 검증은 고친 화면만 찍는다**(PD 2026-11-09 「매번 모든 화면 체크 = 시간·토큰 낭비」): `Tools/mirror_verify.py` → `Unity.exe -projectPath E:/Nerdnavis/_tks_verify -force-d3d11 -executeMethod TKS.EditorTools.DataImporter.CaptureUI -uiDir <dir> -uiOnly hero_list,hero_detail*`(이름 = 정확히 또는 「접두*」 · 다 찍으면 바로 종료 · 1장 ≈ 15초 · 전체 21장 ≈ 3분 · 이름 목록 = `UICaptureDriver` 의 Shot 이름).
|
||||||
|
|
@ -84,7 +84,7 @@ UI 를 새로 만들거나 고칠 때 **반드시** 이 구조를 따른다(어
|
||||||
- **IB(쿠로네코) 이펙트 시험(현재 기본 공격 · PD 2026-11-10 「내가 원한 비주얼은 이런게 아니야(이노센트베인 영상) → 우선 IB 이펙트를 그대로 써서 확인 · 나중에 구조에 맞게 새로 그림(화풍 통일)」)**: IB 슬링샷 발사 `UnitVisualEffectControl.ShowBoosterFX()` 와 같은 구성 — `ef_booster`(앞 빛 물결 2장 + 가시 · 0.2초 반복 맥동 · 유닛 위 · 이동 끝에 바로 지움 = IB Breaking 때 Kill) · `ef_speed_tail`(꼬리 0.6초 + 화살표 입자 · 유닛 아래 · 끝나면 꼬리가 줄며 사라짐 — `SkillVfx.Stop` 이 트레일 길이만큼 기다림) · `ef_booster_dust`(출발 먼지 1번) · 타격 `ef_normal_hit`. 원본 = `Assets/VFX/IB/`(쿠로네코 `E:\NerdNavis\Kuroneko\KuronekoClient` 에서 `Tools/wl_vfx_import.py copy … --wl <쿠로네코> --dest Assets/VFX/IB --skip-scripts` · GUID 유지 · 무수정) · 사본 = `Resources/Fx/TKS_Fx_IB_*`(`Tools/make_ib_fx_copies.cs` — IB 전용 스크립트 부품 제거 · 정렬 레이어 Default · 방향 있는 것은 감싸는 루트 + 안쪽 Z −90°(IB 정면 +Y → 이 게임 +X) · 소리 이벤트 받는 곳 `IbFxAnimEvents`(아무것도 안 함 · 없으면 재생마다 콘솔 오류)). 셰이더 = `Fingar/Additive`(더하기 섞기 · 빌트인 그대로). 2D 돌진 3차 행은 표에 `#` 주석(되돌리기). **진영색(PD 「진영 색을 입히고」)** = `TKS_Fx_IB_Booster_<진영>`(스프라이트 색의 색상만 진영색 · 채도·밝기 유지) · `TKS_Fx_IB_SpeedTail_<진영>`(꼬리 그림 `Assets/VFX/IB/_TKS/Tail_Basic02_<진영>.png` = `Tools/make_ib_camp_textures.py` · 재질 같은 폴더 · 화살표 입자 색) — 둘 다 `make_ib_fx_copies.cs` 가 `Tools/dash2d_camps.csv` 로 만듦 · 원색 사본 `TKS_Fx_IB_Booster` · `SpeedTail` 은 비교용으로 남음(표에서 안 씀). **사라지는 시점(PD 「멈춘 이후 뒤늦게 이펙트가 사라짐 · 위치 안 맞음」)** = 속도 꼬리 `f_fade` 0.12 — 멈춘 자리에서 꼬리째 흐려짐(`SkillVfxFade` 가 트레일 색 그러데이션 불투명도도 낮춤 · 전에는 0.6초 꼬리 머리가 기병 돌격 끝자리에 남아 복귀 중에도 보였음). **타격** = 현재 없음(PD 2026-11-10 「적이 피격되었을 때 히트 이펙트는 제거 · 어떤 히트 이펙트를 쓸지 알려줄게」 · IB NormalHit · BounceHit 행은 표에 `#` 주석). **기준점 `e_anchor` = dash**(빛 물결 · 꼬리 · PD 「복귀할 때 이펙트가 캐릭터보다 위쪽에 생겨 안 보임 → 유닛 바닥에」) = 위·옆으로 갈 때 몸 가운데 · 아래로 갈수록 발 쪽(곧장 아래 = 발). **흙먼지** = WL 창 돌진 흙먼지 `TKS_Fx_SpearRushDust`(0.15초마다 발밑 · PD 「복귀할 때 흙먼지가 너무 세서 뭉개짐 → WL 흙먼지로」 · IB 출발 먼지 행은 `#` 주석).
|
- **IB(쿠로네코) 이펙트 시험(현재 기본 공격 · PD 2026-11-10 「내가 원한 비주얼은 이런게 아니야(이노센트베인 영상) → 우선 IB 이펙트를 그대로 써서 확인 · 나중에 구조에 맞게 새로 그림(화풍 통일)」)**: IB 슬링샷 발사 `UnitVisualEffectControl.ShowBoosterFX()` 와 같은 구성 — `ef_booster`(앞 빛 물결 2장 + 가시 · 0.2초 반복 맥동 · 유닛 위 · 이동 끝에 바로 지움 = IB Breaking 때 Kill) · `ef_speed_tail`(꼬리 0.6초 + 화살표 입자 · 유닛 아래 · 끝나면 꼬리가 줄며 사라짐 — `SkillVfx.Stop` 이 트레일 길이만큼 기다림) · `ef_booster_dust`(출발 먼지 1번) · 타격 `ef_normal_hit`. 원본 = `Assets/VFX/IB/`(쿠로네코 `E:\NerdNavis\Kuroneko\KuronekoClient` 에서 `Tools/wl_vfx_import.py copy … --wl <쿠로네코> --dest Assets/VFX/IB --skip-scripts` · GUID 유지 · 무수정) · 사본 = `Resources/Fx/TKS_Fx_IB_*`(`Tools/make_ib_fx_copies.cs` — IB 전용 스크립트 부품 제거 · 정렬 레이어 Default · 방향 있는 것은 감싸는 루트 + 안쪽 Z −90°(IB 정면 +Y → 이 게임 +X) · 소리 이벤트 받는 곳 `IbFxAnimEvents`(아무것도 안 함 · 없으면 재생마다 콘솔 오류)). 셰이더 = `Fingar/Additive`(더하기 섞기 · 빌트인 그대로). 2D 돌진 3차 행은 표에 `#` 주석(되돌리기). **진영색(PD 「진영 색을 입히고」)** = `TKS_Fx_IB_Booster_<진영>`(스프라이트 색의 색상만 진영색 · 채도·밝기 유지) · `TKS_Fx_IB_SpeedTail_<진영>`(꼬리 그림 `Assets/VFX/IB/_TKS/Tail_Basic02_<진영>.png` = `Tools/make_ib_camp_textures.py` · 재질 같은 폴더 · 화살표 입자 색) — 둘 다 `make_ib_fx_copies.cs` 가 `Tools/dash2d_camps.csv` 로 만듦 · 원색 사본 `TKS_Fx_IB_Booster` · `SpeedTail` 은 비교용으로 남음(표에서 안 씀). **사라지는 시점(PD 「멈춘 이후 뒤늦게 이펙트가 사라짐 · 위치 안 맞음」)** = 속도 꼬리 `f_fade` 0.12 — 멈춘 자리에서 꼬리째 흐려짐(`SkillVfxFade` 가 트레일 색 그러데이션 불투명도도 낮춤 · 전에는 0.6초 꼬리 머리가 기병 돌격 끝자리에 남아 복귀 중에도 보였음). **타격** = 현재 없음(PD 2026-11-10 「적이 피격되었을 때 히트 이펙트는 제거 · 어떤 히트 이펙트를 쓸지 알려줄게」 · IB NormalHit · BounceHit 행은 표에 `#` 주석). **기준점 `e_anchor` = dash**(빛 물결 · 꼬리 · PD 「복귀할 때 이펙트가 캐릭터보다 위쪽에 생겨 안 보임 → 유닛 바닥에」) = 위·옆으로 갈 때 몸 가운데 · 아래로 갈수록 발 쪽(곧장 아래 = 발). **흙먼지** = WL 창 돌진 흙먼지 `TKS_Fx_SpearRushDust`(0.15초마다 발밑 · PD 「복귀할 때 흙먼지가 너무 세서 뭉개짐 → WL 흙먼지로」 · IB 출발 먼지 행은 `#` 주석).
|
||||||
- **방향 결정 UI = IB 이식(PD 2026-11-10 「IB 와 같이 슬링샷 조작 시 방향 결정 UI 등을 그대로 이식」)**: `TKS.IbAimArrow`(IB `GameTouchArrow` 동작 · 프리팹 `Resources/Fx/TKS_Fx_IB_GameArrow` = 원본 `Assets/VFX/IB/AssetResources/Game/GameArrow` 사본) — 누르면 유닛 둘레 고리 + 퍼지는 고리 3개(3초 · 1초 간격) · 당기면 하늘색 화살표가 쏠 방향으로 돌고 당긴 세기만큼 길어짐(0.5~1.5 · 최소 당김 → 슬링샷 최대 거리) · 화살 끝이 전장 밖이면 안으로 · 놓으면 숨김. 전에 쓰던 조준선 · 당김선은 끔(`SlingshotInput.ShowPreview` · IbAimArrow 가 없을 때만 예전 선). 목표 고리 · 강조 · 궁병 화살 경로선 · 기믹 예상 궤적은 그대로. 화살표 색 = IB 공격 종류 3(하늘색) 하나.
|
- **방향 결정 UI = IB 이식(PD 2026-11-10 「IB 와 같이 슬링샷 조작 시 방향 결정 UI 등을 그대로 이식」)**: `TKS.IbAimArrow`(IB `GameTouchArrow` 동작 · 프리팹 `Resources/Fx/TKS_Fx_IB_GameArrow` = 원본 `Assets/VFX/IB/AssetResources/Game/GameArrow` 사본) — 누르면 유닛 둘레 고리 + 퍼지는 고리 3개(3초 · 1초 간격) · 당기면 하늘색 화살표가 쏠 방향으로 돌고 당긴 세기만큼 길어짐(0.5~1.5 · 최소 당김 → 슬링샷 최대 거리) · 화살 끝이 전장 밖이면 안으로 · 놓으면 숨김. 전에 쓰던 조준선 · 당김선은 끔(`SlingshotInput.ShowPreview` · IbAimArrow 가 없을 때만 예전 선). 목표 고리 · 강조 · 궁병 화살 경로선 · 기믹 예상 궤적은 그대로. 화살표 색 = IB 공격 종류 3(하늘색) 하나.
|
||||||
- **여포 크기(PD 2026-11-10 「왜 여포는 다른 기병보다 작지?」)**: 말 탄 유닛은 머리 끝 상한(BalanceConfig.mountedArtMaxTop 1.3 × 표준 키)에 맞춰 줄어드는데, `dz_boss_lvbu` 의 몸 높이(`meta.json` bodyHeightPx · `art_size.csv` body_px)가 머리 깃털 끝까지 819 px 라 몸 전체가 더 줄었음 → 왕관 끝 ~ 발 718 px(front_idle 실측 · 깃털 제외 · 다른 기병의 투구 끝 기준과 같은 규칙)로 고침 → 배율 0.2381 → 0.2716(+14 %). 원본 백업 = 세션 scratchpad. **다른 PC 에서 그림을 다시 export 하면 819 로 되돌아갈 수 있음**(export 규칙 = 불투명 폭 30 % 행 — 깃털이 넓어서 안 걸러짐).
|
- **여포 크기(PD 2026-11-10 「왜 여포는 다른 기병보다 작지?」)**: 말 탄 유닛은 머리 끝 상한(BalanceConfig.mountedArtMaxTop 1.3 × 표준 키)에 맞춰 줄어드는데, `dz_boss_lvbu` 의 몸 높이(`meta.json` bodyHeightPx · `art_size.csv` body_px)가 머리 깃털 끝까지 819 px 라 몸 전체가 더 줄었음 → 왕관 끝 ~ 발 718 px(front_idle 실측 · 깃털 제외 · 다른 기병의 투구 끝 기준과 같은 규칙)로 고침 → 배율 0.2381 → 0.2716(+14 %). 원본 백업 = 세션 scratchpad. **다른 PC 에서 그림을 다시 export 하면 819 로 되돌아갈 수 있음**(export 규칙 = 불투명 폭 30 % 행 — 깃털이 넓어서 안 걸러짐).
|
||||||
- **근접 3병종 특징 = 한 줄 정의(PD 2026-11-10 직접 지시)**: **보병** = 적을 밀치고 튕겨나며 튕기면서 다른 적을 추가로 공격(알까기 + 연쇄 · #1098 그대로 · 목표점 명중 추가 이동 1회는 유지 = PD 확인 대상) · **창병** = 적을 크게 밀치지만 자신은 적게 밀림(`spearPushRatio` 1.5 > 보병 `collisionPushRatio` 1.0 · 반작용 남은 거리 × 0.3 < 보병 자기 튕김 0.5) · **창병 대기(견제) 제거**(「유저가 인지하기 어려움」 · 목표점 명중해도 `SetOverwatch` 안 함 · HUD 「견제 반경·대기 중」 문구 삭제) · **기병** = 적을 관통해 이동 · **목표점 명중 시 제자리 복귀 제거**(「유저가 인지하기 어려움」 · `PreviewCavalryReturn` · 조준 때 복귀 영역 `ShowReturnZones` 삭제 · AI 가중치 `cav_return`·`cav_stranded` 삭제). 견제·복귀 스킬(청룡언월 · 후계 · 구벌중원 · 기린아 · 교변 · 삼대 노신 · 유수의 복수 · 장사 · 백마의 창 · 하북 명장 · 남만의 여왕 · 장사 태수 · 단기구주 · 도주 본능 · 소패왕) = 발동 안 함 · **재설계 PD 결정 대기** → 그때까지 `BattleManager.ApplyOverwatch` · `Unit.Overwatch` · `SkillMods.Overwatch*` 는 남겨 둠(대기 유닛이 없어 실행 안 됨) · `SkillTests.Spear_OverwatchSkills_*` = [Ignore].
|
- **근접 3병종 특징 = 한 줄 정의(PD 2026-11-10 직접 지시)**: **보병** = 적을 밀치고 튕겨나며 튕기면서 다른 적을 추가로 공격(알까기 + 연쇄 · #1098 그대로 · 목표점 명중 추가 이동 1회는 유지 = PD 확인 대상) · **창병** = 적을 크게 밀치지만 자신은 적게 밀림(`spearPushRatio` 1.5 > 보병 `collisionPushRatio` 1.0 · 반작용 남은 거리 × 0.3 < 보병 자기 튕김 0.5) · **창병 대기(견제) 제거**(「유저가 인지하기 어려움」 · 목표점 명중해도 `SetOverwatch` 안 함 · HUD 「견제 반경·대기 중」 문구 삭제) · **기병** = 적을 관통해 이동 · **목표점 명중 시 제자리 복귀 제거**(「유저가 인지하기 어려움」 · `PreviewCavalryReturn` · 조준 때 복귀 영역 `ShowReturnZones` 삭제 · AI 가중치 `cav_return`·`cav_stranded` 삭제). 견제·복귀 스킬 = #1112 PD 새 기획으로 전부 재설계(견제 없음 · 복귀는 스킬 효과 `return_on_hit`·`return_always`·`return_aim` 으로만) · `SkillMods.Overwatch*` 삭제 · `BattleManager.ApplyOverwatch` · `Unit.Overwatch` 는 스킬 확장 없이 남음(대기 유닛이 없어 실행 안 됨).
|
||||||
- **IB 크기 · 캐릭터 연출(PD 2026-11-10 「IB 와 화살표 크기도 다르고 선택된 캐릭터 주위 연출도 누락」)**: 값 표 `Resources/Skills/ib_ui.csv`(`TKS.IbUi`) — IB 유닛 반지름 0.496(ExportCharacter size 8 × 0.062) 기준 배율 = 이 게임 유닛 반지름 / 0.496 → 고리 지름 = 반지름 × 5.4 · 화살표 세기 s = IB 월드 당김 × 0.6(0.5~1.5) · IB 취소 거리 0.5. **`TKS.IbUnitMarks`** = 선택한 행동 가능 아군 발밑 도는 원(`TKS_Fx_IB_TargetCircle` · IB ShowUserTurnMark) + 몸 둘레 빛(`TKS_Fx_IB_AttackReady`) · 조준 중(누름 ~ 놓음) 모든 유닛 속성 방울(`TKS_Fx_IB_Attribute_<속성>` · IB SetActiveBarrierVisualEffect · 진영 → 속성 = 표 element_*: 위 물 · 촉 나무 · 오 불 · 군·진영 없음 어둠).
|
- **IB 크기 · 캐릭터 연출(PD 2026-11-10 「IB 와 화살표 크기도 다르고 선택된 캐릭터 주위 연출도 누락」)**: 값 표 `Resources/Skills/ib_ui.csv`(`TKS.IbUi`) — IB 유닛 반지름 0.496(ExportCharacter size 8 × 0.062) 기준 배율 = 이 게임 유닛 반지름 / 0.496 → 고리 지름 = 반지름 × 5.4 · 화살표 세기 s = IB 월드 당김 × 0.6(0.5~1.5) · IB 취소 거리 0.5. **`TKS.IbUnitMarks`** = 선택한 행동 가능 아군 발밑 도는 원(`TKS_Fx_IB_TargetCircle` · IB ShowUserTurnMark) + 몸 둘레 빛(`TKS_Fx_IB_AttackReady`) · 조준 중(누름 ~ 놓음) 모든 유닛 속성 방울(`TKS_Fx_IB_Attribute_<속성>` · IB SetActiveBarrierVisualEffect · 진영 → 속성 = 표 element_*: 위 물 · 촉 나무 · 오 불 · 군·진영 없음 어둠).
|
||||||
- **타격감(PD 2026-11-10 「화면 흔들림 등도 IB 를 참고해서」)**: `TKS.HitFeel` + 표 `Resources/Skills/hit_feel.csv`(행동 중 유닛이 상대에게 처음 피해를 준 순간 · `SkillSystem.Hit` · 흔들림 = `CameraController.Shake`(추적 위치는 그대로 두고 흔들림만 더함) · 히트 스톱 칸 있음(현재 0)). **IB 실측**: 흔들림 함수 6종은 스킬·궁극기·기믹 이펙트 애니메이션 이벤트로만 불리고 본문이 디컴파일에서 비어 있어 값 없음 · 일반 타격·벽 튕김엔 흔들림 없음 · 히트 스톱 없음 → 표 값(hit 0.15초 · 키 10 % / 기병 0.18초 · 13 %)은 추정.
|
- **타격감(PD 2026-11-10 「화면 흔들림 등도 IB 를 참고해서」)**: `TKS.HitFeel` + 표 `Resources/Skills/hit_feel.csv`(행동 중 유닛이 상대에게 처음 피해를 준 순간 · `SkillSystem.Hit` · 흔들림 = `CameraController.Shake`(추적 위치는 그대로 두고 흔들림만 더함) · 히트 스톱 칸 있음(현재 0)). **IB 실측**: 흔들림 함수 6종은 스킬·궁극기·기믹 이펙트 애니메이션 이벤트로만 불리고 본문이 디컴파일에서 비어 있어 값 없음 · 일반 타격·벽 튕김엔 흔들림 없음 · 히트 스톱 없음 → 표 값(hit 0.15초 · 키 10 % / 기병 0.18초 · 13 %)은 추정.
|
||||||
- **2D 돌진(PD 2026-11-10 「기본 공격(돌진) 이펙트가 너무 3D 느낌 → 2D 처럼」 · MSFX 혜성 행은 표에 `#` 주석으로 남김 = 되돌리기)**: **3차(현재 · PD 2026-11-10 「검기는 이전 형태가 더 나음 · 대쉬 이펙트는 캐릭터 몸을 벗어나지 않도록 · 첨부 같은 느낌으로 몸을 뒤덮는 연출을 스킬용 시험」)** = 초승달 `Head` = 1차 그림 `dash2d_crescent`(검정 외곽선 · 진영색 곱) · 대쉬 층(꼬리 0.04초 · 바람 줄기 · 알갱이 · 흙먼지) = 유닛을 따라 움직이는 Local 공간 · 몸 기준점 반경 0.45 키 안(실측 그림 경계 좌우 ±0.56 · 아래 −0.60 ~ 위 +0.59 키) · 바람 줄기 `Lines` · 알갱이 `Dots` = 유닛 위 반투명(표 `s_over` = `Head;Lines;Dots` · 유닛 아래면 몸에 다 가려 안 보임 · PD 「몸의 대쉬 효과가 왜 안 보이나」) · 꼬리 · 흙먼지 = 유닛 아래 · **기병 복귀 = 돌아서서 감**(`UnitView.TurnAround` · 복귀 방향으로 보기 + 곧장 위·아래면 좌우 반전 · 도착 뒤에도 마지막 이동 방향 PD #1082) · **몸 뒤덮는 기운(시험)** = `TKS_Fx_Aura2D_<진영>`(혜성 기운 `aura_comet_<진영>` 겹쳐 깜빡임 · 불꽃 혀 `aura_wisp` · 반짝임 = 유닛 아래 · 옅은 막 `Veil` · 앞 테 `Rim`(2차 검기 그림) = 유닛 위) · 표 키 `test_aura` · 테스트 씬 「기운 시험」 스위치(`SkillVfx.TestTravelKey` · 켜면 슬링샷 공격 돌진에 함께 · 실제 전투 = 늘 빈칸). 2차 = 외곽선 없음 · 그림 `Assets/VFX/TKS2D/`(만들기 = `python -I -X utf8 Tools/make_dash2d_textures.py` · 진영색 원본 = `Tools/dash2d_camps.csv`) → 프리팹 `Resources/Fx/TKS_Fx_Dash2D_<wei|shu|wu|other>`(검기 초승달 `Head` = 진영별 그림 `dash2d_slash_<진영>` · 유닛 위 · 반투명 꼬리 `Trail`(앞 0.7 → 0) · 흰 바람 줄기 `Lines` · 알갱이 `Dots` = 유닛 아래 · 수명 동안 투명해짐) + `TKS_Fx_Dust2D`(부드러운 반투명 흙먼지 · 모든 진영) · 다시 만들기 = `unity command … script eval --file Tools/build_dash2d.cs`(값을 바꾸면 이 파일과 프리팹을 함께). 1차(검정 외곽선 셀 그림)의 `dash2d_crescent.png` · `dash2d_highlight.png` 는 안 씀. 표 `b_flat` = 1 이면 화면 안에서 진행 방향으로만 돌림(기울기·팩 루트 회전 무시 · `SkillVfx.FlatRotation`) · 크기 단위 = 유닛 키 1(`f_scale` 1) · 끝 소멸 = `f_fade` 0.12(`SkillVfxFade` 가 스프라이트 불투명도도 낮춤).
|
- **2D 돌진(PD 2026-11-10 「기본 공격(돌진) 이펙트가 너무 3D 느낌 → 2D 처럼」 · MSFX 혜성 행은 표에 `#` 주석으로 남김 = 되돌리기)**: **3차(현재 · PD 2026-11-10 「검기는 이전 형태가 더 나음 · 대쉬 이펙트는 캐릭터 몸을 벗어나지 않도록 · 첨부 같은 느낌으로 몸을 뒤덮는 연출을 스킬용 시험」)** = 초승달 `Head` = 1차 그림 `dash2d_crescent`(검정 외곽선 · 진영색 곱) · 대쉬 층(꼬리 0.04초 · 바람 줄기 · 알갱이 · 흙먼지) = 유닛을 따라 움직이는 Local 공간 · 몸 기준점 반경 0.45 키 안(실측 그림 경계 좌우 ±0.56 · 아래 −0.60 ~ 위 +0.59 키) · 바람 줄기 `Lines` · 알갱이 `Dots` = 유닛 위 반투명(표 `s_over` = `Head;Lines;Dots` · 유닛 아래면 몸에 다 가려 안 보임 · PD 「몸의 대쉬 효과가 왜 안 보이나」) · 꼬리 · 흙먼지 = 유닛 아래 · **기병 복귀 = 돌아서서 감**(`UnitView.TurnAround` · 복귀 방향으로 보기 + 곧장 위·아래면 좌우 반전 · 도착 뒤에도 마지막 이동 방향 PD #1082) · **몸 뒤덮는 기운(시험)** = `TKS_Fx_Aura2D_<진영>`(혜성 기운 `aura_comet_<진영>` 겹쳐 깜빡임 · 불꽃 혀 `aura_wisp` · 반짝임 = 유닛 아래 · 옅은 막 `Veil` · 앞 테 `Rim`(2차 검기 그림) = 유닛 위) · 표 키 `test_aura` · 테스트 씬 「기운 시험」 스위치(`SkillVfx.TestTravelKey` · 켜면 슬링샷 공격 돌진에 함께 · 실제 전투 = 늘 빈칸). 2차 = 외곽선 없음 · 그림 `Assets/VFX/TKS2D/`(만들기 = `python -I -X utf8 Tools/make_dash2d_textures.py` · 진영색 원본 = `Tools/dash2d_camps.csv`) → 프리팹 `Resources/Fx/TKS_Fx_Dash2D_<wei|shu|wu|other>`(검기 초승달 `Head` = 진영별 그림 `dash2d_slash_<진영>` · 유닛 위 · 반투명 꼬리 `Trail`(앞 0.7 → 0) · 흰 바람 줄기 `Lines` · 알갱이 `Dots` = 유닛 아래 · 수명 동안 투명해짐) + `TKS_Fx_Dust2D`(부드러운 반투명 흙먼지 · 모든 진영) · 다시 만들기 = `unity command … script eval --file Tools/build_dash2d.cs`(값을 바꾸면 이 파일과 프리팹을 함께). 1차(검정 외곽선 셀 그림)의 `dash2d_crescent.png` · `dash2d_highlight.png` 는 안 씀. 표 `b_flat` = 1 이면 화면 안에서 진행 방향으로만 돌림(기울기·팩 루트 회전 무시 · `SkillVfx.FlatRotation`) · 크기 단위 = 유닛 키 1(`f_scale` 1) · 끝 소멸 = `f_fade` 0.12(`SkillVfxFade` 가 스프라이트 불투명도도 낮춤).
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue