--- name: dota2-item description: Dota 2 自定义物品编写指南。触发词:物品、item、装备、道具、配方、recipe、合成。 --- 为 DOTA2 自定义游戏项目编写自定义物品。 ## 核心原则 1. **配置与代码分离**:所有可能与前端共享或需要集中管理的配置、数值、常量(包括但不限于物品属性、价格、冷却、效果数值等),都必须放在 `game/scripts/npc/` 下的 KV 文件的 `AbilityValues` 中。通过 `this.GetSpecialValueFor('键名')` 在 TSTL 中读取,禁止在代码中硬编码任何可能变化的配置值。修改配置后需运行 `npx gulp kv_2_js` 刷新 JSON,保证前后端数据一致 ## 文件结构 | 文件 | 路径 | 用途 | |---|---|---| | 物品 KV | `game/scripts/npc/items_list/<物品名>.txt` | 物品 KV 键值对(手动创建的自定义物品) | | KV 入口 | `game/scripts/npc/npc_items_custom.txt` | 引用 `#base "items_list.txt"`,自动生成部分由 Excel 管理 | | Excel 数据 | `excels/物品表.xlsx` | 自动生成 `items_list.txt`(请勿手动修改) | | 物品源码 | `game/scripts/src/abilities/<模块名>/<物品名>.ts` | TSTL 物品逻辑(物品继承 `BaseItem`) | | 本地化 | `game/resource/addon.csv` | 物品名称、描述、数值标签 | ## 工作流 ### 步骤 1:确认物品需求 - 明确物品类型(被动/主动/切换/消耗品) - 确认物品价格、商店标签、品质 - 确认物品是否可合成、是否需要配方 - 确认物品是否需要自定义 TSTL 逻辑 ### 步骤 2:编写 KV 配置 在 `game/scripts/npc/items_list/<物品名>.txt` 中创建 KV 文件,或在 `excels/物品表.xlsx` 中添加行(由 Gulp 自动生成到 `items_list.txt`)。 KV 文件必须在 `npc_items_custom.txt` 中通过 `#base` 引用后生效。 物品名必须以 `item_` 开头。 **KV 结构规则**:物品名称必须在第一级,所有属性写在第二级的 `{}` 内: ```kv "item_custom_sword" { "BaseClass" "item_lua" "AbilityBehavior" "DOTA_ABILITY_BEHAVIOR_PASSIVE" "ItemCost" "2150" "ItemQuality" "rare" "AbilityTextureName" "item_custom_sword" } ``` ### 步骤 2b:编译 KV 到 JSON(必须执行) 修改 KV 文件后,必须运行以下命令将 KV 编译为 JSON,供前后端统一引用: ```bash # 编译所有 KV 文件为 JSON npx gulp kv_2_js # 或快捷命令(同时执行 sheet→kv 和 kv→js) npx gulp jssync ``` 编译后输出到: - `content/panorama/src/json/` — 前端 Panorama 使用 - `game/scripts/src/json/` — 后端 TSTL 使用 **前后端数据分离原则**: - 所有游戏数值配置写在 KV 文件中 - 前后端都从编译后的 JSON 文件中读取同一份数据 - 禁止在前端或后端代码中硬编码数值常量,避免数据不一致 ### 步骤 3:编写 TSTL 源码(如需要) 如果物品有自定义行为(包括纯被动),在 `game/scripts/src/abilities/<模块名>/<物品名>.ts` 中: 1. 从 `dota_ts_adapter` 导入 `BaseItem`、`registerAbility` 2. 用 `@registerAbility()` 装饰器注册物品类,继承 `BaseItem` 3. 重写必要的生命周期方法(`OnSpellStart`、`OnChannelFinish` 等) 4. 通过 `this.GetSpecialValueFor('键名')` 读取 KV 中的 `AbilityValues` 5. 如果是制作被动技能或者被动物品,使用 GetIntrinsicModifierName 方法获取 modifier 名称,之后在被动modifier里面配置所有的属性或者被动触发的效果。 6. **Modifier 放置规则**:如果 modifier 是物品自己的(被该物品引用),直接放在物品同一个 `.ts` 文件中,不需要单独建文件;只有独立的、机制性的 modifier(不被任何技能/物品直接引用,如通用抛物线运动、通用buff,或者多个技能共用的modifier等)才需要单独放在 `src/modifiers/` 目录下 ### 步骤 4:添加本地化 在 `game/resource/addon.csv` 中添加: - `DOTA_Tooltip_ability_item_<物品名>` → 物品显示名 - `DOTA_Tooltip_ability_item_<物品名>_Description` → 物品描述 - `DOTA_Tooltip_ability_item_<物品名>_` → 数值标签 ### 步骤 5:验证 - 编译 TSTL(如有自定义逻辑),确认无类型错误 - 进入游戏测试物品效果 ## KV 键值参考 物品继承所有技能的 KV 键,此外还有物品专属键。 ### 通用键(继承自技能) | 键名 | 说明 | 示例值 | |---|---|---| | `BaseClass` | 基类 | `item_lua` | | `AbilityBehavior` | 行为类型 | `DOTA_ABILITY_BEHAVIOR_PASSIVE` / `DOTA_ABILITY_BEHAVIOR_NO_TARGET \| DOTA_ABILITY_BEHAVIOR_ITEM` | | `AbilityTextureName` | 图标名称 | `item_magic_wand` | | `MaxLevel` | 最大等级 | `1` / `3` | | `AbilityCooldown` | 冷却 | `12` | | `AbilityManaCost` | 魔法消耗 | `100` | | `AbilityCastRange` | 施法距离 | `600` | | `AbilityCastPoint` | 施法前摇 | `0.0` | | `AbilityUnitTargetTeam` | 目标队伍 | `DOTA_UNIT_TARGET_TEAM_ENEMY` | | `AbilityUnitTargetType` | 目标类型 | `DOTA_UNIT_TARGET_HERO \| DOTA_UNIT_TARGET_BASIC` | | `AbilityUnitDamageType` | 伤害类型 | `DAMAGE_TYPE_MAGICAL` | | `ScriptFile` | 脚本路径 | `abilities/<模块名>/item_custom.lua` | | `AbilityValues` | 自定义数值 | (子键) | | `Precache` | 预缓存 | (子键:`particle`、`soundfile`、`model`) | ### 物品基础键 | 键名 | 说明 | 示例值 | |---|---|---| | `ItemCost` | 物品价格 | `2150` | | `ItemShopTags` | 商店标签 | `damage;attack_speed` | | `ItemQuality` | 物品品质 | `component` / `common` / `rare` / `epic` / `secret_shop` / `consumable` | | `ItemAliases` | 别名 | `tp;town portal scroll;teleport` | | `ID` | 唯一 ID | `3003` | ### 物品属性键 | 键名 | 说明 | 示例值 | |---|---|---| | `ItemStackable` | 是否可堆叠 | `1` | | `ItemInitialCharges` | 初始充能数 | `1` | | `ItemRequiresCharges` | 需要充能 | `1` | | `ItemDisplayCharges` | 显示充能数 | `1` | | `ItemPermanent` | 永久物品 | `0` | | `ItemShareability` | 共享性 | `ITEM_FULLY_SHAREABLE` / `ITEM_PARTIALLY_SHAREABLE` / `ITEM_NOT_SHAREABLE` | | `ItemDeclares` | 物品声明 | `DECLARE_USAGE_BOT` | | `ItemCanBeDisassembled` | 可拆分 | `1` | | `ItemCanBeSoldByPlayer` | 玩家可出售 | `0` | ### 物品商店键 | 键名 | 说明 | 示例值 | |---|---|---| | `SecretShop` | 秘密商店 | `1` | | `SideShop` | 边路商店 | `1` | | `IsObsolete` | 是否过时 | `1` | | `SuggestPregame` | 建议出门装 | `1` | | `SuggestEarlygame` | 建议前期装 | `1` | | `SuggestLategame` | 建议后期装 | `1` | ### 物品合成键 | 键名 | 说明 | 示例值 | |---|---|---| | `ItemRecipe` | 是否为配方 | `1` | | `ItemResult` | 合成结果 | `item_bfury` | | `ItemRequirements` | 合成需求 | `"01" "item_broadsword;item_mithril_hammer"` | | `UpgradesItems` | 升级物品 | `item_boots` | | `UpgradeRecipe` | 升级配方 | `1` | | `MaxUpgradeLevel` | 最大升级等级 | `3` | | `SubAbilityNames` | 子技能名称 | `ability1;ability2` | | `ReplaceAbility` | 替换技能 | `ability_name` | ### 物品特效与声音键 | 键名 | 说明 | 示例值 | |---|---|---| | `Model` | 模型路径 | `models/props_gameplay/quelling_blade.vmdl` | | `Effect` | 效果 | `particles/...` | | `UIDropSound` | 丢弃声音 | `Item.Drop` | | `UIPickupSound` | 拾取声音 | `Item.PickUp` | | `WorldDropSound` | 世界丢弃声音 | `Item.WorldDrop` | | `PingOverrideText` | Ping 覆盖文本 | `#DOTA_Ping_GG` | ### 物品限制键 | 键名 | 说明 | 示例值 | |---|---|---| | `SpeciallyAllowedInNeutralSlot` | 允许放入中立物品栏 | `1` | | `SpeciallyBannedFromNeutralSlot` | 禁止放入中立物品栏 | `1` | | `ShouldNotSuggestMainGame` | 主模式不推荐 | `1` | | `ShowDroppedItemTooltip` | 显示掉落提示 | `1` | | `ShowGiveIndicatorOnTargetCast` | 显示给与指示 | `1` | | `TokenTier` | 代币等级 | `1` / `2` / `3` / `4` / `5` | ### ItemShareability 常量 | 常量 | 说明 | |---|---| | `ITEM_FULLY_SHAREABLE` | 完全共享 | | `ITEM_PARTIALLY_SHAREABLE` | 部分共享 | | `ITEM_NOT_SHAREABLE` | 不可共享 | ### ItemQuality 常量 | 常量 | 说明 | |---|---| | `component` | 组件 | | `common` | 普通 | | `rare` | 稀有 | | `epic` | 史诗 | | `secret_shop` | 秘密商店 | | `consumable` | 消耗品 | ## TSTL 编程参考 ### 装饰器与基类 | 装饰器/基类 | 用途 | |---|---| | `@registerAbility()` | 注册物品类(物品也使用此装饰器) | | `BaseItem` | 物品基类,所有的物品类都需要继承 | ### 物品常用方法 | 方法 | 用途 | |---|---| | `OnSpellStart()` | 使用物品时 | | `OnAbilityPhaseStart()` | 使用前摇阶段(返回 bool) | | `OnChannelFinish(bInterrupted)` | 持续施法结束 | | `GetBehavior()` | 获取行为类型 | | `GetSpecialValueFor(key)` | 读取 AbilityValues 中的值 | | `Precache(context)` | 预缓存资源 | | `GetCooldown(level)` | 获取冷却 | | `GetManaCost(level)` | 获取魔法消耗 | | `GetCurrentCharges()` | 获取当前充能数 | | `SetCurrentCharges(charges)` | 设置充能数 | | `GetCost()` | 获取物品价格 | | `GetPurchaseTime()` | 获取购买时间 | | `GetPurchaser()` | 获取购买者 | | `IsCombineLocked()` | 是否锁定合成 | | `SetCombineLocked(bLocked)` | 锁定/解锁合成 | ### 物品事件回调 | 方法 | 用途 | |---|---| | `OnItemEquipped(hUnit)` | 装备时 | | `OnItemUnequipped(hUnit)` | 卸下时 | | `OnItemPurchased()` | 购买时 | | `OnItemSold()` | 出售时 | | `OnItemSpawned()` | 物品生成时 |