Rupture has been reworked

This commit is contained in:
nossr50 2021-04-02 11:00:11 -07:00
parent 0cb3d91f0e
commit 48bf79055a
14 changed files with 507 additions and 277 deletions

View File

@ -1,4 +1,19 @@
Version 2.1.186
Rupture has been reworked to solve a few outstanding issues (see notes)
Removed 'Skills.Swords.Rupture.MaxTicks' from advanced.yml
Removed 'Skills.Swords.Rupture.BaseTicks' from advanced.yml
Gore no longer applies Rupture
Gore no longer sends a message to the Wolf owner when it triggers
Gore no longer sends a message to players that are hit by it
Rupture no longer sends a message telling you that your target is bleeding
NOTES:
The old Rupture would constantly interfere with your ability to do a Sweep Attack/Swipe with swords, the new one solves this problem
Targets will bleed and take "pure" damage while bleeding, this never kills the target. It will reduce them to 0.01 HP.
After 5 seconds since your last attack on the target have transpired Rupture "explodes" dealing a large amount of damage, this damage is not pure and is affected by armor etc.
Rupture no longer tells you that you that you applied it to the target, it should be obvious from the sounds/particle effects
The new Rupture no longer constantly interferes with the vanilla Swipe (the AOE attack built into Minecraft)
The new Rupture has not had a fine tuned balance pass, I will be balancing it frequently after this patch, it may be too weak or too strong in its current form
Version 2.1.185

View File

