|
@@ -11,13 +11,9 @@ import com.gmail.nossr50.mcMMO;
|
|
import com.gmail.nossr50.util.EventUtils;
|
|
import com.gmail.nossr50.util.EventUtils;
|
|
import com.gmail.nossr50.util.ItemUtils;
|
|
import com.gmail.nossr50.util.ItemUtils;
|
|
import com.gmail.nossr50.util.Permissions;
|
|
import com.gmail.nossr50.util.Permissions;
|
|
-import com.gmail.nossr50.util.player.NotificationManager;
|
|
|
|
import com.gmail.nossr50.util.random.Probability;
|
|
import com.gmail.nossr50.util.random.Probability;
|
|
import com.gmail.nossr50.util.random.ProbabilityUtil;
|
|
import com.gmail.nossr50.util.random.ProbabilityUtil;
|
|
-import com.gmail.nossr50.util.skills.PerksUtils;
|
|
|
|
import com.gmail.nossr50.util.skills.RankUtils;
|
|
import com.gmail.nossr50.util.skills.RankUtils;
|
|
-import com.gmail.nossr50.util.skills.SkillUtils;
|
|
|
|
-import com.gmail.nossr50.util.sounds.SoundManager;
|
|
|
|
import com.gmail.nossr50.util.sounds.SoundType;
|
|
import com.gmail.nossr50.util.sounds.SoundType;
|
|
import net.kyori.adventure.text.Component;
|
|
import net.kyori.adventure.text.Component;
|
|
import net.kyori.adventure.text.TextComponent;
|
|
import net.kyori.adventure.text.TextComponent;
|
|
@@ -34,9 +30,17 @@ import org.jetbrains.annotations.VisibleForTesting;
|
|
|
|
|
|
import java.util.Locale;
|
|
import java.util.Locale;
|
|
|
|
|
|
|
|
+import static com.gmail.nossr50.util.player.NotificationManager.sendPlayerInformation;
|
|
|
|
+import static com.gmail.nossr50.util.random.ProbabilityUtil.getSubSkillProbability;
|
|
|
|
+import static com.gmail.nossr50.util.skills.SkillUtils.applyXpGain;
|
|
|
|
+import static com.gmail.nossr50.util.sounds.SoundManager.sendCategorizedSound;
|
|
|
|
+
|
|
public class Roll extends AcrobaticsSubSkill {
|
|
public class Roll extends AcrobaticsSubSkill {
|
|
|
|
|
|
|
|
|
|
|
|
+ public static final String GRACEFUL_ROLL_ACTIVATED_LOCALE_STR_KEY = "Acrobatics.Ability.Proc";
|
|
|
|
+ public static final String ROLL_ACTIVATED_LOCALE_KEY = "Acrobatics.Roll.Text";
|
|
|
|
+
|
|
public Roll() {
|
|
public Roll() {
|
|
super("Roll", EventPriority.HIGHEST, SubSkillType.ACROBATICS_ROLL);
|
|
super("Roll", EventPriority.HIGHEST, SubSkillType.ACROBATICS_ROLL);
|
|
}
|
|
}
|
|
@@ -49,23 +53,14 @@ public class Roll extends AcrobaticsSubSkill {
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
public boolean doInteraction(Event event, mcMMO plugin) {
|
|
public boolean doInteraction(Event event, mcMMO plugin) {
|
|
- //TODO: Go through and API this up
|
|
|
|
-
|
|
|
|
- /*
|
|
|
|
- * Roll is a SubSkill which allows players to negate fall damage from certain heights with sufficient Acrobatics skill and luck
|
|
|
|
- * Roll is activated when a player takes damage from a fall
|
|
|
|
- * If a player holds shift, they double their odds at a successful roll and upon success are told they did a graceful roll.
|
|
|
|
- */
|
|
|
|
-
|
|
|
|
- //Casting
|
|
|
|
- EntityDamageEvent entityDamageEvent = (EntityDamageEvent) event;
|
|
|
|
|
|
+ final EntityDamageEvent entityDamageEvent = (EntityDamageEvent) event;
|
|
|
|
|
|
//Make sure a real player was damaged in this event
|
|
//Make sure a real player was damaged in this event
|
|
if (!EventUtils.isRealPlayerDamaged(entityDamageEvent))
|
|
if (!EventUtils.isRealPlayerDamaged(entityDamageEvent))
|
|
return false;
|
|
return false;
|
|
|
|
|
|
- if (entityDamageEvent.getCause() == EntityDamageEvent.DamageCause.FALL) {//Grab the player
|
|
|
|
- McMMOPlayer mmoPlayer = EventUtils.getMcMMOPlayer(entityDamageEvent.getEntity());
|
|
|
|
|
|
+ if (entityDamageEvent.getCause() == EntityDamageEvent.DamageCause.FALL) {
|
|
|
|
+ final McMMOPlayer mmoPlayer = EventUtils.getMcMMOPlayer(entityDamageEvent.getEntity());
|
|
|
|
|
|
if (mmoPlayer == null)
|
|
if (mmoPlayer == null)
|
|
return false;
|
|
return false;
|
|
@@ -75,16 +70,38 @@ public class Roll extends AcrobaticsSubSkill {
|
|
*/
|
|
*/
|
|
|
|
|
|
if (canRoll(mmoPlayer)) {
|
|
if (canRoll(mmoPlayer)) {
|
|
- entityDamageEvent.setDamage(
|
|
|
|
- rollCheck(mmoPlayer, entityDamageEvent.getFinalDamage(), mmoPlayer.getPlayer().isSneaking()));
|
|
|
|
|
|
+ final RollResult rollResult
|
|
|
|
+ = rollCheck(mmoPlayer, entityDamageEvent);
|
|
|
|
+ if (rollResult == null) {
|
|
|
|
+ // no-op - fall was fatal or otherwise did not get processed
|
|
|
|
+ return false;
|
|
|
|
+ }
|
|
|
|
+ entityDamageEvent.setDamage(rollResult.getModifiedDamage());
|
|
|
|
|
|
if (entityDamageEvent.getFinalDamage() == 0) {
|
|
if (entityDamageEvent.getFinalDamage() == 0) {
|
|
entityDamageEvent.setCancelled(true);
|
|
entityDamageEvent.setCancelled(true);
|
|
- return true;
|
|
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ // Roll happened, send messages and XP
|
|
|
|
+ if (rollResult.isRollSuccess()) {
|
|
|
|
+ final String key
|
|
|
|
+ = rollResult.isGraceful() ? GRACEFUL_ROLL_ACTIVATED_LOCALE_STR_KEY : ROLL_ACTIVATED_LOCALE_KEY;
|
|
|
|
+ sendPlayerInformation(mmoPlayer.getPlayer(), NotificationType.SUBSKILL_MESSAGE, key);
|
|
|
|
+ sendCategorizedSound(mmoPlayer.getPlayer(), mmoPlayer.getPlayer().getLocation(),
|
|
|
|
+ SoundType.ROLL_ACTIVATED, SoundCategory.PLAYERS,0.5F);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (!rollResult.isExploiting() && rollResult.getXpGain() > 0) {
|
|
|
|
+ applyXpGain(mmoPlayer, getPrimarySkill(), rollResult.getXpGain(), XPGainReason.PVE);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // Player didn't die, so add the location to the list
|
|
|
|
+ addFallLocation(mmoPlayer);
|
|
|
|
+ return true;
|
|
|
|
+ // We give Acrobatics XP for fall damage even if they haven't unlocked roll
|
|
} else if (mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(mmoPlayer.getPlayer(), PrimarySkillType.ACROBATICS)) {
|
|
} else if (mcMMO.p.getSkillTools().doesPlayerHaveSkillPermission(mmoPlayer.getPlayer(), PrimarySkillType.ACROBATICS)) {
|
|
- //Give XP Anyways
|
|
|
|
- SkillUtils.applyXpGain(mmoPlayer, getPrimarySkill(), calculateRollXP(mmoPlayer, ((EntityDamageEvent) event).getFinalDamage(), false), XPGainReason.PVE);
|
|
|
|
|
|
+ //Give XP
|
|
|
|
+ applyXpGain(mmoPlayer, getPrimarySkill(), calculateRollXP(mmoPlayer, ((EntityDamageEvent) event).getFinalDamage(), false), XPGainReason.PVE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
@@ -167,7 +184,7 @@ public class Roll extends AcrobaticsSubSkill {
|
|
|
|
|
|
@NotNull
|
|
@NotNull
|
|
private Probability getRollProbability(McMMOPlayer mmoPlayer) {
|
|
private Probability getRollProbability(McMMOPlayer mmoPlayer) {
|
|
- return ProbabilityUtil.getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer);
|
|
|
|
|
|
+ return getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
@Override
|
|
@@ -185,54 +202,67 @@ public class Roll extends AcrobaticsSubSkill {
|
|
return true;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
|
|
- private boolean canRoll(McMMOPlayer mmoPlayer) {
|
|
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ public boolean canRoll(McMMOPlayer mmoPlayer) {
|
|
return RankUtils.hasUnlockedSubskill(mmoPlayer.getPlayer(), SubSkillType.ACROBATICS_ROLL)
|
|
return RankUtils.hasUnlockedSubskill(mmoPlayer.getPlayer(), SubSkillType.ACROBATICS_ROLL)
|
|
&& Permissions.isSubSkillEnabled(mmoPlayer.getPlayer(), SubSkillType.ACROBATICS_ROLL);
|
|
&& Permissions.isSubSkillEnabled(mmoPlayer.getPlayer(), SubSkillType.ACROBATICS_ROLL);
|
|
}
|
|
}
|
|
|
|
|
|
- private int getActivationChance(McMMOPlayer mmoPlayer) {
|
|
|
|
- return PerksUtils.handleLuckyPerks(mmoPlayer, getPrimarySkill());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
/**
|
|
/**
|
|
* Handle the damage reduction and XP gain from the Roll / Graceful Roll ability
|
|
* Handle the damage reduction and XP gain from the Roll / Graceful Roll ability
|
|
*
|
|
*
|
|
- * @param damage The amount of damage initially dealt by the event
|
|
|
|
|
|
+ * @param entityDamageEvent the event to modify in the event of roll success
|
|
* @return the modified event damage if the ability was successful, the original event damage otherwise
|
|
* @return the modified event damage if the ability was successful, the original event damage otherwise
|
|
*/
|
|
*/
|
|
- private double rollCheck(McMMOPlayer mmoPlayer, double damage, boolean isGracefulRoll) {
|
|
|
|
|
|
+ @VisibleForTesting
|
|
|
|
+ public RollResult rollCheck(McMMOPlayer mmoPlayer, EntityDamageEvent entityDamageEvent) {
|
|
|
|
+ double baseDamage = entityDamageEvent.getDamage();
|
|
|
|
+ final boolean isGraceful = mmoPlayer.getPlayer().isSneaking();
|
|
|
|
+ final RollResult.Builder rollResultBuilder
|
|
|
|
+ = new RollResult.Builder(entityDamageEvent, isGraceful);
|
|
final Probability probability
|
|
final Probability probability
|
|
- = isGracefulRoll ? getGracefulProbability(mmoPlayer) : getNonGracefulProbability(mmoPlayer);
|
|
|
|
- double modifiedDamage = calculateModifiedRollDamage(damage,
|
|
|
|
|
|
+ = isGraceful ? getGracefulProbability(mmoPlayer) : getNonGracefulProbability(mmoPlayer);
|
|
|
|
+
|
|
|
|
+ double modifiedDamage = calculateModifiedRollDamage(baseDamage,
|
|
mcMMO.p.getAdvancedConfig().getRollDamageThreshold() * 2);
|
|
mcMMO.p.getAdvancedConfig().getRollDamageThreshold() * 2);
|
|
|
|
+ rollResultBuilder.modifiedDamage(modifiedDamage);
|
|
|
|
|
|
|
|
+ boolean isExploiting = isPlayerExploitingAcrobatics(mmoPlayer);
|
|
|
|
+ rollResultBuilder.exploiting(isExploiting);
|
|
|
|
+ // They Rolled
|
|
if (!isFatal(mmoPlayer, modifiedDamage)
|
|
if (!isFatal(mmoPlayer, modifiedDamage)
|
|
&& ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.ACROBATICS, mmoPlayer, probability)) {
|
|
&& ProbabilityUtil.isStaticSkillRNGSuccessful(PrimarySkillType.ACROBATICS, mmoPlayer, probability)) {
|
|
- NotificationManager.sendPlayerInformation(mmoPlayer.getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Acrobatics.Ability.Proc");
|
|
|
|
- SoundManager.sendCategorizedSound(mmoPlayer.getPlayer(), mmoPlayer.getPlayer().getLocation(), SoundType.ROLL_ACTIVATED, SoundCategory.PLAYERS,0.5F);
|
|
|
|
- if (!isExploiting(mmoPlayer) && mmoPlayer.getAcrobaticsManager().canGainRollXP())
|
|
|
|
- SkillUtils.applyXpGain(mmoPlayer, getPrimarySkill(), calculateRollXP(mmoPlayer, damage, true), XPGainReason.PVE);
|
|
|
|
-
|
|
|
|
- addFallLocation(mmoPlayer);
|
|
|
|
- return modifiedDamage;
|
|
|
|
- } else if (!isFatal(mmoPlayer, damage)) {
|
|
|
|
- if (!isExploiting(mmoPlayer) && mmoPlayer.getAcrobaticsManager().canGainRollXP())
|
|
|
|
- SkillUtils.applyXpGain(mmoPlayer, getPrimarySkill(), calculateRollXP(mmoPlayer, damage, false), XPGainReason.PVE);
|
|
|
|
-
|
|
|
|
- addFallLocation(mmoPlayer);
|
|
|
|
- }
|
|
|
|
|
|
+ rollResultBuilder.rollSuccess(true);
|
|
|
|
+ rollResultBuilder.exploiting(isExploiting);
|
|
|
|
+ final boolean canGainXp = mmoPlayer.getAcrobaticsManager().canGainRollXP();
|
|
|
|
|
|
- return damage;
|
|
|
|
|
|
+ if (!isExploiting && canGainXp) {
|
|
|
|
+ rollResultBuilder.xpGain((int) calculateRollXP(mmoPlayer, baseDamage, true));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return rollResultBuilder.build();
|
|
|
|
+ // They did not roll, but they also did not die so reward XP as appropriate
|
|
|
|
+ } else if (!isFatal(mmoPlayer, baseDamage)) {
|
|
|
|
+ rollResultBuilder.rollSuccess(false);
|
|
|
|
+ final boolean canGainXp = mmoPlayer.getAcrobaticsManager().canGainRollXP();
|
|
|
|
+ if (!isExploiting && canGainXp) {
|
|
|
|
+ rollResultBuilder.xpGain((int) calculateRollXP(mmoPlayer, baseDamage, false));
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return rollResultBuilder.build();
|
|
|
|
+ }
|
|
|
|
+ // Fall was fatal return null
|
|
|
|
+ return null;
|
|
}
|
|
}
|
|
|
|
|
|
@NotNull
|
|
@NotNull
|
|
public static Probability getGracefulProbability(McMMOPlayer mmoPlayer) {
|
|
public static Probability getGracefulProbability(McMMOPlayer mmoPlayer) {
|
|
- double gracefulOdds = ProbabilityUtil.getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer).getValue() * 2;
|
|
|
|
|
|
+ double gracefulOdds = getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer).getValue() * 2;
|
|
return Probability.ofValue(gracefulOdds);
|
|
return Probability.ofValue(gracefulOdds);
|
|
}
|
|
}
|
|
|
|
|
|
public static Probability getNonGracefulProbability(McMMOPlayer mmoPlayer) {
|
|
public static Probability getNonGracefulProbability(McMMOPlayer mmoPlayer) {
|
|
- double gracefulOdds = ProbabilityUtil.getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer).getValue();
|
|
|
|
|
|
+ double gracefulOdds = getSubSkillProbability(SubSkillType.ACROBATICS_ROLL, mmoPlayer).getValue();
|
|
return Probability.ofValue(gracefulOdds);
|
|
return Probability.ofValue(gracefulOdds);
|
|
}
|
|
}
|
|
|
|
|
|
@@ -242,7 +272,7 @@ public class Roll extends AcrobaticsSubSkill {
|
|
*
|
|
*
|
|
* @return true if exploits are detected, false otherwise
|
|
* @return true if exploits are detected, false otherwise
|
|
*/
|
|
*/
|
|
- private boolean isExploiting(McMMOPlayer mmoPlayer) {
|
|
|
|
|
|
+ private boolean isPlayerExploitingAcrobatics(McMMOPlayer mmoPlayer) {
|
|
if (!ExperienceConfig.getInstance().isAcrobaticsExploitingPrevented()) {
|
|
if (!ExperienceConfig.getInstance().isAcrobaticsExploitingPrevented()) {
|
|
return false;
|
|
return false;
|
|
}
|
|
}
|
|
@@ -331,11 +361,9 @@ public class Roll extends AcrobaticsSubSkill {
|
|
*/
|
|
*/
|
|
@Override
|
|
@Override
|
|
public Double[] getStats(McMMOPlayer mmoPlayer) {
|
|
public Double[] getStats(McMMOPlayer mmoPlayer) {
|
|
- double playerChanceRoll = ProbabilityUtil.getSubSkillProbability(subSkillType, mmoPlayer).getValue();
|
|
|
|
|
|
+ double playerChanceRoll = getSubSkillProbability(subSkillType, mmoPlayer).getValue();
|
|
double playerChanceGrace = playerChanceRoll * 2;
|
|
double playerChanceGrace = playerChanceRoll * 2;
|
|
|
|
|
|
- double gracefulOdds = ProbabilityUtil.getSubSkillProbability(subSkillType, mmoPlayer).getValue() * 2;
|
|
|
|
-
|
|
|
|
return new Double[]{ playerChanceRoll, playerChanceGrace };
|
|
return new Double[]{ playerChanceRoll, playerChanceGrace };
|
|
}
|
|
}
|
|
|
|
|