--- a/net/minecraft/world/damagesource/DamageSource.java
+++ b/net/minecraft/world/damagesource/DamageSource.java
@@ -13,6 +13,7 @@
 import net.minecraft.world.phys.Vec3;
 
 public class DamageSource {
+
     private final Holder<DamageType> type;
     @Nullable
     private final Entity causingEntity;
@@ -20,8 +21,106 @@
     private final Entity directEntity;
     @Nullable
     private final Vec3 damageSourcePosition;
+    // CraftBukkit start
+    @Nullable
+    private org.bukkit.block.Block directBlock; // The block that caused the damage. damageSourcePosition is not used for all block damages
+    @Nullable
+    private org.bukkit.block.BlockState directBlockState; // The block state of the block relevant to this damage source
+    private boolean sweep = false;
+    private boolean melting = false;
+    private boolean poison = false;
+    @Nullable
+    private Entity customEventDamager = null; // This field is a helper for when causing entity damage is not set by vanilla // Paper - fix DamageSource API
+
+    public DamageSource sweep() {
+        this.sweep = true;
+        return this;
+    }
+
+    public boolean isSweep() {
+        return this.sweep;
+    }
+
+    public DamageSource melting() {
+        this.melting = true;
+        return this;
+    }
+
+    public boolean isMelting() {
+        return this.melting;
+    }
+
+    public DamageSource poison() {
+        this.poison = true;
+        return this;
+    }
+
+    public boolean isPoison() {
+        return this.poison;
+    }
+
+    // Paper start - fix DamageSource API
+    @Nullable
+    public Entity getCustomEventDamager() {
+        return (this.customEventDamager != null) ? this.customEventDamager : this.directEntity;
+    }
+
+    public DamageSource customEventDamager(Entity entity) {
+        if (this.directEntity != null) {
+            throw new IllegalStateException("Cannot set custom event damager when direct entity is already set (report a bug to Paper)");
+        }
+        DamageSource damageSource = this.cloneInstance();
+        damageSource.customEventDamager = entity;
+        // Paper end - fix DamageSource API
+        return damageSource;
+    }
+
+    public org.bukkit.block.Block getDirectBlock() {
+        return this.directBlock;
+    }
+
+    public DamageSource directBlock(net.minecraft.world.level.Level world, net.minecraft.core.BlockPos blockPosition) {
+        if (blockPosition == null || world == null) {
+            return this;
+        }
+        return this.directBlock(org.bukkit.craftbukkit.block.CraftBlock.at(world, blockPosition));
+    }
+
+    public DamageSource directBlock(org.bukkit.block.Block block) {
+        if (block == null) {
+            return this;
+        }
+        // Cloning the instance lets us return unique instances of DamageSource without affecting constants defined in DamageSources
+        DamageSource damageSource = this.cloneInstance();
+        damageSource.directBlock = block;
+        return damageSource;
+    }
+
+    public org.bukkit.block.BlockState getDirectBlockState() {
+        return this.directBlockState;
+    }
+
+    public DamageSource directBlockState(org.bukkit.block.BlockState blockState) {
+        if (blockState == null) {
+            return this;
+        }
+        // Cloning the instance lets us return unique instances of DamageSource without affecting constants defined in DamageSources
+        DamageSource damageSource = this.cloneInstance();
+        damageSource.directBlockState = blockState;
+        return damageSource;
+    }
+
+    private DamageSource cloneInstance() {
+        DamageSource damageSource = new DamageSource(this.type, this.directEntity, this.causingEntity, this.damageSourcePosition);
+        damageSource.directBlock = this.getDirectBlock();
+        damageSource.directBlockState = this.getDirectBlockState();
+        damageSource.sweep = this.isSweep();
+        damageSource.poison = this.isPoison();
+        damageSource.melting = this.isMelting();
+        return damageSource;
+    }
+    // CraftBukkit end
 
-    @Override
     public String toString() {
         return "DamageSource (" + this.type().msgId() + ")";
     }
@@ -42,11 +141,11 @@
     }
 
     public DamageSource(Holder<DamageType> type, @Nullable Entity source, @Nullable Entity attacker) {
-        this(type, source, attacker, null);
+        this(type, source, attacker, (Vec3) null);
     }
 
     public DamageSource(Holder<DamageType> type, Vec3 position) {
-        this(type, null, null, position);
+        this(type, (Entity) null, (Entity) null, position);
     }
 
     public DamageSource(Holder<DamageType> type, @Nullable Entity attacker) {
@@ -54,7 +153,7 @@
     }
 
     public DamageSource(Holder<DamageType> type) {
-        this(type, null, null, null);
+        this(type, (Entity) null, (Entity) null, (Vec3) null);
     }
 
     @Nullable
