nossr50 пре 4 година
родитељ
комит
48bf79055a

+ 15 - 0
Changelog.txt

@@ -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

+ 8 - 2
src/main/java/com/gmail/nossr50/api/AbilityAPI.java

@@ -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;
     }
 }

+ 25 - 0
src/main/java/com/gmail/nossr50/datatypes/meta/RuptureTaskMeta.java

@@ -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;
+    }
+}

+ 10 - 2
src/main/java/com/gmail/nossr50/datatypes/player/McMMOPlayer.java

@@ -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) {

+ 1 - 4
src/main/java/com/gmail/nossr50/mcMMO.java

@@ -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;
 

+ 214 - 214
src/main/java/com/gmail/nossr50/runnables/skills/BleedTimerTask.java

@@ -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);
+//    }
+//}

+ 142 - 0
src/main/java/com/gmail/nossr50/runnables/skills/RuptureTask.java

@@ -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 +
+                '}';
+    }
+}

+ 0 - 1
src/main/java/com/gmail/nossr50/skills/herbalism/HerbalismManager.java

@@ -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;

+ 40 - 25
src/main/java/com/gmail/nossr50/skills/swords/SwordsManager.java

@@ -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,32 +63,50 @@ public class SwordsManager extends SkillManager {
      *
      * @param target The defending entity
      */
-    public void ruptureCheck(@NotNull LivingEntity target) throws IllegalStateException {
-        if(BleedTimerTask.isBleedOperationAllowed()) {
-            if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_RUPTURE, getPlayer())) {
+    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());
+            }
 
-                if (target instanceof Player) {
-                    Player defender = (Player) target;
+            return; //Don't apply bleed
+        }
 
-                    //Don't start or add to a bleed if they are blocking
-                    if(defender.isBlocking())
-                        return;
+        if (RandomChanceUtil.isActivationSuccessful(SkillActivationType.RANDOM_LINEAR_100_SCALE_WITH_CAP, SubSkillType.SWORDS_RUPTURE, getPlayer())) {
 
-                    if (NotificationManager.doesPlayerUseNotifications(defender)) {
-                        if(!BleedTimerTask.isBleeding(defender))
-                            NotificationManager.sendPlayerInformation(defender, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Started");
-                    }
-                }
+            if (target instanceof Player) {
+                Player defender = (Player) target;
 
-                BleedTimerTask.add(target, getPlayer(), getRuptureBleedTicks(), RankUtils.getRank(getPlayer(), SubSkillType.SWORDS_RUPTURE), getToolTier(getPlayer().getInventory().getItemInMainHand()));
+                //Don't start or add to a bleed if they are blocking
+                if(defender.isBlocking())
+                    return;
 
-                if (mmoPlayer.useChatNotifications()) {
-                    NotificationManager.sendPlayerInformation(getPlayer(), NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding");
+                if (NotificationManager.doesPlayerUseNotifications(defender)) {
+                    NotificationManager.sendPlayerInformation(defender, NotificationType.SUBSKILL_MESSAGE, "Swords.Combat.Bleeding.Started");
                 }
             }
+
+            double tickDamageValue = target instanceof Player ? AdvancedConfig.getInstance().getRuptureDamagePlayer() : AdvancedConfig.getInstance().getRuptureDamageMobs();
+
+            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()
     {
         int rank = RankUtils.getRank(getPlayer(), SubSkillType.SWORDS_STAB);
@@ -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;
     }
 
     /**

+ 6 - 15
src/main/java/com/gmail/nossr50/skills/taming/TamingManager.java

@@ -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;
-        }
+        damage = (damage * Taming.goreModifier) - damage;
 
         return damage;
     }

+ 5 - 0
src/main/java/com/gmail/nossr50/util/TransientMetadataTools.java

@@ -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);
     }

+ 3 - 3
src/main/java/com/gmail/nossr50/util/skills/CombatUtils.java

@@ -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:

+ 38 - 5
src/main/java/com/gmail/nossr50/util/skills/ParticleEffectUtils.java

@@ -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,9 +29,40 @@ 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()) {
             return;

+ 0 - 6
src/main/resources/advanced.yml

@@ -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