| name | dota2-ability |
| description | Dota 2 自定义技能编写指南。触发词:技能、ability、spell、被动、主动。 |
为 DOTA2 自定义游戏项目编写 Dota 2 自定义技能。
核心原则
- 配置与代码分离:所有可能与前端共享或需要集中管理的配置、数值、常量(包括但不限于技能属性、施法参数、伤害值、持续时间等),都必须放在
game/scripts/npc/ 下的 KV 文件的 AbilityValues 中。通过 this.GetSpecialValueFor('键名') 在 TSTL 中读取,禁止在代码中硬编码任何可能变化的配置值。修改配置后需运行 npx gulp kv_2_js 刷新 JSON,保证前后端数据一致
文件结构
| 文件 | 路径 | 用途 |
|---|
| 技能源码 | game/scripts/src/abilities/<模块名>/<技能名>.ts | TSTL 技能逻辑 |
| 修饰器源码 | game/scripts/src/modifiers/<模块名>/<modifier名>.ts | TSTL modifier 逻辑(仅用于独立的机制性 modifier) |
| KV 配置 | game/scripts/npc/abilities/<技能名>.txt | 技能 KV 键值对(AI创建的自定义技能) |
| KV 入口 | game/scripts/npc/npc_abilities_custom.txt | 引用 #base "abilities.txt",自动生成部分由 Excel 管理 |
| Excel 数据 | excels/技能表.xlsx | 自动生成 abilities.txt(请勿手动修改) |
| 本地化 | game/resource/addon.csv | 技能名称、描述、数值标签 |
工作流
步骤 1:确认技能需求
- 明确技能名称、行为类型(被动/主动/切换/持续施法等)、施法方式、伤害类型
- 确认技能是否需要自定义 modifier
步骤 2:编写 KV 配置
在 game/scripts/npc/abilities/<技能名>.txt 中创建 KV 文件
如果这是一个写在 npc/abilities.txt 的文件,不要手动修改,告知用户需要到 excels/技能表.xlsx 中修改内容。
KV 文件必须在 npc_abilities_custom.txt 中通过 #base 引用后生效。
KV 结构规则:技能名称必须在第一级,所有属性写在第二级的 {} 内:
"dbg_card_heal"
{
"BaseClass" "ability_lua"
"ScriptFile" "abilities/dbg/card_heal"
"AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_NO_TARGET | DOTA_ABILITY_BEHAVIOR_IMMEDIATE"
"AbilityCastPoint" "0.2"
"AbilityCooldown" "0.0"
"AbilityManaCost" "0"
"MaxLevel" "1"
}
步骤 2b:编译 KV 到 JSON(必须执行)
修改 KV 文件后,必须运行以下命令将 KV 编译为 JSON,供前后端统一引用:
npx gulp kv_2_js
npx gulp jssync
编译后输出到:
content/panorama/src/json/ — 前端 Panorama 使用
game/scripts/src/json/ — 后端 TSTL 使用
前后端数据分离原则:
- 所有游戏数值配置(伤害、CD、范围、速度等)写在 KV 文件中
- 前后端都从编译后的 JSON 文件中读取同一份数据
- 禁止在前端或后端代码中硬编码数值常量,避免数据不一致
步骤 3:编写 TSTL 源码
在 game/scripts/src/abilities/<模块名>/<技能名>.ts 中:
- 从
dota_ts_adapter 导入 BaseAbility、BaseModifier、registerAbility、registerModifier
- 用
@registerAbility() 装饰器注册技能类,继承 BaseAbility
- 如需 modifier,用
@registerModifier() 装饰器注册,继承 BaseModifier 或其运动变体
- 重要:如果 modifier 是技能自己的(被该技能引用),直接放在技能同一个
.ts 文件中,不需要单独建文件
- 只有独立的、机制性的 modifier(不被任何技能引用,如通用抛物线运动、通用buff等)才需要单独放在
src/modifiers/ 目录下
- 重写必要的生命周期方法(
OnSpellStart、OnCreated、DeclareFunctions 等)
- 通过
this.GetSpecialValueFor('键名') 读取 KV 中的 AbilityValues
- 如有粒子特效/音效,重写
Precache(context) 方法
- 如果是制作被动技能或者被动物品,使用 GetIntrinsicModifierName 方法获取 modifier 名称。
步骤 4:添加本地化
在 game/resource/addon.csv 中添加:
DOTA_Tooltip_ability_<技能名> → 技能显示名
DOTA_Tooltip_ability_<技能名>_Description → 技能描述
DOTA_Tooltip_ability_<技能名>_<AbilityValues键名> → 数值标签
步骤 5:验证
- 编译 TSTL,确认无类型错误
- 告知用户进入游戏测试技能行为
KV 键值参考
通用键
| 键名 | 说明 | 示例值 |
|---|
BaseClass | 基类 | ability_lua / ability_datadriven / 原版技能名 |
AbilityBehavior | 技能行为(可用 | 组合) | DOTA_ABILITY_BEHAVIOR_POINT | DOTA_ABILITY_BEHAVIOR_AOE |
AbilityType | 技能类型 | DOTA_ABILITY_TYPE_BASIC / DOTA_ABILITY_TYPE_ULTIMATE |
AbilityTextureName | 技能图标 | crystal_maiden_crystal_nova |
MaxLevel | 最大等级 | 4 |
AbilityCastAnimation | 施法动画 | ACT_DOTA_CAST_ABILITY_1 |
IsBreakable | 可被破被动 | 1 |
ScriptFile | 脚本文件路径(TSTL 编译后的 lua) | abilities/<模块名>/crystal_nova_x.lua |
施法键
| 键名 | 说明 | 示例值 |
|---|
AbilityCastRange | 施法距离(支持多级,空格分隔) | 700 / 600 700 800 900 |
AbilityCastPoint | 施法前摇 | 0.3 |
AbilityCooldown | 冷却(支持多级) | 12 12 12 12 |
AbilityManaCost | 魔法消耗(支持多级) | 100 120 140 160 |
AbilityChannelTime | 持续施法时间 | 3 |
AbilityDuration | 持续时间(支持多级) | 5 6 7 8 |
目标选择键
| 键名 | 说明 | 可选值 |
|---|
AbilityUnitTargetTeam | 目标队伍 | DOTA_UNIT_TARGET_TEAM_ENEMY / DOTA_UNIT_TARGET_TEAM_FRIENDLY / DOTA_UNIT_TARGET_TEAM_BOTH |
AbilityUnitTargetType | 目标类型 | DOTA_UNIT_TARGET_HERO / DOTA_UNIT_TARGET_BASIC / DOTA_UNIT_TARGET_BUILDING / DOTA_UNIT_TARGET_TREE(可 | 组合) |
AbilityUnitTargetFlags | 目标标志 | DOTA_UNIT_TARGET_FLAG_MAGIC_IMMUNE_ENEMIES / DOTA_UNIT_TARGET_FLAG_NOT_MAGIC_IMMUNE_ALLIES 等 |
AbilityUnitDamageType | 伤害类型 | DAMAGE_TYPE_MAGICAL / DAMAGE_TYPE_PHYSICAL / DAMAGE_TYPE_PURE |
AbilityUnitMotionControllerPriority | 运动控制器优先级 | LOWEST / LOW / MEDIUM / HIGH / HIGHEST |
其他键
| 键名 | 说明 | 示例值 |
|---|
AbilityModifierSupportBonus | 修饰器支持加成 | 100 |
AbilityProcsMagicStick | 充能魔棒 | 1 |
AbilityCharges | 充能次数 | 3 |
AbilityChargeRestoreTime | 充能恢复时间 | 30 |
AbilityCastRangeIsBuffered | 施法距离缓冲 | 1 |
IsHiddenOnHero | 英雄上隐藏 | 1 |
IsGrantedByScepter | A杖升级 | 1 |
IsGrantedByShard | 魔晶升级 | 1 |
ScepterUpgradeRule | A杖升级规则 | SCEPTER_UPGRADE_RULE_BASE |
SpellDispellableType | 驱散类型 | SPELL_DISPELLABLE_YES / SPELL_DISPELLABLE_NO / SPELL_DISPELLABLE_YES_STRONG |
SpellImmunityType | 免疫类型 | SPELL_IMMUNITY_ENEMIES_YES / SPELL_IMMUNITY_ENEMIES_NO / SPELL_IMMUNITY_ALLIES_YES / SPELL_IMMUNITY_ALLIES_NO |
FightRecapLevel | 战斗回放等级 | 1 / 2 |
HasSharedCooldown | 共享冷却 | 1 |
SharedCooldownName | 共享冷却名 | arcane_boots |
ConjureEffectName | 召唤特效 | particles/... |
AbilityValues
自定义数值变量,在 TSTL 中通过 this.GetSpecialValueFor('键名') 读取:
"AbilityValues"
{
"duration" "4.5"
"aoe_radius" "400"
"movespeed_slow" "-20 -30 -40 -50"
}
新版也支持对象格式:
"AbilityValues"
{
"radius"
{
"value" "400 500 600 700"
}
}
Precache
预缓存资源:
"Precache"
{
"particle" "particles/units/heroes/hero_axe/axe_counterhelix_ad.vpcf"
"soundfile" "soundevents/game_sounds_heroes/game_sounds_axe.vsndevts"
"model" "models/heroes/axe/axe.vmdl"
}
AbilityBehavior 常量
| 常量 | 说明 |
|---|
DOTA_ABILITY_BEHAVIOR_HIDDEN | 隐藏技能 |
DOTA_ABILITY_BEHAVIOR_PASSIVE | 被动技能 |
DOTA_ABILITY_BEHAVIOR_NO_TARGET | 无目标技能 |
DOTA_ABILITY_BEHAVIOR_UNIT_TARGET | 单位目标技能 |
DOTA_ABILITY_BEHAVIOR_POINT | 点目标技能 |
DOTA_ABILITY_BEHAVIOR_AOE | 范围技能 |
DOTA_ABILITY_BEHAVIOR_NOT_LEARNABLE | 不可学习 |
DOTA_ABILITY_BEHAVIOR_CHANNELLED | 持续施法 |
DOTA_ABILITY_BEHAVIOR_ITEM | 物品技能 |
DOTA_ABILITY_BEHAVIOR_TOGGLE | 切换技能 |
DOTA_ABILITY_BEHAVIOR_DIRECTIONAL | 方向性技能 |
DOTA_ABILITY_BEHAVIOR_IMMEDIATE | 立即释放 |
DOTA_ABILITY_BEHAVIOR_AUTOCAST | 自动施法 |
DOTA_ABILITY_BEHAVIOR_NOASSIST | 无辅助网格 |
DOTA_ABILITY_BEHAVIOR_AURA | 光环 |
DOTA_ABILITY_BEHAVIOR_ATTACK | 法球/攻击 modifier |
DOTA_ABILITY_BEHAVIOR_DONT_RESUME_MOVEMENT | 施法后不恢复移动 |
DOTA_ABILITY_BEHAVIOR_ROOT_DISABLES | 定身时禁用 |
DOTA_ABILITY_BEHAVIOR_UNRESTRICTED | 不受限制 |
DOTA_ABILITY_BEHAVIOR_IGNORE_PSEUDO_QUEUE | 忽略伪队列 |
DOTA_ABILITY_BEHAVIOR_IGNORE_CHANNEL | 忽略持续施法 |
DOTA_ABILITY_BEHAVIOR_DONT_CANCEL_MOVEMENT | 不取消移动 |
DOTA_ABILITY_BEHAVIOR_DONT_ALERT_TARGET | 不惊醒目标 |
DOTA_ABILITY_BEHAVIOR_DONT_RESUME_ATTACK | 施法后不恢复攻击 |
DOTA_ABILITY_BEHAVIOR_NORMAL_WHEN_STOLEN | 被偷取时变为基础技能 |
DOTA_ABILITY_BEHAVIOR_IGNORE_BACKSWING | 忽略后摇 |
DOTA_ABILITY_BEHAVIOR_RUNE_TARGET | 符文目标 |
TSTL 编程参考
装饰器与基类
| 装饰器/基类 | 用途 |
|---|
@registerAbility() | 注册技能类 |
BaseAbility | 技能基类,对应 CDOTA_Ability_Lua |
@registerModifier() | 注册修饰器类 |
BaseModifier | 修饰器基类,对应 CDOTA_Modifier_Lua |
BaseModifierMotionHorizontal | 水平运动修饰器 |
BaseModifierMotionVertical | 垂直运动修饰器 |
BaseModifierMotionBoth | 双向运动修饰器 |
技能常用方法
| 方法 | 用途 |
|---|
OnSpellStart() | 施法开始 |
OnAbilityPhaseStart() | 施法前摇阶段(返回 bool) |
OnAbilityPhaseEnd() | 施法前摇结束 |
OnChannelFinish(bInterrupted) | 持续施法结束 |
GetBehavior() | 获取行为类型 |
GetAOERadius() | 获取 AOE 半径 |
GetIntrinsicModifierName() | 获取内置 modifier 名(被动技能用) |
GetSpecialValueFor(key) | 读取 AbilityValues 中的值 |
Precache(context) | 预缓存资源 |
GetCastRange() | 获取施法范围 |
GetCooldown(level) | 获取冷却 |
GetManaCost(level) | 获取魔法消耗 |
Modifier 常用方法
| 方法 | 用途 |
|---|
OnCreated(kv) | modifier 创建时 |
OnRefresh(kv) | modifier 刷新时 |
OnDestroy() | modifier 销毁时 |
DeclareFunctions() | 声明修饰器函数 |
IsHidden() | 是否隐藏 |
IsDebuff() | 是否减益 |
IsPurgable() | 是否可驱散 |
IsStunDebuff() | 是否眩晕减益 |
GetOverrideAnimation() | 覆盖动画 |
CheckState() | 返回状态表 |
Modifier 静态方法
| 方法 | 用途 |
|---|
ModifierClass.apply(target, caster, ability, kv) | 施加 modifier |
ModifierClass.find_on(target) | 查找 modifier |
ModifierClass.remove(target) | 移除 modifier |
Modifier 事件(DeclareFunctions 返回值)
| 事件常量 | 说明 |
|---|
ModifierFunction.ON_ATTACK_LANDED | 攻击命中时 |
ModifierFunction.ON_ATTACK_START | 攻击开始时 |
ModifierFunction.ON_ATTACKED | 被攻击时 |
ModifierFunction.ON_TAKEDAMAGE | 受到伤害时 |
ModifierFunction.ON_DEATH | 死亡时 |
ModifierFunction.ON_ABILITY_EXECUTED | 施法完成时 |
ModifierFunction.ON_ABILITY_START | 施法开始时 |
ModifierFunction.ON_BREAK_INVISIBILITY | 打破隐身时 |
ModifierFunction.ON_RESPAWN | 复活时 |
ModifierFunction.ON_UNIT_MOVED | 单位移动时 |
ModifierFunction.ON_HEALTH_GAINED | 获得生命时 |
ModifierFunction.ON_MANA_GAINED | 获得魔法时 |
ModifierFunction.ON_SPENT_MANA | 消耗魔法时 |
ModifierFunction.ON_TELEPORTING | 传送中 |
ModifierFunction.ON_TELEPORTED | 传送完成 |
Modifier 属性函数(DeclareFunctions 返回值)
| 属性常量 | 说明 |
|---|
ModifierFunction.PREATTACK_BONUS_DAMAGE | 额外攻击力 |
ModifierFunction.ATTACKSPEED_BONUS_CONSTANT | 攻击速度加成 |
ModifierFunction.MOVESPEED_BONUS_CONSTANT | 移速加成 |
ModifierFunction.MOVESPEED_BONUS_PERCENTAGE | 移速百分比加成 |
ModifierFunction.PHYSICAL_ARMOR_BONUS | 护甲加成 |
ModifierFunction.MAGICAL_RESISTANCE_BONUS | 魔抗加成 |
ModifierFunction.STATS_STRENGTH_BONUS | 力量加成 |
ModifierFunction.STATS_AGILITY_BONUS | 敏捷加成 |
ModifierFunction.STATS_INTELLECT_BONUS | 智力加成 |
ModifierFunction.HEALTH_BONUS | 生命加成 |
ModifierFunction.MANA_BONUS | 魔法加成 |
ModifierFunction.HEALTH_REGEN_CONSTANT | 生命回复 |
ModifierFunction.MANA_REGEN_CONSTANT | 魔法回复 |
ModifierFunction.PREATTACK_CRITICALSTRIKE | 暴击 |
ModifierFunction.PROCATTACK_BONUS_DAMAGE_PHYSICAL | 物理额外伤害 |
ModifierFunction.PROCATTACK_BONUS_DAMAGE_MAGICAL | 魔法额外伤害 |
ModifierFunction.PROCATTACK_BONUS_DAMAGE_PURE | 纯粹额外伤害 |
ModifierFunction.EVASION_CONSTANT | 闪避 |
ModifierFunction.STATUS_RESISTANCE_STACKING | 状态抗性 |
ModifierFunction.SPELL_AMPLIFY_PERCENTAGE | 法术增幅 |
ModifierFunction.SPELL_LIFESTEAL | 法术吸血 |
ModifierFunction.HEALTH_REGEN_PERCENTAGE | 百分比生命回复 |
ModifierFunction.TURN_RATE_PERCENTAGE | 转身速率 |
ModifierFunction.RESPAWNTIME | 重生时间 |
ModifierFunction.RESPAWNTIME_PERCENTAGE | 重生时间百分比 |
ModifierFunction.COOLDOWN_PERCENTAGE | 冷却缩减 |
ModifierFunction.MANA_COST_PERCENTAGE | 魔耗增减 |
ModifierFunction.INCOMING_DAMAGE_PERCENTAGE | 受到伤害百分比 |
ModifierFunction.TOTALDAMAGEOUTGOING_PERCENTAGE | 造成伤害百分比 |
ModifierFunction.PERSISTENT_INVISIBILITY | 持续隐身 |
ModifierFunction.REINCARNATION | 重生(骷髅王) |
Modifier 状态(CheckState 返回值)
| 状态常量 | 说明 |
|---|
ModifierState.STUNNED | 眩晕 |
ModifierState.ROOTED | 定身 |
ModifierState.SILENCED | 沉默 |
ModifierState.DISARMED | 缴械 |
ModifierState.INVULNERABLE | 无敌 |
ModifierState.MAGIC_IMMUNE | 魔法免疫 |
ModifierState.INVISIBLE | 隐身 |
ModifierState.FLYING | 飞行 |
ModifierState.FROZEN | 冰冻 |
ModifierState.HEXED | 妖术 |
ModifierState.MUTED | 静默(物品不可用) |
ModifierState.OUT_OF_GAME | 脱离游戏 |
ModifierState.UNSELECTABLE | 不可选中 |
ModifierState.NOT_ON_MINIMAP | 不在小地图 |
ModifierState.NOT_ON_MINIMAP_FOR_ENEMIES | 敌方小地图不可见 |
ModifierState.NO_UNIT_COLLISION | 无碰撞 |
ModifierState.ATTACK_IMMUNE | 攻击免疫 |
ModifierState.BLIND | 致盲 |
ModifierState.PASSIVES_DISABLED | 被动禁用 |
ModifierState.NO_HEALTH_BAR | 无血条 |
ModifierState.NO_TEAM_MOVE_TO | 队友不可移动至 |
ModifierState.NO_TEAM_SELECT | 队友不可选中 |
ModifierState.SPECIALLY_DENIABLE | 可反补 |
ModifierState.COMMAND_RESTRICTED | 指令受限 |
ModifierState.NIGHTMARED | 噩梦 |
ModifierState.LOW_ATTACK_PRIORITY | 低攻击优先级 |
ModifierState.PROVIDES_VISION | 提供视野 |
ModifierState.BLOCK_DISABLED | 格挡禁用 |
ModifierState.EVADE_DISABLED | 闪避禁用 |
ModifierState.CANNOT_MISS | 不可闪避 |
ModifierState.SOFT_DISARMED | 软缴械 |
更多的API请查阅 node_modules/dota-lua-types 目录下的文件。
数据来源
- d2vpkr:
https://github.com/dotabuff/d2vpkr/tree/master/dota/scripts/npc
- Valve Developer Wiki:
https://developer.valvesoftware.com/wiki/Dota_2_Workshop_Tools/Scripting/Abilities_Data_Driven
- ModDota:
https://moddota.com/abilities/ability-keyvalues
- DOTA2 中文 Wiki:
https://www.dota2.com.cn/wiki/Dota_2_Workshop_Tools/Scripting/Abilities_Data_Driven.htm