伤害类型与伤害来源
伤害类型表示实体受到哪一种伤害,例如物理、火焰、溺水、魔法、虚空等。区分伤害类型可用于免疫判定(例如烈焰人不受火焰伤害)、附魔(例如爆炸保护仅抵御爆炸伤害)等多种场景。
可以把伤害类型视为伤害来源的模板,或者把伤害来源视为伤害类型的实例。伤害类型在代码中以 ResourceKey 存在,但全部属性定义在数据包中。伤害来源则由游戏根据数据包文件中的值按需创建,还可保存攻击实体等额外上下文。
创建伤害类型
首先创建自己的 DamageType。它属于数据包注册表,因此不在代码中注册,而是在添加对应文件后自动注册。不过,仍需给代码提供获取伤害来源的引用点。为此,定义一个资源键:
public static final ResourceKey<DamageType> EXAMPLE_DAMAGE =
ResourceKey.create(Registries.DAMAGE_TYPE, Identifier.fromNamespaceAndPath(ExampleMod.MOD_ID, "example"));
有了代码中的引用后,就可以在数据文件中指定属性。文件位于 data/examplemod/damage_type/example.json(将 examplemod 和 example 分别替换为 Mod ID 与资源位置名称),内容如下:
{
// The death message id of the damage type. The full death message translation key will be
// "death.attack.examplemod.example" (with swapped-out mod id and name).
"message_id": "examplemod.example",
// Whether this damage type's damage amount scales with difficulty or not. Valid vanilla values are:
// - "never": The damage value remains the same on any difficulty. Common for player-caused damage types.
// - "when_caused_by_living_non_player": The damage value is scaled if the entity is caused by a
// living entity of some sort, including indirectly (e.g. an arrow shot by a skeleton), that is not a player.
// - "always": The damage value is always scaled. Commonly used by explosion-like damage.
"scaling": "when_caused_by_living_non_player",
// The amount of exhaustion caused by receiving this kind of damage.
"exhaustion": 0.1,
// The damage effects (currently only sound effects) that are applied when receiving this kind of damage. Optional.
// Valid vanilla values are "hurt" (default), "thorns", "drowning", "burning", "poking", and "freezing".
"effects": "hurt",
// The death message type. Determines how the death message is built. Optional.
// Valid vanilla values are "default" (default), "fall_variants", and "intentional_game_design".
"death_message_type": "default"
}
建议
scaling、effects和death_message_type字段在内部分别由DamageScaling、DamageEffects与DeathMessageType枚举控制。需要时可以扩展这些枚举以添加自定义值。
原版伤害类型也使用相同格式,整合包开发者可按需修改这些值。
创建和使用伤害来源
通常在调用 Entity#hurt 时即时创建 DamageSource。由于伤害类型属于数据包注册表,需要通过 RegistryAccess 查询,可从 Level#registryAccess 获取。调用 DamageSource 构造函数即可创建来源,最多传入四个参数:
DamageSource damageSource = new DamageSource(
// The damage type holder to use. Query from the registry. This is the only required parameter.
registryAccess.lookupOrThrow(Registries.DAMAGE_TYPE).getOrThrow(EXAMPLE_DAMAGE),
// The direct entity. For example, if a skeleton shot you, the skeleton would be the causing entity
// (= the parameter above), and the arrow would be the direct entity (= this parameter). Similar to
// the causing entity, this isn't always applicable and therefore nullable. Optional, defaults to null.
null,
// The entity causing the damage. This isn't always applicable (e.g. when falling out of the world)
// and may therefore be null. Optional, defaults to null.
null,
// The damage source position. This is rarely used, one example would be intentional game design
// (= nether beds exploding). Nullable and optional, defaulting to null.
null
);
警告
DamageSources#source是new DamageSource的包装方法,但它将第二和第三个参数(直接实体与致害实体)的位置对调了。请确认每个值都传给了正确参数。
如果伤害来源完全没有实体或位置上下文,可以将它缓存在字段中。对于包含这类上下文的来源,通常会添加辅助方法:
public static DamageSource exampleDamage(Entity causer) {
return new DamageSource(
causer.level().registryAccess().lookupOrThrow(Registries.DAMAGE_TYPE).getOrThrow(EXAMPLE_DAMAGE),
causer);
}
建议 原版
DamageSource的工厂方法位于DamageSources,原版DamageType的资源键位于DamageTypes。实体还提供Entity#damageSources作为获取DamageSources实例的便捷方法。
伤害来源最主要的用途是 Entity#hurt,实体受到伤害时会调用该方法。要使用自定义类型对实体造成伤害,只需主动调用它:
// The second parameter is the amount of damage, in half hearts.
entity.hurt(exampleDamage(player), 10);
其他与伤害类型相关的行为(例如无敌判定)通常通过伤害类型标签实现。Minecraft 和 NeoForge 都提供这类标签,分别位于 DamageTypeTags 与 Tags.DamageTypes。
数据生成
更多信息见数据包注册表的数据生成。
伤害类型 JSON 文件可以通过数据生成创建。由于它们属于数据包注册表,需要通过 GatherDataEvent#createDatapackRegistryObjects 添加 DatapackBuiltinEntriesProvider,并将伤害类型放入 RegistrySetBuilder:
// In your datagen class
@SubscribeEvent // on the mod event bus
public static void onGatherData(GatherDataEvent.Client event) {
event.createDatapackRegistryObjects(new RegistrySetBuilder()
// Add a datapack builtin entry provider for damage types. If this lambda becomes longer,
// this should probably be extracted into a separate method for the sake of readability.
.add(Registries.DAMAGE_TYPE, bootstrap -> {
// Use new DamageType() to create an in-code representation of a damage type.
// The parameters map to the values of the JSON file, in the order seen above.
// All parameters except for the message id and the exhaustion value are optional.
bootstrap.register(EXAMPLE_DAMAGE, new DamageType(EXAMPLE_DAMAGE.identifier(),
DamageScaling.WHEN_CAUSED_BY_LIVING_NON_PLAYER,
0.1f,
DamageEffects.HURT,
DeathMessageType.DEFAULT)
)
})
// Add datapack providers for other datapack entries, if applicable.
.add(...)
);
// ...
}
来源: NeoForged/Documentation(26.1)。中文翻译依据提交
4a680cdd4d15。