Starting in 1.19.4, damage sources have been removed and replaced with a new system called DamageTypes. Damage Types are now a datapack registry, meaning they are no longer hardcoded. There are now also DamageType tags. To fetch DamageTypes, you will need to be able to use RegistryAccess.
There are 2 ways to fetch vanilla DamageTypes: use level.damageSources()
or entity.damageSources()
. Theres a method for each vanilla type, such as .fall()
for fall damage, .mobAttack()
for mob attacks, and so on. You can find all the ones you can use in DamageSources
. Here's how to call vanilla sources: https://github.com/TeamTwilight/twilightforest/blob/1.19.x/src/main/java/twilightforest/entity/projectile/SlimeProjectile.java#L66
As an example, I will be walking through how to register a DamageType called thrown_block
.
First, create a ResourceKey for the damage like so:
public static final ResourceKey<DamageType> THROWN_BLOCK = ResourceKey.create(Registries.DAMAGE_TYPE, new ResourceLocation("modid", "thrown_block"));
Next, you'll need to register it via a datapack. You can either add the file to a datapack manually or datagen it. I'll run through datagen, but will also show how the file is supposed to look if you want to manually add it. The method below will be called from datagen, so you can throw it wherever. I normally store it in the same class my ResourceKeys are in.
public static void bootstrap(BootstapContext<DamageType> context) {
context.register(THROWN_BLOCK, new DamageType("modid.thrownBlock", 0.0F));
}
This will tie a DamageType to your ResourceKey. DamageType has a few different constructors. I used the one that uses the most defaults, but you can define a lot. I'll go over what each parameter does. (The entries in parenthesis are how theyre formatted in the json file)
msgId (message_id)
: the string identifier for the localized string. Death messages are localized as follows: death.attack.msgId
. I definitely recommend adding your modid into this message to avoid conflicts.
scaling (scaling)
: a reference to a new enum called DamageScaling. There are 3 available options.
NEVER
: damage always stays the same.WHEN_CAUSED_BY_LIVING_NON_PLAYER
: damage scales based on difficulty when a mob causes the damage.ALWAYS
: damage always scales with difficulty regardless of origin.
exhaustion (exhaustion)
: a float that defines the amount of exhaustion to cause after damage is dealt. Vanilla typically uses anywhere from 0.0F to 0.1F.
effects (effects)
: a reference to a new enum called DamageEffects. Defines what sound should play after a player is hurt. There are 6 available options, each should speak for itself:
HURT
: the generic hurt soundTHORNS
: the sound that plays when thorns activatesDROWNING
: the bubbling sound that plays when a player drownsBURNING
: the scorching sound that plays when a player takes damage while on firePOKING
: plays a pricking sound, used when stepping through a sweet berry bushFREEZING
: plays a crackling sound, used when freezing in powder snow
deathMessageType (death_message_type)
: a reference to a new enum called DeathMessageType. Defines how the unlocalized death message is formatted. There are 3 available options:
DEFAULT
: the default formatting, as defined in themsgId
FALL_VARIANTS
: handles the fall variants of death messages. Its a bit complex to explain, but the logic is found inCombatTracker.getDeathMessage
INTENTIONAL_GAME_DESIGN
: appends a link to bug report seen in the nether bed death message
All of minecraft's damage types are defined here if you would like the json examples: https://github.com/InventivetalentDev/minecraft-assets/tree/1.19.4/data/minecraft/damage_type
Youll need to set up a new class to call from the GatherDataEvent. If you have a class that already extends DatapackBuiltinEntriesProvider
because you have worldgen stuff, you can simply append the DamageType call to the rest of the registries you have defined. Otherwise you can use the below class for reference.
public class RegistryDataGenerator extends DatapackBuiltinEntriesProvider {
public static final RegistrySetBuilder BUILDER = new RegistrySetBuilder()
.add(Registries.DAMAGE_TYPE, YourDamageTypeClass::bootstrap);
private RegistryDataGenerator(PackOutput output, CompletableFuture<HolderLookup.Provider> provider) {
super(output, provider, BUILDER, Set.of("minecraft", "modid"));
}
public static void addProviders(boolean isServer, DataGenerator generator, PackOutput output, CompletableFuture<HolderLookup.Provider> provider, ExistingFileHelper helper) {
generator.addProvider(isServer, new RegistryDataGenerator(output, provider));
generator.addProvider(isServer, new DamageTypeTagGenerator(output, provider.thenApply(r -> append(r, BUILDER)), helper));
}
private static HolderLookup.Provider append(HolderLookup.Provider original, RegistrySetBuilder builder) {
return builder.buildPatch(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), original);
}
}
You'll notice this class also calls DamageTypeTagGenerator
which doesnt exist quite yet. This is so you can tag your sources as needed. All tags are stored in DamageTypeTags
. I assume you know how this part works, so I wont explain it unless asked. These tags essentially replace the old modifiers DamageSources used to have. If youre using the tags, make sure to call addProviders from the event. This is important as the tags wont properly parse otherwise!
Calling your DamageType:
As mentioned before, you will need to be able to use RegistryAccess to fetch your DamageType. I made a few custom methods to do this in TF, but you can also unprivate the various source
methods in vanilla's DamageSources
class. https://github.com/TeamTwilight/twilightforest/blob/1.19.x/src/main/java/twilightforest/init/TFDamageTypes.java#L56-L66. Heres an example of how to call a DamageType: https://github.com/TeamTwilight/twilightforest/blob/1.19.x/src/main/java/twilightforest/entity/monster/GiantMiner.java#L93