Skip to content

Commit

Permalink
Allow setting if the critical hit should disable sweep attack in Crit…
Browse files Browse the repository at this point in the history
…icalHitEvent, adding SweepAttackEvent (#1496)

This allows mods to have better handling over crit-sweep behavior through a new flag in `CriticalHitEvent` as well as providing full sweep control via `SweepAttackEvent`.
  • Loading branch information
lcy0x1 authored Sep 24, 2024
1 parent 5a2503e commit 8ec872a
Show file tree
Hide file tree
Showing 4 changed files with 136 additions and 13 deletions.
17 changes: 13 additions & 4 deletions patches/net/minecraft/world/entity/player/Player.java.patch
Original file line number Diff line number Diff line change
Expand Up @@ -254,12 +254,12 @@
if (p_36347_.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE)
&& p_36347_ instanceof Projectile projectile
&& projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this, this, true)) {
@@ -1170,8 +_,12 @@
@@ -1170,19 +_,28 @@
&& !this.isPassenger()
&& p_36347_ instanceof LivingEntity
&& !this.isSprinting();
+ // Neo: Fire the critical hit event and override the critical hit status and damage multiplier based on the event.
+ // The boolean local above (flag2) is the vanilla critical hit result.
+ // The boolean local above (flag1) is the vanilla critical hit result.
+ var critEvent = net.neoforged.neoforge.common.CommonHooks.fireCriticalHit(this, p_36347_, flag1, flag1 ? 1.5F : 1.0F);
+ flag1 = critEvent.isCriticalHit();
if (flag1) {
Expand All @@ -268,17 +268,26 @@
}

float f3 = f + f1;
@@ -1179,9 +_,7 @@
boolean flag2 = false;
double d0 = (double)(this.walkDist - this.walkDistO);
if (flag4 && !flag1 && !flag && this.onGround() && d0 < (double)this.getSpeed()) {
- if (flag4 && !flag1 && !flag && this.onGround() && d0 < (double)this.getSpeed()) {
+ // Neo: Replace !flag1 (!isCriticalHit) with the logic from the CriticalHitEvent.
+ boolean critBlocksSweep = critEvent.isCriticalHit() && critEvent.disableSweep();
+ if (flag4 && !critBlocksSweep && !flag && this.onGround() && d0 < (double)this.getSpeed()) {
+ // Neo: Make sweep attacks check SWORD_SWEEP instead of instanceof SwordItem.
ItemStack itemstack1 = this.getItemInHand(InteractionHand.MAIN_HAND);
- if (itemstack1.getItem() instanceof SwordItem) {
- flag2 = true;
- }
+ flag2 = itemstack1.canPerformAction(net.neoforged.neoforge.common.ItemAbilities.SWORD_SWEEP);
}
+
+ // Neo: Fire the SweepAttackEvent and overwrite the value of flag2 (the local controlling if a sweep will occur).
+ var sweepEvent = net.neoforged.neoforge.common.CommonHooks.fireSweepAttack(this, p_36347_, flag2);
+ flag2 = sweepEvent.isSweeping();

float f6 = 0.0F;
if (p_36347_ instanceof LivingEntity livingentity) {
@@ -1217,11 +_,12 @@

for (LivingEntity livingentity2 : this.level()
Expand Down
12 changes: 12 additions & 0 deletions src/main/java/net/neoforged/neoforge/common/CommonHooks.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@
import net.neoforged.neoforge.event.entity.player.PlayerEnchantItemEvent;
import net.neoforged.neoforge.event.entity.player.PlayerEvent;
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
import net.neoforged.neoforge.event.entity.player.SweepAttackEvent;
import net.neoforged.neoforge.event.level.BlockDropsEvent;
import net.neoforged.neoforge.event.level.BlockEvent;
import net.neoforged.neoforge.event.level.NoteBlockEvent;
Expand Down Expand Up @@ -923,6 +924,17 @@ public static CriticalHitEvent fireCriticalHit(Player player, Entity target, boo
return NeoForge.EVENT_BUS.post(new CriticalHitEvent(player, target, damageModifier, vanillaCritical));
}

/**
* Fires the {@link SweepAttackEvent} and returns the resulting event.
*
* @param player The attacking player.
* @param target The attack target.
* @param isVanillaSweep If the attack would have been a sweep attack by vanilla's rules in {@link Player#attack(Entity)}.
*/
public static SweepAttackEvent fireSweepAttack(Player player, Entity target, boolean isVanillaSweep) {
return NeoForge.EVENT_BUS.post(new SweepAttackEvent(player, target, isVanillaSweep));
}

/**
* Hook to fire {@link ItemAttributeModifierEvent}. Modders should use {@link ItemStack#forEachModifier(EquipmentSlot, BiConsumer)} instead.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,20 @@

/**
* This event is fired when a player attacks an entity in {@link Player#attack(Entity)}.
* It can be used to change the critical hit status and damage modifier
* <p>
* In the event the attack was not a critical hit, the event will still be fired, but it will be preemptively cancelled.
**/
* It can be used to change the critical hit status and the critical damage multiplier.
* Additionally, this event allows controlling if the critical hit will impact sweep conditions.
* <p>
* This event is fired on both the logical client and logical server.
*/
public class CriticalHitEvent extends PlayerEvent {
private final Entity target;
private final float vanillaDmgMultiplier;
private final boolean isVanillaCritical;

private float dmgMultiplier;
private boolean isCriticalHit;
private boolean disableSweep = true;

/**
* Fire via {@link CommonHooks#fireCriticalHit(Player, Entity, boolean, float)}
Expand All @@ -44,8 +47,6 @@ public Entity getTarget() {
* The damage multiplier is applied to the base attack's damage if the attack {@linkplain #isCriticalHit() critically hits}.
* <p>
* A damage multiplier of 1.0 will not change the damage, a value of 1.5 will increase the damage by 50%, and so on.
*
* @param modifier The new damage modifier.
*/
public float getDamageMultiplier() {
return this.dmgMultiplier;
Expand All @@ -55,8 +56,8 @@ public float getDamageMultiplier() {
* Sets the damage multiplier for the critical hit. Not used if {@link #isCriticalHit()} is false.
* <p>
* Changing the damage modifier to zero does not guarantee that the attack does zero damage.
*
* @param modifier The new damage modifier. Must not be negative.
*
* @param dmgMultiplier The new damage modifier. Must not be negative.
* @see #getDamageMultiplier()
*/
public void setDamageMultiplier(float dmgMultiplier) {
Expand All @@ -75,7 +76,7 @@ public boolean isCriticalHit() {

/**
* Changes the critical hit state.
*
*
* @param isCriticalHit true if the attack should critically hit
*/
public void setCriticalHit(boolean isCriticalHit) {
Expand All @@ -86,7 +87,7 @@ public void setCriticalHit(boolean isCriticalHit) {
* Gets the original damage multiplier set by vanilla.
* <p>
* If the event {@link #isVanillaCritical()}, the damage multiplier will be 1.5, otherwise it will be 1.0
*
*
* @see #getDamageMultiplier()
*/
public float getVanillaMultiplier() {
Expand All @@ -99,4 +100,27 @@ public float getVanillaMultiplier() {
public boolean isVanillaCritical() {
return this.isVanillaCritical;
}

/**
* Sets if this attack should prevent a sweep from occurring.
* <p>
* In vanilla, a critical hit always prevents a sweep from occurring.
* This method can allow an attack to both critically hit and sweep without having to validate the other sweep conditions.
*
* @see {@link SweepAttackEvent} for more advanced sweep attack handling.
*/
public void setDisableSweep(boolean disableSweep) {
this.disableSweep = disableSweep;
}

/**
* If this attack is a {@linkplain #isCriticalHit() critical hit}, returns if a sweep should be prevented.
* <p>
* If this attack is <b>not</b> a critical hit, the return value of this method is meaningless.
*
* @see {@link SweepAttackEvent} for more advanced sweep attack handling.
*/
public boolean disableSweep() {
return disableSweep;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright (c) NeoForged and contributors
* SPDX-License-Identifier: LGPL-2.1-only
*/

package net.neoforged.neoforge.event.entity.player;

import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.neoforged.bus.api.ICancellableEvent;
import net.neoforged.neoforge.common.ItemAbilities;

/**
* The SweepAttackEvent is fired when a {@link Player} attacks a target, after the {@link CriticalHitEvent} has been fired.
* <p>
* This event can be used to force an attack to trigger a sweep, or to prevent a sweep from occurring.
* <p>
* This event is fired on both the logical client and logical server.
*/
public class SweepAttackEvent extends PlayerEvent implements ICancellableEvent {
private final Entity target;
private final boolean isVanillaSweep;

private boolean isSweeping;

public SweepAttackEvent(Player player, Entity target, boolean isVanillaSweep) {
super(player);
this.target = target;
this.isSweeping = this.isVanillaSweep = isVanillaSweep;
}

/**
* Returns the target of the attack, which is guaranteed to be a valid attack target.
*/
public Entity getTarget() {
return this.target;
}

/**
* Returns true if the attack would cause a sweep by utilizing the vanilla rules.
* <p>
* The vanilla rules are as follows. All of them must be true for a vanilla sweep to occur:
* <ol>
* <li>The player's attack strength is greater than 90%.</li>
* <li>The attack is not a critical hit, or is a critical hit which does not {@linkplain CriticalHitEvent#disableSweep() disable the sweep attack}.</li>
* <li>The player is on the ground.</li>
* <li>The distance the player has traveled this tick is less than their speed.</li>
* <li>The player's weapon supports sweep attacks via {@link ItemAbilities#SWORD_SWEEP}.</li>
* </ol>
*/
public boolean isVanillaSweep() {
return this.isVanillaSweep;
}

/**
* Returns true if the attack will be trigger a sweep.
*/
public boolean isSweeping() {
return this.isSweeping;
}

/**
* @param sweep Whether to enable a sweep for this attack.
*/
public void setSweeping(boolean sweep) {
this.isSweeping = sweep;
}

/**
* Cancels the event, preventing further event handlers from acting. Canceling the event will use the current value of {@link #isSweeping()}.
* <p>
* If you intend to perform a custom sweep attack, you should cancel the event and {@link #setSweeping} to false before performing your handling.
*/
@Override
public void setCanceled(boolean canceled) {
ICancellableEvent.super.setCanceled(canceled);
}
}

0 comments on commit 8ec872a

Please sign in to comment.