@ -2,7 +2,7 @@ package com.gmail.nossr50.api;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.player.UserManager;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
@ -83,6 +83,12 @@ public final class AbilityAPI {
}
public static boolean isBleeding(LivingEntity entity) {
return BleedTimerTask.isBleeding(entity);
if(entity.isValid()) {
if(entity.hasMetadata(mcMMO.RUPTURE_META_KEY)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,25 @@
package com.gmail.nossr50.datatypes.meta;
import com.gmail.nossr50.runnables.skills.RuptureTask;
import org.bukkit.metadata.FixedMetadataValue;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
public class RuptureTaskMeta extends FixedMetadataValue {
private final @NotNull RuptureTask ruptureTask;
/**
* Initializes a FixedMetadataValue with an Object
*
* @param owningPlugin the {@link Plugin} that created this metadata value
* @param ruptureTask the value assigned to this metadata value
*/
public RuptureTaskMeta(@NotNull Plugin owningPlugin, @NotNull RuptureTask ruptureTask) {
super(owningPlugin, ruptureTask);
this.ruptureTask = ruptureTask;
}
public @NotNull RuptureTask getRuptureTimerTask() {
return ruptureTask;
}
}

View File

@ -10,6 +10,7 @@ import com.gmail.nossr50.datatypes.chat.ChatChannel;
import com.gmail.nossr50.datatypes.experience.XPGainReason;
import com.gmail.nossr50.datatypes.experience.XPGainSource;
import com.gmail.nossr50.datatypes.interactions.NotificationType;
import com.gmail.nossr50.datatypes.meta.RuptureTaskMeta;
import com.gmail.nossr50.datatypes.mods.CustomTool;
import com.gmail.nossr50.datatypes.party.Party;
import com.gmail.nossr50.datatypes.party.PartyTeleportRecord;
@ -23,7 +24,6 @@ import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.party.PartyManager;
import com.gmail.nossr50.party.ShareHandler;
import com.gmail.nossr50.runnables.skills.AbilityDisableTask;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.runnables.skills.ToolLowerTask;
import com.gmail.nossr50.skills.SkillManager;
import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
@ -1080,7 +1080,15 @@ public class McMMOPlayer implements Identified {
*/
public void logout(boolean syncSave) {
Player thisPlayer = getPlayer();
BleedTimerTask.bleedOut(getPlayer());
if(getPlayer().hasMetadata(mcMMO.RUPTURE_META_KEY)) {
RuptureTaskMeta ruptureTaskMeta = (RuptureTaskMeta) getPlayer().getMetadata(mcMMO.RUPTURE_META_KEY);
//Punish a logout
ruptureTaskMeta.getRuptureTimerTask().explode();
ruptureTaskMeta.getRuptureTimerTask().explode();
ruptureTaskMeta.getRuptureTimerTask().explode();
}
cleanup();
if (syncSave) {

View File

@ -27,7 +27,6 @@ import com.gmail.nossr50.runnables.party.PartyAutoKickTask;
import com.gmail.nossr50.runnables.player.ClearRegisteredXPGainTask;
import com.gmail.nossr50.runnables.player.PlayerProfileLoadingTask;
import com.gmail.nossr50.runnables.player.PowerLevelUpdatingTask;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.skills.alchemy.Alchemy;
import com.gmail.nossr50.skills.child.ChildConfig;
import com.gmail.nossr50.skills.repair.repairables.Repairable;
@ -126,6 +125,7 @@ public class mcMMO extends JavaPlugin {
/* Metadata Values */
public static final String REPLANT_META_KEY = "mcMMO: Recently Replanted";
public static final String RUPTURE_META_KEY = "mcMMO: RuptureTask";
public static final String FISH_HOOK_REF_METAKEY = "mcMMO: Fish Hook Tracker";
public static final String DODGE_TRACKER = "mcMMO: Dodge Tracker";
public static final String CUSTOM_DAMAGE_METAKEY = "mcMMO: Custom Damage";
@ -641,9 +641,6 @@ public class mcMMO extends JavaPlugin {
// Cleanup the backups folder
new CleanBackupsTask().runTaskAsynchronously(mcMMO.p);
// Bleed timer (Runs every 0.5 seconds)
new BleedTimerTask().runTaskTimer(this, Misc.TICK_CONVERSION_FACTOR, (Misc.TICK_CONVERSION_FACTOR / 2));
// Old & Powerless User remover
long purgeIntervalTicks = Config.getInstance().getPurgeInterval() * 60L * 60L * Misc.TICK_CONVERSION_FACTOR;

View File

@ -1,214 +1,214 @@
package com.gmail.nossr50.runnables.skills;
import com.gmail.nossr50.config.AdvancedConfig;
import com.gmail.nossr50.datatypes.interactions.NotificationType;
import com.gmail.nossr50.events.fake.FakeEntityDamageByEntityEvent;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MobHealthbarUtils;
import com.gmail.nossr50.util.player.NotificationManager;
import com.gmail.nossr50.util.skills.CombatUtils;
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
import com.gmail.nossr50.util.sounds.SoundManager;
import com.gmail.nossr50.util.sounds.SoundType;
import org.bukkit.Bukkit;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
public class BleedTimerTask extends BukkitRunnable {
private static final @NotNull Map<LivingEntity, BleedContainer> bleedList = new HashMap<>();
private static boolean isIterating = false;
@Override
public void run() {
isIterating = true;
Iterator<Entry<LivingEntity, BleedContainer>> bleedIterator = bleedList.entrySet().iterator();
while (bleedIterator.hasNext()) {
Entry<LivingEntity, BleedContainer> containerEntry = bleedIterator.next();
LivingEntity target = containerEntry.getKey();
int toolTier = containerEntry.getValue().toolTier;
// String debugMessage = "";
// debugMessage += ChatColor.GOLD + "Target ["+target.getName()+"]: " + ChatColor.RESET;
// debugMessage+="RemainingTicks=["+containerEntry.getValue().bleedTicks+"], ";
if (containerEntry.getValue().bleedTicks <= 0 || !target.isValid()) {
if(target instanceof Player)
{
NotificationManager.sendPlayerInformation((Player) target, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Stopped");
}
bleedIterator.remove();
continue;
}
int armorCount = 0;
double damage;
if (target instanceof Player) {
damage = AdvancedConfig.getInstance().getRuptureDamagePlayer();
//Above Bleed Rank 3 deals 50% more damage
if (containerEntry.getValue().toolTier >= 4 && containerEntry.getValue().bleedRank >= 3)
damage = damage * 1.5;
Player player = (Player) target;
if (!player.isOnline()) {
continue;
}
//Count Armor
for (ItemStack armorPiece : ((Player) target).getInventory().getArmorContents()) {
//We only want to count slots that contain armor.
if (armorPiece != null) {
armorCount++;
}
}
} else {
damage = AdvancedConfig.getInstance().getRuptureDamageMobs();
// debugMessage+="BaseDMG=["+damage+"], ";
//Above Bleed Rank 3 deals 50% more damage
if (containerEntry.getValue().bleedRank >= 3)
{
damage = damage * 1.5;
}
// debugMessage+="Rank4Bonus=["+String.valueOf(containerEntry.getValue().bleedRank >= 3)+"], ";
MobHealthbarUtils.handleMobHealthbars(target, damage, mcMMO.p); //Update health bars
}
// debugMessage+="FullArmor=["+String.valueOf(armorCount > 3)+"], ";
if(armorCount > 3)
{
damage = damage * .75;
}
// debugMessage+="AfterRankAndArmorChecks["+damage+"], ";
//Weapons below Diamond get damage cut in half
if(toolTier < 4)
damage = damage / 2;
// debugMessage+="AfterDiamondCheck=["+String.valueOf(damage)+"], ";
//Wood weapons get damage cut in half again
if(toolTier < 2)
damage = damage / 2;
// debugMessage+="AfterWoodenCheck=["+String.valueOf(damage)+"], ";
double victimHealth = target.getHealth();
// debugMessage+="TargetHealthBeforeDMG=["+String.valueOf(target.getHealth())+"], ";
//Fire a fake event
FakeEntityDamageByEntityEvent fakeEntityDamageByEntityEvent = (FakeEntityDamageByEntityEvent) CombatUtils.sendEntityDamageEvent(containerEntry.getValue().damageSource, target, EntityDamageEvent.DamageCause.CUSTOM, damage);
Bukkit.getPluginManager().callEvent(fakeEntityDamageByEntityEvent);
CombatUtils.dealNoInvulnerabilityTickDamageRupture(target, damage, containerEntry.getValue().damageSource, toolTier);
double victimHealthAftermath = target.getHealth();
// debugMessage+="TargetHealthAfterDMG=["+String.valueOf(target.getHealth())+"], ";
if(victimHealthAftermath <= 0 || victimHealth != victimHealthAftermath)
{
//Play Bleed Sound
SoundManager.worldSendSound(target.getWorld(), target.getLocation(), SoundType.BLEED);
ParticleEffectUtils.playBleedEffect(target);
}
//Lower Bleed Ticks
BleedContainer loweredBleedContainer = copyContainer(containerEntry.getValue());
loweredBleedContainer.bleedTicks -= 1;
// debugMessage+="RemainingTicks=["+loweredBleedContainer.bleedTicks+"]";
containerEntry.setValue(loweredBleedContainer);
// Bukkit.broadcastMessage(debugMessage);
}
isIterating = false;
}
public static @NotNull BleedContainer copyContainer(@NotNull BleedContainer container)
{
LivingEntity target = container.target;
LivingEntity source = container.damageSource;
int bleedTicks = container.bleedTicks;
int bleedRank = container.bleedRank;
int toolTier = container.toolTier;
return new BleedContainer(target, bleedTicks, bleedRank, toolTier, source);
}
/**
* Instantly Bleed out a LivingEntity
*
* @param entity LivingEntity to bleed out
*/
public static void bleedOut(@NotNull LivingEntity entity) {
/*
* Don't remove anything from the list outside of run()
*/
if (bleedList.containsKey(entity)) {
CombatUtils.dealNoInvulnerabilityTickDamage(entity, bleedList.get(entity).bleedTicks * 2, bleedList.get(entity).damageSource);
}
}
/**
* Add a LivingEntity to the bleedList if it is not in it.
*
* @param entity LivingEntity to add
* @param attacker source of the bleed/rupture
* @param ticks Number of bleeding ticks
*/
public static void add(@NotNull LivingEntity entity, @NotNull LivingEntity attacker, int ticks, int bleedRank, int toolTier) {
if (!Bukkit.isPrimaryThread()) {
throw new IllegalStateException("Cannot add bleed task async!");
}
if(isIterating) {
//Used to throw an error here, but in reality all we are really doing is preventing concurrency issues from other plugins being naughty and its not really needed
//I'm not really a fan of silent errors, but I'm sick of seeing people using crazy enchantments come in and report this "bug"
return;
}
// if (isIterating) throw new IllegalStateException("Cannot add task while iterating timers!");
if(toolTier < 4)
ticks = Math.max(1, (ticks / 3));
ticks+=1;
BleedContainer newBleedContainer = new BleedContainer(entity, ticks, bleedRank, toolTier, attacker);
bleedList.put(entity, newBleedContainer);
}
public static boolean isBleedOperationAllowed() {
return !isIterating && Bukkit.isPrimaryThread();
}
public static boolean isBleeding(@NotNull LivingEntity entity) {
return bleedList.containsKey(entity);
}
}
//package com.gmail.nossr50.runnables.skills;
//
//import com.gmail.nossr50.config.AdvancedConfig;
//import com.gmail.nossr50.datatypes.interactions.NotificationType;
//import com.gmail.nossr50.events.fake.FakeEntityDamageByEntityEvent;
//import com.gmail.nossr50.mcMMO;
//import com.gmail.nossr50.util.MobHealthbarUtils;
//import com.gmail.nossr50.util.player.NotificationManager;
//import com.gmail.nossr50.util.skills.CombatUtils;
//import com.gmail.nossr50.util.skills.ParticleEffectUtils;
//import com.gmail.nossr50.util.sounds.SoundManager;
//import com.gmail.nossr50.util.sounds.SoundType;
//import org.bukkit.Bukkit;
//import org.bukkit.entity.LivingEntity;
//import org.bukkit.entity.Player;
//import org.bukkit.event.entity.EntityDamageEvent;
//import org.bukkit.inventory.ItemStack;
//import org.bukkit.scheduler.BukkitRunnable;
//import org.jetbrains.annotations.NotNull;
//
//import java.util.HashMap;
//import java.util.Iterator;
//import java.util.Map;
//import java.util.Map.Entry;
//
//public class BleedTimerTask extends BukkitRunnable {
// private static final @NotNull Map<LivingEntity, BleedContainer> bleedList = new HashMap<>();
// private static boolean isIterating = false;
//
// @Override
// public void run() {
// isIterating = true;
// Iterator<Entry<LivingEntity, BleedContainer>> bleedIterator = bleedList.entrySet().iterator();
//
// while (bleedIterator.hasNext()) {
// Entry<LivingEntity, BleedContainer> containerEntry = bleedIterator.next();
// LivingEntity target = containerEntry.getKey();
// int toolTier = containerEntry.getValue().toolTier;
//
//// String debugMessage = "";
//// debugMessage += ChatColor.GOLD + "Target ["+target.getName()+"]: " + ChatColor.RESET;
//
//// debugMessage+="RemainingTicks=["+containerEntry.getValue().bleedTicks+"], ";
//
// if (containerEntry.getValue().bleedTicks <= 0 || !target.isValid()) {
// if(target instanceof Player)
// {
// NotificationManager.sendPlayerInformation((Player) target, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Stopped");
// }
//
// bleedIterator.remove();
// continue;
// }
//
// int armorCount = 0;
//
// double damage;
//
// if (target instanceof Player) {
// damage = AdvancedConfig.getInstance().getRuptureDamagePlayer();
//
// //Above Bleed Rank 3 deals 50% more damage
// if (containerEntry.getValue().toolTier >= 4 && containerEntry.getValue().bleedRank >= 3)
// damage = damage * 1.5;
//
// Player player = (Player) target;
//
// if (!player.isOnline()) {
// continue;
// }
//
// //Count Armor
// for (ItemStack armorPiece : ((Player) target).getInventory().getArmorContents()) {
// //We only want to count slots that contain armor.
// if (armorPiece != null) {
// armorCount++;
// }
// }
//
// } else {
// damage = AdvancedConfig.getInstance().getRuptureDamageMobs();
//
//// debugMessage+="BaseDMG=["+damage+"], ";
//
// //Above Bleed Rank 3 deals 50% more damage
// if (containerEntry.getValue().bleedRank >= 3)
// {
// damage = damage * 1.5;
// }
//
//// debugMessage+="Rank4Bonus=["+String.valueOf(containerEntry.getValue().bleedRank >= 3)+"], ";
//
//
// MobHealthbarUtils.handleMobHealthbars(target, damage, mcMMO.p); //Update health bars
// }
//
//// debugMessage+="FullArmor=["+String.valueOf(armorCount > 3)+"], ";
//
// if(armorCount > 3)
// {
// damage = damage * .75;
// }
//
//// debugMessage+="AfterRankAndArmorChecks["+damage+"], ";
//
// //Weapons below Diamond get damage cut in half
// if(toolTier < 4)
// damage = damage / 2;
//
//// debugMessage+="AfterDiamondCheck=["+String.valueOf(damage)+"], ";
//
// //Wood weapons get damage cut in half again
// if(toolTier < 2)
// damage = damage / 2;
//
//// debugMessage+="AfterWoodenCheck=["+String.valueOf(damage)+"], ";
//
// double victimHealth = target.getHealth();
//
//// debugMessage+="TargetHealthBeforeDMG=["+String.valueOf(target.getHealth())+"], ";
//
// //Fire a fake event
// FakeEntityDamageByEntityEvent fakeEntityDamageByEntityEvent = (FakeEntityDamageByEntityEvent) CombatUtils.sendEntityDamageEvent(containerEntry.getValue().damageSource, target, EntityDamageEvent.DamageCause.CUSTOM, damage);
// Bukkit.getPluginManager().callEvent(fakeEntityDamageByEntityEvent);
//
// CombatUtils.dealNoInvulnerabilityTickDamageRupture(target, damage, containerEntry.getValue().damageSource, toolTier);
//
// double victimHealthAftermath = target.getHealth();
//
//// debugMessage+="TargetHealthAfterDMG=["+String.valueOf(target.getHealth())+"], ";
//
// if(victimHealthAftermath <= 0 || victimHealth != victimHealthAftermath)
// {
// //Play Bleed Sound
// SoundManager.worldSendSound(target.getWorld(), target.getLocation(), SoundType.BLEED);
//
// ParticleEffectUtils.playBleedEffect(target);
// }
//
// //Lower Bleed Ticks
// BleedContainer loweredBleedContainer = copyContainer(containerEntry.getValue());
// loweredBleedContainer.bleedTicks -= 1;
//
//// debugMessage+="RemainingTicks=["+loweredBleedContainer.bleedTicks+"]";
// containerEntry.setValue(loweredBleedContainer);
//
//// Bukkit.broadcastMessage(debugMessage);
// }
// isIterating = false;
// }
//
// public static @NotNull BleedContainer copyContainer(@NotNull BleedContainer container)
// {
// LivingEntity target = container.target;
// LivingEntity source = container.damageSource;
// int bleedTicks = container.bleedTicks;
// int bleedRank = container.bleedRank;
// int toolTier = container.toolTier;
//
// return new BleedContainer(target, bleedTicks, bleedRank, toolTier, source);
// }
//
// /**
// * Instantly Bleed out a LivingEntity
// *
// * @param entity LivingEntity to bleed out
// */
// public static void bleedOut(@NotNull LivingEntity entity) {
// /*
// * Don't remove anything from the list outside of run()
// */
//
// if (bleedList.containsKey(entity)) {
// CombatUtils.dealNoInvulnerabilityTickDamage(entity, bleedList.get(entity).bleedTicks * 2, bleedList.get(entity).damageSource);
// }
// }
//
// /**
// * Add a LivingEntity to the bleedList if it is not in it.
// *
// * @param entity LivingEntity to add
// * @param attacker source of the bleed/rupture
// * @param ticks Number of bleeding ticks
// */
// public static void add(@NotNull LivingEntity entity, @NotNull LivingEntity attacker, int ticks, int bleedRank, int toolTier) {
// if (!Bukkit.isPrimaryThread()) {
// throw new IllegalStateException("Cannot add bleed task async!");
// }
//
// if(isIterating) {
// //Used to throw an error here, but in reality all we are really doing is preventing concurrency issues from other plugins being naughty and its not really needed
// //I'm not really a fan of silent errors, but I'm sick of seeing people using crazy enchantments come in and report this "bug"
// return;
// }
//
//// if (isIterating) throw new IllegalStateException("Cannot add task while iterating timers!");
//
// if(toolTier < 4)
// ticks = Math.max(1, (ticks / 3));
//
// ticks+=1;
//
// BleedContainer newBleedContainer = new BleedContainer(entity, ticks, bleedRank, toolTier, attacker);
// bleedList.put(entity, newBleedContainer);
// }
//
// public static boolean isBleedOperationAllowed() {
// return !isIterating && Bukkit.isPrimaryThread();
// }
//
// public static boolean isBleeding(@NotNull LivingEntity entity) {
// return bleedList.containsKey(entity);
// }
//}

View File

@ -0,0 +1,142 @@
package com.gmail.nossr50.runnables.skills;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.MobHealthbarUtils;
import com.gmail.nossr50.util.skills.ParticleEffectUtils;
import com.google.common.base.Objects;
import org.bukkit.entity.LivingEntity;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
public class RuptureTask extends BukkitRunnable {
public static final int FIVE_SECOND_DURATION = 20 * 5;
public static final int DAMAGE_TICK_INTERVAL = 10;
private final @NotNull McMMOPlayer ruptureSource;
private final @NotNull LivingEntity targetEntity;
private final int ruptureRank;
private final int expireTick;
private int ruptureTick;
private int damageTickTracker;
private final double damageValue; //TODO: Make configurable
public RuptureTask(@NotNull McMMOPlayer ruptureSource, @NotNull LivingEntity targetEntity, int ruptureRank, double damageValue) {
this.ruptureSource = ruptureSource;
this.targetEntity = targetEntity;
this.ruptureRank = ruptureRank;
this.expireTick = FIVE_SECOND_DURATION;
this.damageValue = damageValue;
this.ruptureTick = 0;
this.damageTickTracker = 0;
}
@Override
public void run() {
//Check validity
if(targetEntity.isValid()) {
ruptureTick += 1; //Advance rupture tick by 1.
damageTickTracker += 1; //Increment damage tick tracker
//Rupture hasn't ended yet
if(ruptureTick < expireTick) {
//Is it time to damage?
if(damageTickTracker >= DAMAGE_TICK_INTERVAL) {
damageTickTracker = 0; //Reset
ParticleEffectUtils.playBleedEffect(targetEntity); //Animate
if(targetEntity.getHealth() > 0.01) {
double healthBeforeRuptureIsApplied = targetEntity.getHealth();
double damagedHealth = healthBeforeRuptureIsApplied - getTickDamage();
if(damagedHealth <= 0) {
mcMMO.p.getLogger().severe("DEBUG: Miscalculating Rupture tick damage");
} else {
targetEntity.setHealth(damagedHealth); //Hurt entity without the unwanted side effects of damage()
//TODO: Do we need to set last damage? Double check
double finalDamage = healthBeforeRuptureIsApplied - targetEntity.getHealth();
//Update health bars
MobHealthbarUtils.handleMobHealthbars(targetEntity, finalDamage, mcMMO.p);
if(finalDamage <= 0) {
mcMMO.p.getLogger().severe("DEBUG: Miscalculating final damage for Rupture");
} else {
//Actually should this even be done?
targetEntity.setLastDamage(finalDamage);
}
}
}
}
} else {
explode();
}
} else {
targetEntity.removeMetadata(mcMMO.RUPTURE_META_KEY, mcMMO.p);
this.cancel(); //Task no longer needed
}
}
public void explode() {
ParticleEffectUtils.playBleedEffect(targetEntity); //Animate
if(ruptureSource.getPlayer() != null && ruptureSource.getPlayer().isValid()) {
targetEntity.damage(getExplosionDamage(), ruptureSource.getPlayer());
} else {
targetEntity.damage(getExplosionDamage(), null);
}
targetEntity.removeMetadata(mcMMO.RUPTURE_META_KEY, mcMMO.p);
this.cancel(); //Task no longer needed
}
private double getTickDamage() {
double tickDamage = damageValue;
if(targetEntity.getHealth() <= tickDamage) {
tickDamage = targetEntity.getHealth() - 0.01;
if(tickDamage <= 0) {
tickDamage = 0;
}
}
return tickDamage;
}
private int getExplosionDamage() {
//TODO: Balance pass
return ruptureRank * 10;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RuptureTask that = (RuptureTask) o;
return ruptureRank == that.ruptureRank && expireTick == that.expireTick && ruptureTick == that.ruptureTick && damageTickTracker == that.damageTickTracker && Double.compare(that.damageValue, damageValue) == 0 && Objects.equal(ruptureSource, that.ruptureSource) && Objects.equal(targetEntity, that.targetEntity);
}
@Override
public int hashCode() {
return Objects.hashCode(ruptureSource, targetEntity, ruptureRank, expireTick, ruptureTick, damageTickTracker, damageValue);
}
@Override
public String toString() {
return "RuptureTimerTask{" +
"ruptureSource=" + ruptureSource +
", targetEntity=" + targetEntity +
", ruptureRank=" + ruptureRank +
", expireTick=" + expireTick +
", ruptureTick=" + ruptureTick +
", damageTickTracker=" + damageTickTracker +
", damageValue=" + damageValue +
'}';
}
}

View File

@ -29,7 +29,6 @@ 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.text.StringUtils;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;

View File

@ -1,12 +1,15 @@
package com.gmail.nossr50.skills.swords;
import com.gmail.nossr50.config.AdvancedConfig;
import com.gmail.nossr50.datatypes.interactions.NotificationType;
import com.gmail.nossr50.datatypes.meta.RuptureTaskMeta;
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
import com.gmail.nossr50.datatypes.skills.SubSkillType;
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
import com.gmail.nossr50.datatypes.skills.ToolType;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.skills.RuptureTask;
import com.gmail.nossr50.skills.SkillManager;
import com.gmail.nossr50.util.ItemUtils;
import com.gmail.nossr50.util.Permissions;
@ -60,8 +63,18 @@ public class SwordsManager extends SkillManager {
*
* @param target The defending entity
*/
public void ruptureCheck(@NotNull LivingEntity target) throws IllegalStateException {
if(BleedTimerTask.isBleedOperationAllowed()) {
public void processRupture(@NotNull LivingEntity target) throws IllegalStateException {
if(target.hasMetadata(mcMMO.REPLANT_META_KEY)) {
if(mmoPlayer.isDebugMode()) {
mmoPlayer.getPlayer().sendMessage("Rupture task ongoing for target " + target.toString());
RuptureTaskMeta ruptureTaskMeta = (RuptureTaskMeta) target.getMetadata(mcMMO.RUPTURE_META_KEY);
RuptureTask ruptureTask = (RuptureTask) target.getMetadata(mcMMO.RUPTURE_META_KEY);
mmoPlayer.getPlayer().sendMessage(ruptureTask.toString());
}
return; //Don't apply bleed
}
if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_RUPTURE, getPlayer())) {
if (target instanceof Player) {
@ -72,18 +85,26 @@ public class SwordsManager extends SkillManager {
return;
if (NotificationManager.doesPlayerUseNotifications(defender)) {
if(!BleedTimerTask.isBleeding(defender))
NotificationManager.sendPlayerInformation(defender, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Started");
}
}
BleedTimerTask.add(target, getPlayer(), getRuptureBleedTicks(), RankUtils.getRank(getPlayer(), SubSkillType.SWORDS_RUPTURE), getToolTier(getPlayer().getInventory().getItemInMainHand()));
double tickDamageValue = target instanceof Player ? AdvancedConfig.getInstance().getRuptureDamagePlayer() : AdvancedConfig.getInstance().getRuptureDamageMobs();
if (mmoPlayer.useChatNotifications()) {
NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding");
}
RuptureTask ruptureTask = new RuptureTask(mmoPlayer, target, RankUtils.getRank(mmoPlayer.getPlayer(), SubSkillType.SWORDS_RUPTURE), tickDamageValue);
RuptureTaskMeta ruptureTaskMeta = new RuptureTaskMeta(mcMMO.p, ruptureTask);
ruptureTask.runTaskTimer(mcMMO.p, 0, 1);
target.setMetadata(mcMMO.RUPTURE_META_KEY, ruptureTaskMeta);
// if (mmoPlayer.useChatNotifications()) {
// NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding");
// }
}
}
private int getRuptureRank() {
return RankUtils.getRank(getPlayer(), SubSkillType.SWORDS_RUPTURE);
}
public double getStabDamage()
@ -112,14 +133,8 @@ public class SwordsManager extends SkillManager {
return 1;
}
public int getRuptureBleedTicks()
{
int bleedTicks = 2 * RankUtils.getRank(getPlayer(), SubSkillType.SWORDS_RUPTURE);
if(bleedTicks > Swords.bleedMaxTicks)
bleedTicks = Swords.bleedMaxTicks;
return bleedTicks;
public int getRuptureBleedTicks() {
return RuptureTask.FIVE_SECOND_DURATION / RuptureTask.DAMAGE_TICK_INTERVAL;
}
/**

View File

@ -12,7 +12,6 @@ import com.gmail.nossr50.datatypes.skills.subskills.taming.CallOfTheWildType;
import com.gmail.nossr50.datatypes.skills.subskills.taming.TamingSummon;
import com.gmail.nossr50.locale.LocaleLoader;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.runnables.skills.BleedTimerTask;
import com.gmail.nossr50.skills.SkillManager;
import com.gmail.nossr50.util.Misc;
import com.gmail.nossr50.util.Permissions;
@ -168,21 +167,13 @@ public class TamingManager extends SkillManager {
* @param damage The initial damage
*/
public double gore(@NotNull LivingEntity target, double damage) {
if(BleedTimerTask.isBleedOperationAllowed()) {
if (!RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.TAMING_GORE, getPlayer())) {
return 0;
}
BleedTimerTask.add(target, getPlayer(), Taming.goreBleedTicks, 1, 2);
if (target instanceof Player) {
NotificationManager.sendPlayerInformation((Player)target, NotificationType.SUBSKILL_MESSAGE, "Combat.StruckByGore");
}
NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Combat.Gore");
// if (target instanceof Player) {
// NotificationManager.sendPlayerInformation((Player)target, NotificationType.SUBSKILL_MESSAGE, "Combat.StruckByGore");
// }
//
// NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Combat.Gore");
damage = (damage * Taming.goreModifier) - damage;
}
return damage;
}

View File

@ -33,6 +33,11 @@ public class TransientMetadataTools {
livingEntity.removeMetadata(mcMMO.travelingBlock, pluginRef);
}
if(livingEntity.hasMetadata(mcMMO.REPLANT_META_KEY)) {
livingEntity.removeMetadata(mcMMO.REPLANT_META_KEY, pluginRef);
}
//Cleanup mob metadata
mcMMO.getCompatibilityManager().getPersistentDataLayer().removeMobFlags(livingEntity);
}

View File

@ -12,7 +12,6 @@ import com.gmail.nossr50.events.fake.FakeEntityDamageByEntityEvent;
import com.gmail.nossr50.events.fake.FakeEntityDamageEvent;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.party.PartyManager;
import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
import com.gmail.nossr50.skills.acrobatics.AcrobaticsManager;
import com.gmail.nossr50.skills.archery.ArcheryManager;
import com.gmail.nossr50.skills.axes.AxesManager;
@ -41,6 +40,7 @@ import org.bukkit.potion.PotionEffectType;
import org.bukkit.projectiles.ProjectileSource;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.gmail.nossr50.runnables.skills.AwardCombatXpTask;
import java.util.EnumMap;
import java.util.HashMap;
@ -96,7 +96,7 @@ public final class CombatUtils {
if(target.getHealth() - event.getFinalDamage() >= 1)
{
if (swordsManager.canUseRupture()) {
swordsManager.ruptureCheck(target);
swordsManager.processRupture(target);
}
}
@ -714,7 +714,7 @@ public final class CombatUtils {
NotificationManager.sendPlayerInformation((Player)entity, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.SS.Struck");
}
UserManager.getPlayer(attacker).getSwordsManager().ruptureCheck(target);
UserManager.getPlayer(attacker).getSwordsManager().processRupture(target);
break;
case AXES:

View File

@ -1,17 +1,19 @@
package com.gmail.nossr50.util.skills;
import com.gmail.nossr50.config.Config;
import com.gmail.nossr50.mcMMO;
import com.gmail.nossr50.util.sounds.SoundManager;
import com.gmail.nossr50.util.sounds.SoundType;
import org.bukkit.Effect;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.apache.commons.lang.math.RandomUtils;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.LivingEntity;
import org.bukkit.entity.Player;
import java.util.ArrayList;
import java.util.Arrays;
public final class ParticleEffectUtils {
private ParticleEffectUtils() {}
@ -27,8 +29,39 @@ public final class ParticleEffectUtils {
return;
}
livingEntity.getWorld().playEffect(livingEntity.getEyeLocation(), Effect.STEP_SOUND, Material.REDSTONE_WIRE);
Location origin = livingEntity.getEyeLocation().clone();
World world = origin.getWorld();
double x = origin.getX();
double y = origin.getY();
double z = origin.getZ();
double offSetVal = 0.3D;
Location locA = new Location(world, x - offSetVal, y, z);
Location locB = new Location(world, x + offSetVal, y, z);
Location locC = new Location(world, x, y + offSetVal, z);
Location locD = new Location(world, x, y - offSetVal, z);
Location locE = new Location(world, x, y, z + offSetVal);
Location locF = new Location(world, x, y, z - offSetVal);
Location locG = new Location(world, x + offSetVal, y, z + offSetVal);
Location locH = new Location(world, x - offSetVal, y, z - offSetVal);
Location locI = new Location(world, x - offSetVal, y - offSetVal, z - offSetVal);
Location locJ = new Location(world, x + offSetVal, y - offSetVal, z + offSetVal);
Location locK = new Location(world, x - offSetVal, y + offSetVal, z - offSetVal);
Location locL = new Location(world, x - offSetVal, y + offSetVal, z - offSetVal);
Location[] particleLocations = new Location[]{ locA, locB, locC, locD, locE, locF, locG, locH, locI, locJ, locK, locL};
for(Location location : particleLocations) {
if(RandomUtils.nextInt(100) > 30) {
//TODO: Change
livingEntity.getWorld().playEffect(location, Effect.STEP_SOUND, Material.REDSTONE_WIRE);
}
}
}
public static void playDodgeEffect(Player player) {
if (!Config.getInstance().getDodgeEffectEnabled()) {

View File

@ -465,13 +465,7 @@ Skills:
# DamageMobs: Bleeding damage dealt to mobs
DamagePlayer: 2.0
DamageMobs: 3.0
# These settings determine how long the Bleeding effect lasts
MaxTicks: 8
BaseTicks: 2
CounterAttack:
# ChanceMax: Maximum chance of triggering a counter attack
# MaxBonusLevel: On this level, the chance to Counter will be <ChanceMax>
ChanceMax: 30.0