主题
响应系统:主动方、被动方与两条效果链
响应用于在伤害、技能阶段、投射物命中、单位死亡等时机检查条件、修改本次行为,或执行附加效果。配置反伤、受击自疗、吸血和死亡复活时,先分别确定原事件中的双方、响应挂载对象,以及附加效果的施法者与目标。
原事件中的主动方与被动方
ResponseContext.Attacker 和 Defender 描述引发响应的事件,不是响应附加效果自身的 Caster 和 Target。例如 A 伤害 B,主动方是 A,被动方是 B;B 的反伤效果则通常由 B 对 A 执行。
| 响应类型 | 主动方 Attacker | 被动方 Defender | IncomingContext |
|---|---|---|---|
伤害 GameDataResponseDamage | 伤害效果的施法者 | 伤害效果的目标 | EffectDamage,可从 ResponseContextDamage 读取 Damage 和 EffectDamage |
技能阶段 GameDataResponseAbilityStage | 施法单位 | 技能指令目标 | 技能指令;另有 Ability、Stage、AbilityTarget |
移动器命中 GameDataResponseMoverImpact | 移动器上下文的施法者 | 本次命中目标 | Mover;投射物发射效果可读 LaunchEffect |
死亡 GameDataResponseUnitDeath | 击杀者;没有击杀者时回退死亡单位 | 死亡单位 | 死亡单位;击杀者另读 Killer / KillerUnit |
Defender 可以是位置等 ITarget,不要一律强转为 Unit。死亡响应的 IncomingContext 也不是致死伤害效果,不能将它强转为 EffectDamage。
Location 选择响应侧,附加效果默认指向另一方
GameDataResponse.Location 默认为 ResponseLocation.Defender。它决定宿主以原事件中的哪一方触发响应;伤害、技能阶段和死亡响应都遵循这条规则。附加效果的默认目标按下表确定:
Location | 一般挂载位置 | ResponseEffect 的默认目标 |
|---|---|---|
Attacker | 主动方单位或其技能、Buff | 本次事件的 Defender |
Defender | 被动方单位或其技能、Buff | 本次事件的 Attacker |
Location = Defender 不表示给自己执行效果。 它很适合“受击后反伤攻击者”;“受击后治疗自己”还需在治疗节点显式选择目标。Location 也不会把响应成本转交给另一方:成本始终由响应宿主 Host 支付。
死亡响应中,Location = Defender 匹配 Host 自身死亡;Location = Attacker 匹配 Host 作为 Killer 击杀单位。无击杀者时不触发主动方响应;上下文的 Attacker 回退为死亡单位只用于目标解析,不会将死亡单位视为击杀者。若明确由单位击杀自己,则同一宿主分别满足两侧条件。
引发响应的效果链与响应附加效果链
以 A 攻击 B、B 身上直接挂载被动方伤害响应为例:
text
A 的伤害效果链:Caster=A,伤害节点 Target=B
|
| 引发 B 的响应,IncomingContext 指向该伤害节点
v
B 的响应附加效果链:Caster=B,根节点 DefaultTarget=A
|
+-- 默认目标的伤害节点:Target=A(反伤)
+-- 显式选择 Caster 的治疗节点:Target=B(自疗)两条链的上下文含义不同:
| 字段 | 在响应附加效果中的含义 |
|---|---|
Caster | 来自响应 Owner 的执行上下文;不是自动沿用引发响应的施法者 |
DefaultTarget | 传给本节点的默认目标;根节点默认是事件另一方,子节点通常继承父节点的已解析目标 |
Target | 本节点按 TargetLocation 与 TargetType 解析后的实际目标 |
MainTarget | 创建响应附加效果共享数据时记录的主目标;改某个节点的目标不会自动改它 |
Shared.ResponseContext | 本次响应的上下文,可读取原事件的双方与具体响应信息 |
Shared.ResponseContext.IncomingContext | 引发响应的来源;它的 Caster/Target 仍属于原事件 |
响应附加效果由 Owner 创建,并携带响应上下文。不要把它当成原伤害节点直接 ExecuteChild 的延续,也不要假定它继承原树的施法者、主目标、局部变量或可按名称查找的祖先节点。需要原事件信息时,显式读取 Shared.ResponseContext,再按具体类型读取伤害、技能、命中或死亡信息。
例如在响应附加效果公式中读取原伤害:
csharp
#if SERVER
Amount = effect => effect.Shared.ResponseContext is ResponseContextDamage context
? context.Damage.Current * 0.2
: 0,
#endif这只决定数值,不会自动将效果目标改为受伤单位。公式参数若是 ResponseContext,是在读取响应事件;参数若是 Effect,effect.Caster 和 effect.Target 是当前附加效果的施法者与实际目标。
Host、Owner 与 Caster
Response.Host 是承载响应的单位,Response.Owner 是提供这条响应配置的对象。二者可能不同:
| 响应配置来源 | Host | Owner | 附加效果的 Caster 来源 |
|---|---|---|---|
单位 Responses | 该单位 | 该单位 | 该单位 |
技能 Responses | 技能宿主单位 | 技能实例 | 技能宿主单位 |
Buff Responses | Buff 宿主单位 | Buff 实例 | Buff 当前叠层保存的施加上下文,可能是另一单位 |
所以 TargetLocation.Caster 适合单位直接挂载响应时的“作用于自己”。Buff 响应中它可能指向 Buff 施加者,不能一律当成 Buff 宿主;需要宿主或死亡单位时,从 Shared.ResponseContext.Response.Host 或具体响应上下文显式取得。Buff 自身保存的创建链也不等于这次触发响应的伤害链。
三种常见配置
受击反伤
把伤害响应挂在 B 上,Location = Defender。附加伤害效果保留 TargetLocation.Default 时目标为攻击者 A;单位直接挂载时附加效果施法者为 B。反伤数值可以从 ResponseContextDamage.Damage 读取。
受击自疗
同样在 B 上挂被动方响应,但治疗效果显式设置:
csharp
TargetLocation = new() { Value = TargetLocation.Caster },
TargetType = TargetType.Unit,这样目标是 B 自己。若来自 Buff,先核对施加者与宿主,按上一节选择对象。
死亡后延迟复活自己
在单位上直接挂 GameDataResponseUnitDeath,ResponseEffect 指向延迟效果,延迟结束后执行复活效果。两个节点都可显式指向 Caster:
json
{
"Root": {
"$type": "GameCore.Execution.Data.GameDataEffectPersistDelay",
"Amount": "00:00:05",
"TargetLocation": { "Value": "Caster" },
"TargetType": "Unit",
"FinalEffect": "$this.Revive"
},
"Revive": {
"$type": "GameCore.Execution.Data.GameDataEffectUnitRevive",
"TargetLocation": { "Value": "Caster" },
"TargetType": "Unit"
}
}此片段是效果表,还需把死亡响应的 ResponseEffect 链接到它的 Root,并将响应挂入目标单位的 Responses。DeathRemovalDelay 需要覆盖复活等待时间,且不能使用会立即销毁实体的死亡类型。
Unit.Revive() 和复活效果只恢复存活状态,不自动补满生命值;按玩法另配生命恢复效果或生命值修改。
若保留默认目标,怪物杀死主角时,复活效果会指向怪物;直接 unit.Kill() 没有击杀者时,主动方回退为主角自己,两种测试便会表现不同。这不能单凭“主角没复活”判定为死亡响应未触发。
JSON、代码生成与排查
数编 JSON 的枚举使用名称字符串,例如 "Location": "Defender"、"Value": "Caster"、"TargetType": "Unit"。不要用 C# 枚举的底层数字代替名称;生成器可能省略这类赋值,效果便使用默认值。C# 定义则使用对应枚举成员。
排查“响应没有生效”时按下列顺序核对:
- 核对源 JSON、继承后的有效值、生成代码及本局实际加载的配置。若生成结果出现
// Value = 1或// TargetType = 1,赋值没有生效;支持的编辑器版本还会给出GDATA001诊断。修源数据并重新生成,不手改生成代码。 - 确认响应实际挂载到预期
Host/Owner,并核对响应侧、激活状态、条件、概率与成本。 - 分别记录原事件
Attacker/Defender和附加效果Caster/DefaultTarget/Target的对象身份。收到死亡事件只证明死亡发生,不能证明复活节点选中了死亡单位。 - 区分响应未执行、附加效果验证失败、效果成功但目标选错。
LogExecutionFailure记录效果执行失败;对活单位执行复活是无变化的成功调用,不会因此打印失败日志。 - 死亡复活同时测试“脚本直接 Kill”与“另一单位伤害致死”。若采用代码事件作为临时复活通路,恢复数编方案时避免两条通路同时执行。
运行期证据收集见从配置到运行证据与行为问题排查 Skill。效果树的一般目标规则见效果系统。