@@ -73,19 +172,29 @@
     }
 
     public Component getLocalizedDeathMessage(LivingEntity killed) {
-        String string = "death.attack." + this.type().msgId();
+        String s = "death.attack." + this.type().msgId();
+
         if (this.causingEntity == null && this.directEntity == null) {
-            LivingEntity livingEntity2 = killed.getKillCredit();
-            String string2 = string + ".player";
-            return livingEntity2 != null
-                ? Component.translatable(string2, killed.getDisplayName(), livingEntity2.getDisplayName())
-                : Component.translatable(string, killed.getDisplayName());
+            LivingEntity entityliving1 = killed.getKillCredit();
+            String s1 = s + ".player";
+
+            return entityliving1 != null ? Component.translatable(s1, killed.getDisplayName(), entityliving1.getDisplayName()) : Component.translatable(s, killed.getDisplayName());
         } else {
-            Component component = this.causingEntity == null ? this.directEntity.getDisplayName() : this.causingEntity.getDisplayName();
-            ItemStack itemStack = this.causingEntity instanceof LivingEntity livingEntity ? livingEntity.getMainHandItem() : ItemStack.EMPTY;
-            return !itemStack.isEmpty() && itemStack.has(DataComponents.CUSTOM_NAME)
-                ? Component.translatable(string + ".item", killed.getDisplayName(), component, itemStack.getDisplayName())
-                : Component.translatable(string, killed.getDisplayName(), component);
+            Component ichatbasecomponent = this.causingEntity == null ? this.directEntity.getDisplayName() : this.causingEntity.getDisplayName();
+            Entity entity = this.causingEntity;
+            ItemStack itemstack;
+
+            if (entity instanceof LivingEntity) {
+                LivingEntity entityliving2 = (LivingEntity) entity;
+
+                itemstack = entityliving2.getMainHandItem();
+            } else {
+                itemstack = ItemStack.EMPTY;
+            }
+
+            ItemStack itemstack1 = itemstack;
+
+            return !itemstack1.isEmpty() && itemstack1.has(DataComponents.CUSTOM_NAME) ? Component.translatable(s + ".item", killed.getDisplayName(), ichatbasecomponent, itemstack1.getDisplayName()) : Component.translatable(s, killed.getDisplayName(), ichatbasecomponent);
         }
     }
 
@@ -94,28 +203,43 @@
     }
 
     public boolean scalesWithDifficulty() {
-        return switch (this.type().scaling()) {
-            case NEVER -> false;
-            case WHEN_CAUSED_BY_LIVING_NON_PLAYER -> this.causingEntity instanceof LivingEntity && !(this.causingEntity instanceof Player);
-            case ALWAYS -> true;
-        };
+        boolean flag;
+
+        switch (this.type().scaling()) {
+            case NEVER:
+                flag = false;
+                break;
+            case WHEN_CAUSED_BY_LIVING_NON_PLAYER:
+                flag = this.causingEntity instanceof LivingEntity && !(this.causingEntity instanceof Player);
+                break;
+            case ALWAYS:
+                flag = true;
+                break;
+            default:
+                throw new MatchException((String) null, (Throwable) null);
+        }
+
+        return flag;
     }
 
     public boolean isCreativePlayer() {
-        if (this.getEntity() instanceof Player player && player.getAbilities().instabuild) {
-            return true;
+        Entity entity = this.getEntity();
+        boolean flag;
+
+        if (entity instanceof Player entityhuman) {
+            if (entityhuman.getAbilities().instabuild) {
+                flag = true;
+                return flag;
+            }
         }
 
-        return false;
+        flag = false;
+        return flag;
     }
 
     @Nullable
     public Vec3 getSourcePosition() {
-        if (this.damageSourcePosition != null) {
-            return this.damageSourcePosition;
-        } else {
-            return this.directEntity != null ? this.directEntity.position() : null;
-        }
+        return this.damageSourcePosition != null ? this.damageSourcePosition : (this.directEntity != null ? this.directEntity.position() : null);
     }
 
     @Nullable
@@ -132,10 +256,24 @@
     }
 
     public DamageType type() {
-        return this.type.value();
+        return (DamageType) this.type.value();
     }
 
     public Holder<DamageType> typeHolder() {
         return this.type;
     }
+
+    // Paper start - add critical damage API
+    private boolean critical;
+    public boolean isCritical() {
+        return this.critical;
+    }
+    public DamageSource critical() {
+        return this.critical(true);
+    }
+    public DamageSource critical(boolean critical) {
+        this.critical = critical;
+        return this;
+    }
+    // Paper end - add critical damage API
 }
