diff --git a/TwosideKeeper.jar b/TwosideKeeper.jar index 66e95d7..c26935f 100644 Binary files a/TwosideKeeper.jar and b/TwosideKeeper.jar differ diff --git a/src/plugin.yml b/src/plugin.yml index 0c872ef..78a34fa 100644 --- a/src/plugin.yml +++ b/src/plugin.yml @@ -1,6 +1,6 @@ name: TwosideKeeper main: sig.plugin.TwosideKeeper.TwosideKeeper -version: 3.10.16 +version: 3.10.17 loadbefore: [aPlugin] commands: money: diff --git a/src/sig/plugin/TwosideKeeper/Boss/EliteZombie.java b/src/sig/plugin/TwosideKeeper/Boss/EliteZombie.java index 74bde3c..ad9a8e4 100644 --- a/src/sig/plugin/TwosideKeeper/Boss/EliteZombie.java +++ b/src/sig/plugin/TwosideKeeper/Boss/EliteZombie.java @@ -32,6 +32,7 @@ import sig.plugin.TwosideKeeper.CustomDamage; import sig.plugin.TwosideKeeper.EliteMonster; import sig.plugin.TwosideKeeper.MonsterController; import sig.plugin.TwosideKeeper.TwosideKeeper; +import sig.plugin.TwosideKeeper.HelperStructures.Channel; import sig.plugin.TwosideKeeper.HelperStructures.LivingEntityDifficulty; import sig.plugin.TwosideKeeper.HelperStructures.Loot; import sig.plugin.TwosideKeeper.HelperStructures.MonsterDifficulty; @@ -276,12 +277,12 @@ public class EliteZombie extends EliteMonster{ } } if (!storingenergy) { - if (storingenergy_hit>0) { - storingenergy_hit/=1.04f; + /*if (storingenergy_hit>0) { + //storingenergy_hit/=1.04f; if (storingenergy_hit<10) { storingenergy_hit=0; } - } + } */ if (l.getLocation().distanceSquared(m.getLocation())>8192 && !leaping && last_leap_time+200) { - storingenergy_hit=(last_storingenergy_health-m.getHealth())*500d; + storingenergy_hit=(last_storingenergy_health-m.getHealth()); for (int i=0;inull if no buff found! Use hasBuff() to verify they have + * a buff beforehand. + * + * This version of the method uses a BuffTemplate, which allows you to use an already defined setup for + * a Buff's appearance and display. + */ + public static Buff getBuff(LivingEntity l, BuffTemplate buff) { + return getBuff(l,buff.getKeyName()); + } /** * Returns null if no buff found! Use hasBuff() to verify they have @@ -149,6 +187,32 @@ public class Buff { public boolean getDisplayTimerAlways() { return displayTimer; } + + /** + * Attempts to add a buff to the target. This will not necessarily add the buff if the amplifier + * is weaker than what is currently applied, or the amplifier is the same but the duration is less. + * This follows the same rules established by all other buff mechanics added previously to the server. + * + * This version of the method uses a BuffTemplate, which allows you to use an already defined setup for + * a Buff's appearance and display. + */ + public static void addBuff(LivingEntity l, long duration, int amplifier, BuffTemplate buff, boolean stacking) { + addBuff(l,buff.getKeyName(),new Buff( + buff.getDisplayName(), + duration, + amplifier, + buff.getParticleColor(), + buff.getIcon(), + buff.isGoodBuff(), + buff.isPermanentBuff(), + buff.isDisplayTimer() + ),stacking); + } + + + public static void addBuff(LivingEntity ent, int duration, int amplifier, BuffTemplate buff) { + addBuff(ent,duration,amplifier,buff,false); + } public static void addBuff(LivingEntity l, String name, Buff buff) { addBuff(l,name,buff,false); @@ -212,6 +276,10 @@ public class Buff { } } } + public static void removeBuff(LivingEntity l, BuffTemplate buff) { + removeBuff(l,buff.getKeyName()); + } + public static void removeBuff(LivingEntity l, String name) { if (l instanceof Player) { Player p = (Player)l; diff --git a/src/sig/plugin/TwosideKeeper/ChargeZombie.java b/src/sig/plugin/TwosideKeeper/ChargeZombie.java index 197ccab..70f08a6 100644 --- a/src/sig/plugin/TwosideKeeper/ChargeZombie.java +++ b/src/sig/plugin/TwosideKeeper/ChargeZombie.java @@ -6,6 +6,7 @@ import org.bukkit.Sound; import org.bukkit.block.Block; import org.bukkit.entity.Monster; +import sig.plugin.TwosideKeeper.HelperStructures.Effects.TemporaryBlock; import sig.plugin.TwosideKeeper.HelperStructures.Utils.SoundUtils; public class ChargeZombie { @@ -152,6 +153,9 @@ public class ChargeZombie { public static boolean ChanceToBreak(Block b) { int blocktoughness = 0; + if (TemporaryBlock.isTemporaryBlock(b) || !aPlugin.API.isDestroyable(b)) { + return false; + } switch (b.getType()) { case BEDROCK: { blocktoughness=999999; diff --git a/src/sig/plugin/TwosideKeeper/CustomDamage.java b/src/sig/plugin/TwosideKeeper/CustomDamage.java index 1c0f4a5..fc8a38e 100644 --- a/src/sig/plugin/TwosideKeeper/CustomDamage.java +++ b/src/sig/plugin/TwosideKeeper/CustomDamage.java @@ -61,6 +61,7 @@ import sig.plugin.TwosideKeeper.Events.PlayerDodgeEvent; import sig.plugin.TwosideKeeper.HelperStructures.AdvancedTitle; import sig.plugin.TwosideKeeper.HelperStructures.ArtifactAbility; import sig.plugin.TwosideKeeper.HelperStructures.BowMode; +import sig.plugin.TwosideKeeper.HelperStructures.BuffTemplate; import sig.plugin.TwosideKeeper.HelperStructures.Channel; import sig.plugin.TwosideKeeper.HelperStructures.DamageStructure; import sig.plugin.TwosideKeeper.HelperStructures.ItemSet; @@ -71,6 +72,8 @@ import sig.plugin.TwosideKeeper.HelperStructures.PlayerMode; import sig.plugin.TwosideKeeper.HelperStructures.WorldShop; import sig.plugin.TwosideKeeper.HelperStructures.Common.BaublePouch; import sig.plugin.TwosideKeeper.HelperStructures.Common.GenericFunctions; +import sig.plugin.TwosideKeeper.HelperStructures.Effects.EffectPool; +import sig.plugin.TwosideKeeper.HelperStructures.Effects.TemporaryBlock; import sig.plugin.TwosideKeeper.HelperStructures.Effects.TemporaryBlockNode; import sig.plugin.TwosideKeeper.HelperStructures.Utils.ArtifactUtils; import sig.plugin.TwosideKeeper.HelperStructures.Utils.DebugUtils; @@ -84,6 +87,7 @@ import sig.plugin.TwosideKeeper.Monster.Dummy; import sig.plugin.TwosideKeeper.Monster.HellfireGhast; import sig.plugin.TwosideKeeper.Monster.HellfireSpider; import sig.plugin.TwosideKeeper.Monster.Knight; +import sig.plugin.TwosideKeeper.Monster.SniperSkeleton; public class CustomDamage { @@ -899,10 +903,42 @@ public class CustomDamage { GenericFunctions.logAndApplyPotionEffectToEntity(type,GenericFunctions.getBasePotionDuration(pd)/8, (pd.isUpgraded())?1:0, target); } } + if (proj.hasMetadata("SNIPER_NORMAL") || + proj.hasMetadata("SNIPER_POISON") || + proj.hasMetadata("SNIPER_BLEED") || + proj.hasMetadata("SNIPER_CRIPPLINGINFECTION")) { + if (proj.hasMetadata("SNIPER_POISON")) { + //Apply a stacking Poison. + Buff.addBuff(target, "Poison", new Buff("Poison",20*15,1,Color.YELLOW,ChatColor.YELLOW+"☣",false),true); + createPoisonPool(proj, target); + } + if (proj.hasMetadata("SNIPER_BLEED")) { + Buff.addBuff(target, "BLEEDING", new Buff("Bleed",20*15,1,Color.MAROON,ChatColor.DARK_RED+"☠",false),true); + createBloodPool(proj,target); + if (TwosideKeeper.custommonsters.containsKey(shooter.getUniqueId())) { + CustomMonster cm = TwosideKeeper.custommonsters.get(shooter.getUniqueId()); + cm.bloodPoolSpawnedEvent(target); + } + } + if (proj.hasMetadata("SNIPER_CRIPPLINGINFECTION")) { + Buff.addBuff(target, 20*30, 1, BuffTemplate.INFECTION); + } + GenericFunctions.removeNoDamageTick(target, damager); + } } return damage; } + private static void createBloodPool(Arrow proj, LivingEntity target) { + TemporaryBlock.createTemporaryBlockCircle(target.getLocation(), 1, Material.WOOL, (byte)14, 20*30, "BLOODPOOL"); + new EffectPool(target.getLocation(),1,20*30,Color.fromRGB(255, 0, 0)); + } + + private static void createPoisonPool(Arrow proj, LivingEntity target) { + TemporaryBlock.createTemporaryBlockCircle(target.getLocation(), 1, Material.WOOL, (byte)4, 20*30, "POISONPOOL"); + new EffectPool(target.getLocation(),1,20*30,Color.fromRGB(255, 255, 0)); + } + private static double IncreaseDamageFromDarkSubmission(Player p, Entity damager, double damage) { LivingEntity shooter = getDamagerEntity(damager); double bonusdmg = 0; @@ -1876,12 +1912,26 @@ public class CustomDamage { } public static void addToCustomStructures(LivingEntity m) { - addHellfireSpiderToList(m); - addHellfireGhastToList(m); - addBlazeToList(m); - addWitherToList(m); - addKnighttoList(m); removeStraySpiderMinions(m); + + + if (addHellfireSpiderToList(m)) {return;} + if (addHellfireGhastToList(m)) {return;} + if (addBlazeToList(m)) {return;} + if (addWitherToList(m)) {return;} + + + if (m instanceof Skeleton) { + if (Math.random()<=0.5) { + if (addKnighttoList(m)) {return;} else { + addSniperSkeletontoList(m); + } + } else { + if (addSniperSkeletontoList(m)) {return;} else { + addKnighttoList(m); + } + } + } } private static void removeStraySpiderMinions(LivingEntity m) { @@ -1891,51 +1941,76 @@ public class CustomDamage { } } - private static void addKnighttoList(LivingEntity m) { + public static boolean addSniperSkeletontoList(LivingEntity m) { + if (!TwosideKeeper.custommonsters.containsKey(m.getUniqueId()) && + (SniperSkeleton.isSniperSkeleton(m) || + (m instanceof Skeleton && + SniperSkeleton.randomlyConvertAsSniperSkeleton(m)))) { + TwosideKeeper.custommonsters.put(m.getUniqueId(),new SniperSkeleton(m)); + TwosideKeeper.log("Spawned a new "+LivingEntityStructure.getCustomLivingEntityName(m), 2); + TwosideKeeper.LAST_SPECIAL_SPAWN=TwosideKeeper.getServerTickTime(); + return true; + } + return false; + } + + private static boolean addKnighttoList(LivingEntity m) { if (!TwosideKeeper.custommonsters.containsKey(m.getUniqueId()) && (Knight.isKnight(m) || (m instanceof Skeleton && Knight.randomlyConvertAsKnight(m)))) { TwosideKeeper.custommonsters.put(m.getUniqueId(),new Knight(m)); - TwosideKeeper.log("Spawned a new "+LivingEntityStructure.getCustomLivingEntityName(m), 0); + TwosideKeeper.log("Spawned a new "+LivingEntityStructure.getCustomLivingEntityName(m), 2); TwosideKeeper.LAST_SPECIAL_SPAWN=TwosideKeeper.getServerTickTime(); + return true; } + return false; } - private static void addWitherToList(LivingEntity m) { + private static boolean addWitherToList(LivingEntity m) { if (!TwosideKeeper.custommonsters.containsKey(m.getUniqueId()) && m instanceof Wither) { TwosideKeeper.custommonsters.put(m.getUniqueId(),new sig.plugin.TwosideKeeper.Monster.Wither((Monster)m)); + return true; } + return false; } - static void addChargeZombieToList(LivingEntity m) { + static boolean addChargeZombieToList(LivingEntity m) { if (!TwosideKeeper.chargezombies.containsKey(m.getUniqueId()) && MonsterController.isChargeZombie(m)) { TwosideKeeper.chargezombies.put(m.getUniqueId(),new ChargeZombie((Monster)m)); + return true; } + return false; } - static void addHellfireSpiderToList(LivingEntity m) { + static boolean addHellfireSpiderToList(LivingEntity m) { if (!TwosideKeeper.custommonsters.containsKey(m.getUniqueId()) && MonsterController.isHellfireSpider(m)) { TwosideKeeper.custommonsters.put(m.getUniqueId(),new HellfireSpider((Monster)m)); TwosideKeeper.log("Added Hellfire Spider.", 5); + return true; } + return false; } - static void addBlazeToList(LivingEntity m) { + static boolean addBlazeToList(LivingEntity m) { if (!TwosideKeeper.custommonsters.containsKey(m.getUniqueId()) && m instanceof Blaze) { TwosideKeeper.custommonsters.put(m.getUniqueId(),new sig.plugin.TwosideKeeper.Monster.Blaze((Monster)m)); + return true; } + return false; } - static void addHellfireGhastToList(LivingEntity m) { + static boolean addHellfireGhastToList(LivingEntity m) { if (!TwosideKeeper.custommonsters.containsKey(m.getUniqueId()) && MonsterController.isHellfireGhast(m)) { TwosideKeeper.custommonsters.put(m.getUniqueId(),new HellfireGhast(m)); + return true; } + return false; } public static void addMonsterToTargetList(LivingEntity m,Player p) { @@ -2051,7 +2126,7 @@ public class CustomDamage { if (isFlagSet(flags,IGNORE_DAMAGE_TICK)) { GenericFunctions.removeNoDamageTick(target, damager); } - if (isFlagSet(flags,IGNORE_DAMAGE_TICK) || (GenericFunctions.enoughTicksHavePassed(target, damager) && canHitMobDueToWeakness(damager) && !GenericFunctions.isSuppressed(getDamagerEntity(damager)) && !target.isDead())) { + if (isFlagSet(flags,IGNORE_DAMAGE_TICK) || (GenericFunctions.enoughTicksHavePassed(target, damager) && canHitMobDueToWeakness(damager) && (!GenericFunctions.isSuppressed(getDamagerEntity(damager)) || damager instanceof Projectile) && !target.isDead())) { TwosideKeeper.log("Enough ticks have passed.", 5); if (CanResistExplosionsWithExperienceSet(damager, target, reason)) { @@ -2200,6 +2275,10 @@ public class CustomDamage { private static boolean PassesIframeCheck(LivingEntity target, Entity damager) { if ((target instanceof Player) && isInIframe((Player)target)) { return true; + } else + if (TwosideKeeper.custommonsters.containsKey(target.getUniqueId())) { + CustomMonster cm = TwosideKeeper.custommonsters.get(target.getUniqueId()); + return cm.isInIframe(); } return false; } @@ -3238,19 +3317,33 @@ public class CustomDamage { double mult = 0.0; if (target!=null) { if (target.hasPotionEffect(PotionEffectType.POISON)) { - mult += (GenericFunctions.getPotionEffectLevel(PotionEffectType.POISON, target)+1)*0.5; + mult += (GenericFunctions.getPotionEffectLevel(PotionEffectType.POISON, target)+1)*getPoisonMult(target); } /*if (target.hasPotionEffect(PotionEffectType.BLINDNESS)) { mult += (GenericFunctions.getPotionEffectLevel(PotionEffectType.BLINDNESS, target)+1)*0.5; }*/ if (Buff.hasBuff(target, "Poison")) { - mult += Buff.getBuff(target, "Poison").getAmplifier()*0.5; + mult += Buff.getBuff(target, "Poison").getAmplifier()*getPoisonMult(target); } } TwosideKeeper.log("Mult is "+mult, 5); return mult; } + private static double getPoisonMult(LivingEntity target) { + double mult = 0.5; + if (!(target instanceof Player)) { + LivingEntityStructure les = LivingEntityStructure.GetLivingEntityStructure(target); + if (les.isElite || les.isLeader || + MonsterController.getLivingEntityDifficulty(target)==LivingEntityDifficulty.T1_MINIBOSS || + MonsterController.getLivingEntityDifficulty(target)==LivingEntityDifficulty.T2_MINIBOSS || + MonsterController.getLivingEntityDifficulty(target)==LivingEntityDifficulty.T3_MINIBOSS) { + mult = 0.1; + } + } + return mult; + } + public static double calculateStrikerMultiplier(Entity damager, LivingEntity target) { double mult=0.0; if (damager instanceof Player) { diff --git a/src/sig/plugin/TwosideKeeper/CustomMonster.java b/src/sig/plugin/TwosideKeeper/CustomMonster.java index 3b09bbb..ee39f35 100644 --- a/src/sig/plugin/TwosideKeeper/CustomMonster.java +++ b/src/sig/plugin/TwosideKeeper/CustomMonster.java @@ -5,6 +5,8 @@ import java.io.File; import org.bukkit.entity.LivingEntity; import org.bukkit.entity.Monster; import org.bukkit.entity.Player; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.inventivetalent.glow.GlowAPI.Color; import sig.plugin.TwosideKeeper.Events.EntityChannelCastEvent; import sig.plugin.TwosideKeeper.HelperStructures.Utils.Classes.MixedDamage; @@ -63,6 +65,10 @@ public class CustomMonster { } + public boolean isImmuneToSuppression() { + return false; + } + public void cleanup() { } @@ -75,7 +81,22 @@ public class CustomMonster { } + public void runProjectileLaunchEvent(ProjectileLaunchEvent ev) { + + } + public void onDeathEvent() { } + + public Color getGlowColor() { + return null; + } + + public boolean isInIframe() { + return false; + } + + public void bloodPoolSpawnedEvent(LivingEntity target) { + } } diff --git a/src/sig/plugin/TwosideKeeper/EliteMonster.java b/src/sig/plugin/TwosideKeeper/EliteMonster.java index 5ff91ae..7bece2e 100644 --- a/src/sig/plugin/TwosideKeeper/EliteMonster.java +++ b/src/sig/plugin/TwosideKeeper/EliteMonster.java @@ -25,6 +25,7 @@ import org.bukkit.potion.PotionEffectType; import org.bukkit.util.Vector; import org.inventivetalent.glow.GlowAPI; +import sig.plugin.TwosideKeeper.HelperStructures.Channel; import sig.plugin.TwosideKeeper.HelperStructures.Common.GenericFunctions; import sig.plugin.TwosideKeeper.HelperStructures.Utils.SoundUtils; @@ -127,7 +128,7 @@ public class EliteMonster { } } bar.setProgress(m.getHealth()/m.getMaxHealth()); - bar.setTitle(GenericFunctions.getDisplayName(m) + ((m.getTarget()!=null && (m.getTarget() instanceof Player))?(ChatColor.DARK_AQUA+" "+arrow+" "+ChatColor.YELLOW+((Player)m.getTarget()).getName()):"")); + bar.setTitle((Channel.isChanneling(m)?LivingEntityStructure.getChannelingBar(m):GenericFunctions.getDisplayName(m)) + ((m.getTarget()!=null && (m.getTarget() instanceof Player))?(ChatColor.DARK_AQUA+" "+arrow+" "+ChatColor.YELLOW+((Player)m.getTarget()).getName()):"")); if (!(m instanceof Wither || m instanceof EnderDragon)) { displayHealthbarToNearbyPlayers(); } diff --git a/src/sig/plugin/TwosideKeeper/HelperStructures/ArtifactAbility.java b/src/sig/plugin/TwosideKeeper/HelperStructures/ArtifactAbility.java index bb0637c..ca4ce13 100644 --- a/src/sig/plugin/TwosideKeeper/HelperStructures/ArtifactAbility.java +++ b/src/sig/plugin/TwosideKeeper/HelperStructures/ArtifactAbility.java @@ -75,7 +75,7 @@ public enum ArtifactAbility { //Sword abilities PROVOKE("Provoke","Your attacks provoke enemies for [VAL] seconds.",new double[]{0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1}, - new double[]{3.0,2.9,2.8,2.7,2.6,2.5,2.4,2.3,2.2,2.1,2.0,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1},10000,10,UpgradePath.SWORD,1), + new double[]{3.0,2.9,2.8,2.7,2.6,2.5,2.4,2.3,2.2,2.1,2.0,1.9,1.8,1.7,1.6,1.5,1.4,1.3,1.2,1.1},10000,10,UpgradePath.PROVOKE,1), COMBO("Belligerent","[VAL]% more damage for each successive strike on a mob. Resets after 2 seconds of no combat.",new double[]{0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1}, new double[]{1.0,0.975,0.95,0.925,0.9,0.875,0.85,0.825,0.8,0.75,0.7,0.65,0.6,0.55,0.5},10000,40,UpgradePath.SWORD,1), @@ -441,6 +441,13 @@ public enum ArtifactAbility { return true; } }break; + case PROVOKE:{ + if ((item.getType().toString().contains("AXE") && !item.getType().toString().contains("PICKAXE")) + || item.getType().toString().contains("SWORD")) { + //This is an item that can upgrade with Provoke. + return true; + } + }break; case BASIC:{ if (!item.getType().toString().contains("HELMET") && !item.getType().toString().contains("CHESTPLATE") && diff --git a/src/sig/plugin/TwosideKeeper/HelperStructures/BuffTemplate.java b/src/sig/plugin/TwosideKeeper/HelperStructures/BuffTemplate.java new file mode 100644 index 0000000..8e3e5f6 --- /dev/null +++ b/src/sig/plugin/TwosideKeeper/HelperStructures/BuffTemplate.java @@ -0,0 +1,81 @@ +package sig.plugin.TwosideKeeper.HelperStructures; + +import org.bukkit.ChatColor; +import org.bukkit.Color; + +import sig.plugin.TwosideKeeper.Buff; + +public enum BuffTemplate { + POISON("Poison","Poison",Color.YELLOW,ChatColor.YELLOW+"☣",false), + SHRAPNEL("SHRAPNEL","Shrapnel",Color.RED,ChatColor.RED+"❂",false), + WINDCHARGES("WINDCHARGE","Wind",Color.GRAY,"๑",true), + BLEEDING("BLEEDING","Bleed",Color.MAROON,ChatColor.DARK_RED+"☠",false), + REGENERATION("REGENERATION","Regeneration",Color.GREEN,ChatColor.GREEN+""+ChatColor.BOLD+"✙",true), + INFECTION("INFECTION","Infection",Color.GRAY,ChatColor.GRAY+"❧",false), + CRIPPLE("CRIPPLE","Cripple",Color.WHITE,ChatColor.WHITE+"☹",false), + DARKSUBMISSION("DARKSUBMISSION",ChatColor.GRAY+"Dark Submission"+ChatColor.RESET,Color.BLACK,ChatColor.BLACK+""+ChatColor.MAGIC+"☁"+ChatColor.RESET,false), + CONFUSION("CONFUSION","Confusion",Color.PURPLE,ChatColor.DARK_PURPLE+"๑"+ChatColor.RESET,false), + + UNDYINGRAGE_COOLDOWN("COOLDOWN_UNDYING_RAGE","Undying Rage Cooldown",null,ChatColor.WHITE+"",true,true), + UNSTOPPABLETEAM_COOLDOWN("Unstoppable Team Unavailable","Unstoppable Team Unavailable",null,ChatColor.WHITE+"",true,true), + ; + + String keyName; + String displayName; + Color col; + String icon; + boolean isGoodBuff; + boolean permanentBuff; + boolean displayTimer; + + public String getKeyName() { + return keyName; + } + public String getDisplayName() { + return displayName; + } + public Color getParticleColor() { + return col; + } + public String getIcon() { + return icon; + } + public boolean isGoodBuff() { + return isGoodBuff; + } + public boolean isPermanentBuff() { + return permanentBuff; + } + public boolean isDisplayTimer() { + return displayTimer; + } + + BuffTemplate(String keyName, String displayName, Color particleColor, String icon, boolean isGoodBuff) { + this.keyName=keyName; + this.displayName=displayName; + this.col=particleColor; + this.icon=icon; + this.isGoodBuff=isGoodBuff; + this.permanentBuff=false; + this.displayTimer=false; + } + BuffTemplate(String keyName, String displayName, Color particleColor, String icon, boolean isGoodBuff, boolean isPermanentBuff) { + this.keyName=keyName; + this.displayName=displayName; + this.col=particleColor; + this.icon=icon; + this.isGoodBuff=isGoodBuff; + this.permanentBuff=isPermanentBuff; + this.displayTimer=false; + } + BuffTemplate(String keyName, String displayName, Color particleColor, String icon, boolean isGoodBuff, boolean isPermanentBuff, boolean displayTimer) { + this.keyName=keyName; + this.displayName=displayName; + this.col=particleColor; + this.icon=icon; + this.isGoodBuff=isGoodBuff; + this.permanentBuff=isPermanentBuff; + this.displayTimer=displayTimer; + } + //new Buff("Poison",20*20,Integer.parseInt(args[1]),Color.YELLOW,ChatColor.YELLOW+"☣",false) +} diff --git a/src/sig/plugin/TwosideKeeper/HelperStructures/Common/GenericFunctions.java b/src/sig/plugin/TwosideKeeper/HelperStructures/Common/GenericFunctions.java index 8a28af5..135f75d 100644 --- a/src/sig/plugin/TwosideKeeper/HelperStructures/Common/GenericFunctions.java +++ b/src/sig/plugin/TwosideKeeper/HelperStructures/Common/GenericFunctions.java @@ -72,6 +72,7 @@ import sig.plugin.TwosideKeeper.Artifact; import sig.plugin.TwosideKeeper.AwakenedArtifact; import sig.plugin.TwosideKeeper.Buff; import sig.plugin.TwosideKeeper.CustomDamage; +import sig.plugin.TwosideKeeper.CustomMonster; import sig.plugin.TwosideKeeper.EliteMonster; import sig.plugin.TwosideKeeper.MonsterController; import sig.plugin.TwosideKeeper.LivingEntityStructure; @@ -4892,6 +4893,12 @@ public class GenericFunctions { } private static void SetupSuppression(Entity ent, int ticks) { + if (TwosideKeeper.custommonsters.containsKey(ent.getUniqueId())) { + CustomMonster cm = TwosideKeeper.custommonsters.get(ent.getUniqueId()); + if (cm.isImmuneToSuppression()) { + return; + } + } if (!TwosideKeeper.suppressed_entities.contains(ent)) { TwosideKeeper.suppressed_entities.add(ent); } @@ -4915,6 +4922,18 @@ public class GenericFunctions { TwosideKeeper.log("Base Value: "+l.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getBaseValue(), 5); l.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0d); l.setAI(false); + /*double prev_movespd = l.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).getBaseValue(); + l.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0); + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{ + if (l!=null && l.isValid()) { + l.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(prev_movespd); + } + }, 2);*/ + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{ + if (l!=null && l.isValid()) { + l.setVelocity(new Vector(0,0,0)); + } + },1); } } diff --git a/src/sig/plugin/TwosideKeeper/HelperStructures/Effects/EffectPool.java b/src/sig/plugin/TwosideKeeper/HelperStructures/Effects/EffectPool.java new file mode 100644 index 0000000..684f23a --- /dev/null +++ b/src/sig/plugin/TwosideKeeper/HelperStructures/Effects/EffectPool.java @@ -0,0 +1,35 @@ +package sig.plugin.TwosideKeeper.HelperStructures.Effects; + +import org.bukkit.Color; +import org.bukkit.Location; + +import sig.plugin.TwosideKeeper.TwosideKeeper; +import sig.plugin.TwosideKeeper.HelperStructures.Utils.Classes.ColoredParticle; + +public class EffectPool { + double radius = 1; + Color col = null; + Location loc = null; + long expireTime=0; + final int PARTICLE_DENSITY = 5; + public EffectPool(Location loc, double radius, int duration, Color col) { + this.loc=loc.clone(); + this.radius=radius; + this.col=col; + this.expireTime=TwosideKeeper.getServerTickTime()+duration; + TwosideKeeper.effectpools.add(this); + } + + public boolean runTick() { + int density = (int)Math.pow(PARTICLE_DENSITY, radius); + for (int i=0;i endermites = new ArrayList(); LivingEntity silverfish = null; @@ -126,6 +129,7 @@ public class Knight extends CustomMonster{ m.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.31f); relinkToSpider(); m.setAI(false); + m.setRemoveWhenFarAway(false); createBossHealthbar(); //GenericFunctions.setGlowing(m, Color.AQUA); setupDarkSword(); @@ -145,8 +149,57 @@ public class Knight extends CustomMonster{ increaseBarTextScroll(); performSpells(); performSilverfishNotification(); + removeDebuffs(); + updateAI(); + removeIfTooOld(); + } + + public Color getGlowColor() { + return Color.AQUA; } + private void removeIfTooOld() { + if (m.getTicksLived()>72000 && !startedfight) { + m.remove(); + } + } + + private void updateAI() { + if (!startedfight) { + m.setAI(false); + } + } + + private void removeDebuffs() { + if (phaseii || lastremoveddebufftime+DEBUFFREMOVAL_COOLDOWN<=TwosideKeeper.getServerTickTime()) { + removeADebuff(); + } + } + + public boolean isImmuneToSuppression() { + return phaseii; + } + + private void removeADebuff() { + for (PotionEffect pe : m.getActivePotionEffects()) { + if (GenericFunctions.isBadEffect(pe.getType())) { + GenericFunctions.logAndRemovePotionEffectFromEntity(pe.getType(), m); + return; + } + } + for (String s : Buff.getBuffData(m).keySet()) { + Buff b = Buff.getBuffData(m).get(s); + if (b.isDebuff()) { + /*TwosideKeeper.ScheduleRemoval(Buff.getBuffData(m), s); + return;*/ + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{ + Buff.removeBuff(m, s); + }, 1); + return; + } + } + } + private void performSilverfishNotification() { if (silverfish!=null && silverfishtimer+(MINDFIELD.getCooldowns()[getDifficultySlot()])<=TwosideKeeper.getServerTickTime()) { @@ -170,6 +223,7 @@ public class Knight extends CustomMonster{ public void onPlayerSlayEvent(Player p, String reason) { if (reason.equalsIgnoreCase("Line Drive Knight")) { changeAggroToRandomNewTarget(); + LINEDRIVE.setLastCastedTime(0); attemptSpellCast(LINEDRIVE); SoundUtils.playGlobalSound(m.getLocation(), Sound.UI_BUTTON_CLICK, 1.0f, 0.9f); } @@ -422,13 +476,21 @@ public class Knight extends CustomMonster{ protected boolean attemptSpellCast(Spell spell) { if (cooldownIsAvailable(spell.getLastCastedTime(),spell)) { //Face target. - Channel.createNewChannel(m, spell.getName(), spell.getCastTimes()[getDifficultySlot()]); + Channel.createNewChannel(m, spell.getName(), (int)(spell.getCastTimes()[getDifficultySlot()]*getCastTimeMultiplier())); Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{FaceTarget(m);}, 5); return true; } return false; } + private double getCastTimeMultiplier() { + double mult = 1.0; + if (phaseii) { + mult=0.5; + } + return mult; + } + public MixedDamage getBasicAttackDamage() { return BASIC_ATTACK_DAMAGE[getDifficultySlot()]; } diff --git a/src/sig/plugin/TwosideKeeper/Monster/SniperSkeleton.java b/src/sig/plugin/TwosideKeeper/Monster/SniperSkeleton.java new file mode 100644 index 0000000..e0a06d7 --- /dev/null +++ b/src/sig/plugin/TwosideKeeper/Monster/SniperSkeleton.java @@ -0,0 +1,869 @@ +package sig.plugin.TwosideKeeper.Monster; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.text.DecimalFormat; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.attribute.Attribute; +import org.bukkit.block.Block; +import org.bukkit.block.BlockFace; +import org.bukkit.boss.BarColor; +import org.bukkit.boss.BarFlag; +import org.bukkit.boss.BarStyle; +import org.bukkit.boss.BossBar; +import org.bukkit.enchantments.Enchantment; +import org.bukkit.entity.Arrow; +import org.bukkit.entity.Endermite; +import org.bukkit.entity.Entity; +import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Monster; +import org.bukkit.entity.Player; +import org.bukkit.entity.Projectile; +import org.bukkit.entity.Skeleton; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.LeatherArmorMeta; +import org.bukkit.metadata.FixedMetadataValue; +import org.bukkit.entity.Skeleton.SkeletonType; +import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.potion.PotionEffectType; +import org.bukkit.util.Vector; +import org.inventivetalent.glow.GlowAPI.Color; + +import sig.plugin.TwosideKeeper.Buff; +import sig.plugin.TwosideKeeper.ChargeZombie; +import sig.plugin.TwosideKeeper.CustomDamage; +import sig.plugin.TwosideKeeper.CustomMonster; +import sig.plugin.TwosideKeeper.LivingEntityStructure; +import sig.plugin.TwosideKeeper.MonsterController; +import sig.plugin.TwosideKeeper.TwosideKeeper; +import sig.plugin.TwosideKeeper.Events.EntityChannelCastEvent; +import sig.plugin.TwosideKeeper.HelperStructures.BuffTemplate; +import sig.plugin.TwosideKeeper.HelperStructures.Channel; +import sig.plugin.TwosideKeeper.HelperStructures.LivingEntityDifficulty; +import sig.plugin.TwosideKeeper.HelperStructures.Spell; +import sig.plugin.TwosideKeeper.HelperStructures.Common.GenericFunctions; +import sig.plugin.TwosideKeeper.HelperStructures.Effects.HighlightCircle; +import sig.plugin.TwosideKeeper.HelperStructures.Utils.EntityUtils; +import sig.plugin.TwosideKeeper.HelperStructures.Utils.MovementUtils; +import sig.plugin.TwosideKeeper.HelperStructures.Utils.SoundUtils; +import sig.plugin.TwosideKeeper.HelperStructures.Utils.Classes.ColoredParticle; +import sig.plugin.TwosideKeeper.HelperStructures.Utils.Classes.MixedDamage; + +public class SniperSkeleton extends CustomMonster{ + + BossBar healthbar; + protected String arrow = "->"; + int scroll=0; + protected List participantlist = new ArrayList(); + protected HashMap dpslist = new HashMap(); + long lasthit; + boolean startedfight=false; + private long stuckTimer=0; + private Location lastLoc = null; + MixedDamage[] BASIC_ATTACK_DAMAGE = new MixedDamage[]{MixedDamage.v(30),MixedDamage.v(50),MixedDamage.v(130, 0.02)}; + final Spell PIERCING_ARROW = new Spell("Piercing Arrow",new int[]{40,20,20},new int[]{240,200,160},new MixedDamage[]{MixedDamage.v(100,0.02),MixedDamage.v(140,0.04),MixedDamage.v(200,0.08)}); + final Spell BURNING_PLUME = new Spell("Burning Plume",new int[]{50,40,30},new int[]{300,240,200},new MixedDamage[]{MixedDamage.v(200,0.06),MixedDamage.v(300,0.08),MixedDamage.v(500,0.15)}); + final Spell CRIPPLING_INFECTION = new Spell("Crippling Infection",new int[]{30,20,20},new int[]{240,240,240}); + final Spell SIPHON_BURST = new Spell("Siphon Burst",new int[]{80,60,40},new int[]{0,0,0},new MixedDamage[]{MixedDamage.v(10,0.06),MixedDamage.v(40,0.1),MixedDamage.v(70,0.15)}); + final Spell ARROW_RAIN = new Spell("Arrow Rain",new int[]{160,160,160},new int[]{800,800,800},new MixedDamage[]{MixedDamage.v(100,0,5),MixedDamage.v(125,0.05,10),MixedDamage.v(150,0.10,20)}); + final Spell MODE_SHIFT = new Spell("Mode Shift",new int[]{20,20,20},new int[]{120,120,120}); + int randomness = 20; + final Spell ENERGIZEDSHOTS = new Spell("Energized Shots",new int[]{60,40,40},new int[]{0,0,0}); + boolean phaseii = false; + ShotMode mode = ShotMode.NORMAL; + + long shotmodeExpireTime = 0; + + + long lastUsedDodge=0; + final static int[] DODGE_COOLDOWN = new int[]{80,60,40}; + final int[] MODE_EXPIRE_TIME = new int[]{120,240,360}; + final static double[] BLOODMITE_HEALTH = new double[]{180,650,1200}; + + List bloodmites = new ArrayList(); + + public SniperSkeleton(LivingEntity m) { + super(m); + LivingEntityStructure les = LivingEntityStructure.GetLivingEntityStructure(m); + les.setCustomLivingEntityName(m, ChatColor.GOLD+"Sniper Skeleton"); + LivingEntityDifficulty led = MonsterController.getLivingEntityDifficulty(m); + switch (led) { + case T1_MINIBOSS:{ + m.setMaxHealth(16000); + }break; + case T2_MINIBOSS:{ + m.setMaxHealth(41000); + }break; + case T3_MINIBOSS:{ + m.setMaxHealth(108000); + }break; + } + m.setHealth(m.getMaxHealth()); + m.getAttribute(Attribute.GENERIC_MOVEMENT_SPEED).setBaseValue(0.3625f); + m.setAI(false); + m.setRemoveWhenFarAway(false); + createBossHealthbar(); + setupBow(); + GenericFunctions.logAndRemovePotionEffectFromEntity(PotionEffectType.INVISIBILITY, m); + } + + private void setupBow() { + ItemStack bow = new ItemStack(Material.BOW); + bow.addUnsafeEnchantment(Enchantment.ARROW_DAMAGE, 10); + bow.addUnsafeEnchantment(Enchantment.ARROW_INFINITE, 10); + bow.addUnsafeEnchantment(Enchantment.ARROW_KNOCKBACK, 1); + bow.addUnsafeEnchantment(Enchantment.ARROW_FIRE, 10); + m.getEquipment().setItemInMainHand(bow); + m.getEquipment().setItemInMainHandDropChance(0.2f); + } + + private void createBossHealthbar() { + healthbar = Bukkit.getServer().createBossBar(GenericFunctions.getDisplayName(m), BarColor.WHITE, BarStyle.SEGMENTED_10, BarFlag.CREATE_FOG); + healthbar.setProgress(m.getHealth()/m.getMaxHealth()); + } + + public void runTick() { + updateHealthbarForNearbyPlayers(); + updateTargetIfLost(); + regenerateHealthAndResetBossIfIdle(); + keepHealthbarUpdated(); + unstuckIfStuck(); + increaseBarTextScroll(); + performSpells(); + updateAI(); + removeIfTooOld(); + giveHatProtectionAndFireResist(); + setModeBackToNormal(); + } + + private void setModeBackToNormal() { + if (mode!=ShotMode.NORMAL && + shotmodeExpireTime<=TwosideKeeper.getServerTickTime()) { + mode=ShotMode.NORMAL; + } + } + + public void runProjectileLaunchEvent(ProjectileLaunchEvent ev) { + Projectile proj = ev.getEntity(); + proj.setMetadata("SNIPER_"+mode.name(), new FixedMetadataValue(TwosideKeeper.plugin,true)); + } + + private void giveHatProtectionAndFireResist() { + ItemStack helm = new ItemStack(Material.LEATHER_HELMET); + LeatherArmorMeta meta = (LeatherArmorMeta)(helm.getItemMeta()); + meta.setColor(org.bukkit.Color.fromRGB(255,255,0)); + helm.setItemMeta(meta); + m.getEquipment().setHelmet(helm); + } + + public Color getGlowColor() { + if (isInIframe()) { + return Color.WHITE; + } else { + switch (mode) { + case NORMAL: return Color.AQUA; + case POISON: return Color.YELLOW; + case BLEED: return Color.RED; + default: return Color.AQUA; + } + } + } + + private void removeIfTooOld() { + if (m.getTicksLived()>72000 && !startedfight) { + m.remove(); + } + } + + private void updateAI() { + if (!startedfight) { + m.setAI(false); + } + } + + public boolean isImmuneToSuppression() { + return phaseii; + } + + public void runChannelCastEvent(EntityChannelCastEvent ev) { + switch (ev.getAbilityName()) { + case "Piercing Arrow":{ + final int NUMBER_OF_PARTICLES=40; + final int RANGE = 20; + Location baseloc = m.getEyeLocation().clone(); + SoundUtils.playGlobalSound(m.getLocation(), Sound.ENTITY_ARROW_SHOOT, 1.0f, 1.6f); + for (int i=0;i0) {GenericFunctions.DealDamageToNearbyPlayers(baseloc, dmg.getTruePctDmgComponent(), 1, false, true, 0, m, "Piercing Arrow", false, true);} + if (dmg.getTrueDmgComponent()>0) {GenericFunctions.DealDamageToNearbyPlayers(baseloc, dmg.getTrueDmgComponent(), 1, false, true, 0, m, "Piercing Arrow", true, false);} + } + PIERCING_ARROW.setLastCastedTime(TwosideKeeper.getServerTickTime()); + }break; + case "Mode Shift":{ + if (mode!=ShotMode.NORMAL) { + mode=ShotMode.NORMAL; + } else { + if (Math.random()<=0.5) { + mode=ShotMode.POISON; + } else { + mode=ShotMode.BLEED; + } + } + MODE_SHIFT.setLastCastedTime(TwosideKeeper.getServerTickTime()); + shotmodeExpireTime = TwosideKeeper.getServerTickTime()+MODE_EXPIRE_TIME[getDifficultySlot()]; + }break; + case "Burning Plume":{ + final int RANGE = 12; + final int PARTICLE_AMT = 100; + BlockFace facingdir = EntityUtils.getFacingDirection(m); + Location startingloc = m.getLocation().add(new Vector(facingdir.getModX(),0,facingdir.getModZ())); + List firestarters = new ArrayList(); + int[] size = new int[]{1,3,3}; + for (int i=-size[getDifficultySlot()];i<=size[getDifficultySlot()];i++) { + BlockFace[] newdir = MovementUtils.get90DegreeDirections(facingdir); + if (i<0) { + Location newloc = m.getLocation().add(new Vector(newdir[1].getModX()*Math.abs(i),0,newdir[1].getModZ()*Math.abs(i))); + firestarters.add(newloc); + } else + if (i>0){ + Location newloc = m.getLocation().add(new Vector(newdir[0].getModX()*Math.abs(i),0,newdir[0].getModZ()*Math.abs(i))); + firestarters.add(newloc); + } + } + firestarters.add(startingloc); + Location centerloc = startingloc.clone(); + for (int i=0;i { + for (Location l : firestarters) { + Block b = findFireBlock(l); + //TwosideKeeper.log("Fire starter: "+l+". Block chosen: "+b, 0); + if (b!=null) { + b.setType(Material.FIRE); + } + l.add(new Vector(facingdir.getModX(),0,facingdir.getModZ())); + + for (int j=0;j players = GenericFunctions.DealDamageToNearbyPlayers(dmgloc, dmg.getDmgComponent(), size[getDifficultySlot()], false, true, 0, m, "Burning Plume", false, false); + if (dmg.getTruePctDmgComponent()>0) {GenericFunctions.DealDamageToNearbyPlayers(dmgloc, dmg.getTruePctDmgComponent(), size[getDifficultySlot()], false, true, 0, m, "Burning Plume", false, true);} + if (dmg.getTrueDmgComponent()>0) {GenericFunctions.DealDamageToNearbyPlayers(dmgloc, dmg.getTrueDmgComponent(), size[getDifficultySlot()], false, true, 0, m, "Burning Plume", true, false);} + for (Player p : players) { + p.setFireTicks(p.getFireTicks()+(20*10)); + } + SoundUtils.playGlobalSound(dmgloc, Sound.BLOCK_FURNACE_FIRE_CRACKLE, 1.0f, 1.0f); + }, (3*i)+3); + } + BURNING_PLUME.setLastCastedTime(TwosideKeeper.getServerTickTime()); + }break; + case "Crippling Infection":{ + BlockFace facingdir = EntityUtils.getFacingDirection(m); + Location newloc = m.getLocation().add(facingdir.getModX(),0,facingdir.getModZ()); + double dist = newloc.distance(m.getLocation()); + //Arrow a = m.launchProjectile(Arrow.class); + List speeds = new ArrayList(); + for (BlockFace bf : MovementUtils.get45DegreeDirections(facingdir)) { + speeds.add(new Vector(bf.getModX(),0,bf.getModZ()).multiply(dist)); + } + for (BlockFace bf : MovementUtils.get90DegreeDirections(facingdir)) { + speeds.add(new Vector(bf.getModX(),0,bf.getModZ()).multiply(dist)); + } + for (int i=0;i0) {CustomDamage.ApplyDamage(p.getMaxHealth()*(stackamt*dmg.getTruePctDmgComponent()), m, p, null, "Siphon Burst", CustomDamage.TRUEDMG|CustomDamage.IGNORE_DAMAGE_TICK|CustomDamage.IGNOREDODGE);} + if (dmg.getTrueDmgComponent()>0) {CustomDamage.ApplyDamage((stackamt*dmg.getTrueDmgComponent()), m, p, null, "Siphon Burst", CustomDamage.TRUEDMG|CustomDamage.IGNORE_DAMAGE_TICK|CustomDamage.IGNOREDODGE);} + SoundUtils.playGlobalSound(p.getLocation(),Sound.ENTITY_ZOMBIE_ATTACK_IRON_DOOR, 1.0f, 1.0f); + } + } + + private Block findFireBlock(Location l) { + int diffy = 0; + while (diffy<=5) { + Block testb = l.getBlock().getRelative(0, diffy, 0); + if (testb.getType()==Material.AIR && + testb.getRelative(0, -1, 0).getType().isSolid()) { + return testb; + } + if (diffy<=0) { + diffy--; + if (diffy<-5) { + diffy=1; + } + } else { + diffy++; + } + } + return null; + } + + public void bloodPoolSpawnedEvent(LivingEntity target) { + Endermite bloodmite = (Endermite)target.getWorld().spawnEntity(target.getLocation(), EntityType.ENDERMITE); + Bloodmite bm = new Bloodmite(bloodmite); + bm.setMainEntity(this); + bm.GetMonster().setMaxHealth(BLOODMITE_HEALTH[getDifficultySlot()]); + bm.GetMonster().setHealth(bm.GetMonster().getMaxHealth()); + bloodmite.setTarget(pickRandomTarget()); + TwosideKeeper.custommonsters.put(bloodmite.getUniqueId(), bm); + bloodmites.add(bloodmite); + } + + private void announceMessageToParticipants(String msg) { + for (Player p : participantlist) { + p.sendMessage(msg); + } + } + + protected boolean attemptSpellCast(Spell spell) { + if (cooldownIsAvailable(spell.getLastCastedTime(),spell)) { + //Face target. + Channel.createNewChannel(m, spell.getName(), (int)(spell.getCastTimes()[getDifficultySlot()])); + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{FaceTarget(m);}, 5); + return true; + } + return false; + } + + public void cleanup() { + healthbar.removeAll(); + for (LivingEntity ent : bloodmites) { + if (ent!=null && ent.isValid()) { + ent.remove(); + } + } + } + + public MixedDamage getBasicAttackDamage() { + return BASIC_ATTACK_DAMAGE[getDifficultySlot()]; + } + + private void FaceTarget(LivingEntity m) { + if (((Monster)m).getTarget()!=null) { + Location loc = m.getLocation(); + loc.setDirection(MovementUtils.pointTowardsLocation(loc, ((Monster)m).getTarget().getLocation())); + m.teleport(loc); + } + } + private boolean cooldownIsAvailable(long spell_timer, Spell spell) { + return spell_timer+spell.getCooldowns()[getDifficultySlot()]<=TwosideKeeper.getServerTickTime(); + } + + public boolean isInIframe() { + return m.hasPotionEffect(PotionEffectType.INVISIBILITY); + } + + private void performDodge() { + if (lastUsedDodge+DODGE_COOLDOWN[getDifficultySlot()]<=TwosideKeeper.getServerTickTime()) { + SoundUtils.playGlobalSound(m.getLocation(), Sound.ENTITY_DONKEY_CHEST, 1.0f, 1.0f); + GenericFunctions.logAndApplyPotionEffectToEntity(PotionEffectType.INVISIBILITY, 20, 0, m, true); + if (distanceToTarget()<9) { + m.setVelocity(m.getLocation().getDirection().multiply(-0.4f)); + } + lastUsedDodge=TwosideKeeper.getServerTickTime(); + } + } + + private double distanceToTarget() { + Monster me = (Monster)m; + if (me.getTarget()!=null && me.getTarget().getWorld().equals(m.getWorld())) { + return m.getLocation().distanceSquared(me.getTarget().getLocation()); + } else { + return 0; + } + } + + private void performSpells() { + final Runnable[] actions = new Runnable[]{ + ()->{performDodge();}, + ()->{attemptSpellCast(PIERCING_ARROW);}, + ()->{attemptSpellCast(MODE_SHIFT);}, + ()->{attemptSpellCast(BURNING_PLUME);}, + ()->{attemptSpellCast(CRIPPLING_INFECTION);}, + }; + final Runnable[] actions2 = new Runnable[]{ + ()->{attemptSpellCast(MODE_SHIFT);}, + ()->{attemptSpellCast(CRIPPLING_INFECTION);}, + ()->{if (meetsConditionsForSiphon()) { + attemptSpellCast(SIPHON_BURST); + }}, + ()->{if (attemptSpellCast(ARROW_RAIN)) { + runArrowRain();}}, + }; + if (canCastSpells()) { + if (phaseii) { + for (Runnable r : actions2) { + if (Math.random()<=1d/actions2.length) { + Bukkit.getScheduler().runTask(TwosideKeeper.plugin, r); + break; + } + } + } else { + performSpellFromFirstPhase(actions); + } + } + if (!phaseii && m.getHealth()<=m.getMaxHealth()/2 && startedfight) { + if (attemptSpellCast(ENERGIZEDSHOTS)) { + phaseii=true; + } + } + } + + private void runArrowRain() { + new HighlightCircle(m.getLocation(),8,20,ARROW_RAIN.getCastTimes()[getDifficultySlot()]); + for (int i=0;i<200;i++) { + int randomTickTime=(int)(Math.random()*ARROW_RAIN.getCastTimes()[getDifficultySlot()]); + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, + ()->{ + Arrow a = m.launchProjectile(Arrow.class); + BlockFace facingdir = EntityUtils.getFacingDirection(m); + Location newloc = m.getLocation().add(facingdir.getModX(),1,facingdir.getModZ()); + double dist = newloc.distance(m.getLocation()); + a.setVelocity(new Vector(Math.random()*2-1,0.1,Math.random()*2-1).multiply(dist)); + switch ((int)(Math.random()*3)) { + case 0:{ + a.setMetadata("SNIPER_POISON", new FixedMetadataValue(TwosideKeeper.plugin,true)); + }break; + case 1:{ + a.setMetadata("SNIPER_BLEED", new FixedMetadataValue(TwosideKeeper.plugin,true)); + }break; + case 2:{ + a.setMetadata("SNIPER_CRIPPLINGINFECTION", new FixedMetadataValue(TwosideKeeper.plugin,true)); + }break; + } + for (int j=0;j<5;j++) + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, + ()->{ + a.getWorld().spawnParticle(Particle.CRIT, a.getLocation(), 3); + },j*1); + }, randomTickTime); + } + } + + private boolean meetsConditionsForSiphon() { + int debufflevels = 0; + for (Player p : participantlist) { + if (Buff.hasBuff(p, "BLEEDING")) { + debufflevels++; + } + if (Buff.hasBuff(p, "INFECTION")) { + debufflevels++; + } + if (Buff.hasBuff(p, "Poison")) { + debufflevels++; + } + if (debufflevels>=2) { + return true; + } + } + return false; + } + + private void performSpellFromFirstPhase(final Runnable[] actions) { + for (Runnable r : actions) { + if (Math.random()<=1d/actions.length) { + Bukkit.getScheduler().runTask(TwosideKeeper.plugin, r); + break; + } + } + } + + public static double getDamageReduction() { + return 0.0; + } + + public static boolean randomlyConvertAsSniperSkeleton(LivingEntity m) { + return randomlyConvertAsSniperSkeleton(m,false); + } + + private Player setAggroOnRandomTarget() { + Player p = pickRandomTarget(); + setAggro((Monster)m,p); + return p; + } + + public LivingEntityDifficulty getDifficulty() { + return MonsterController.getLivingEntityDifficulty(m); + } + + public int getDifficultySlot() { + switch (getDifficulty()) { + case T1_MINIBOSS:{ + return 0; + } + case T2_MINIBOSS:{ + return 1; + } + case T3_MINIBOSS:{ + return 2; + } + default:{ + TwosideKeeper.log("WARNING! Could not get proper difficulty slot for Difficulty "+getDifficulty()+". Defaulting to slot 0.", 1); + return 0; + } + } + } + + private Player changeAggroToRandomNewTarget() { + if (Math.random()<=0.5) { + Monster me = (Monster)m; + Player newtarget = pickRandomTarget(); + setAggro(me, newtarget); + return newtarget; + } else { + Monster me = (Monster)m; + return (Player)me.getTarget(); + } + } + + private void setAggro(Monster me, Player newtarget) { + if (newtarget!=null) { + me.setTarget(newtarget); + LivingEntityStructure les = LivingEntityStructure.GetLivingEntityStructure(m); + les.SetTarget(me.getTarget()); + } + } + + private Player pickRandomTarget() { + updateTargetList(); + if (participantlist.size()>0) { + for (Player p : participantlist) { + if (Math.random()<=1d/participantlist.size() && + !p.isDead() && p.isValid()) { + return p; + } + } + return participantlist.get(0); + } else { + return null; + } + } + + private void updateTargetList() { + for (int i=0;i2500) { + participantlist.remove(i--); + } + } + } + + private boolean canCastSpells() { + return Math.random()<=1/8d && !Buff.hasBuff(m, "SILENCE") && startedfight && !Channel.isChanneling(m) && !m.hasPotionEffect(PotionEffectType.INVISIBILITY); + } + + private void unstuckIfStuck() { + if (!startedfight) { + ChargeZombie.BreakBlocksAroundArea((Monster)m, 1); + } else + if (startedfight) { + lastLoc = m.getLocation().clone(); + if (lastLoc!=null && lastLoc.distance(m.getLocation())<=0.4) { + stuckTimer++; + //TwosideKeeper.log("Stuck. "+stuckTimer, 0); + ChargeZombie.BreakBlocksAroundArea((Monster)m, 1); + } else { + stuckTimer=0; + } + if (!Channel.isChanneling(m) && stuckTimer>5) { + //Teleport randomly. + double numb = Math.random(); + if (numb<=0.33) { + Location newloc = m.getLocation().add(Math.random()*10-5,0,0); + if (!newloc.getBlock().getType().isSolid() && + !newloc.getBlock().getRelative(0,1,0).getType().isSolid()) { + SoundUtils.playGlobalSound(m.getLocation(), Sound.ENTITY_ENDERMEN_TELEPORT, 0.4f, 0.95f); + m.teleport(newloc); + } + } else + if (numb<=0.5) { + Location newloc = m.getLocation().add(0,0,Math.random()*10-5); + if (!newloc.getBlock().getType().isSolid() && + !newloc.getBlock().getRelative(0,1,0).getType().isSolid()) { + SoundUtils.playGlobalSound(m.getLocation(), Sound.ENTITY_ENDERMEN_TELEPORT, 0.4f, 0.95f); + m.teleport(newloc); + } + } + stuckTimer=0; + } + } + } + + private void keepHealthbarUpdated() { + healthbar.setProgress(m.getHealth()/m.getMaxHealth()); + Monster me = (Monster)m; + String healthbarfooter = ((me.getTarget()!=null && (me.getTarget() instanceof Player))?(ChatColor.DARK_AQUA+" "+arrow+" "+ChatColor.YELLOW+((Player)me.getTarget()).getName()):""); + if (Channel.isChanneling(m)) { + healthbar.setTitle(LivingEntityStructure.getChannelingBar(m)+healthbarfooter); + } else { + healthbar.setTitle(GenericFunctions.getDisplayName(m)+healthbarfooter); + } + } + + private void regenerateHealthAndResetBossIfIdle() { + if (lasthit+20*15<=TwosideKeeper.getServerTickTime()) { + GenericFunctions.HealEntity(m, m.getMaxHealth()*0.01); + if (startedfight) { + healthbar.setColor(BarColor.GREEN); + } + } else { + if (startedfight) { + healthbar.setColor(BarColor.BLUE); + } + } + if (participantlist.size()==0 && startedfight) { + startedfight=false; + m.setAI(false); + m.setHealth(m.getMaxHealth()); + announceFailedTakedown(); + } + } + + private void updateTargetIfLost() { + Monster mm = (Monster)m; + LivingEntityStructure les = LivingEntityStructure.GetLivingEntityStructure(m); + if (mm.getTarget()==null || !mm.getTarget().isValid() || + les.GetTarget()==null || !mm.getTarget().isValid() || + ((mm.getTarget().getLocation().distanceSquared(mm.getLocation())>2500 || + les.GetTarget().getLocation().distanceSquared(mm.getLocation())>2500 + ))) { + //See if there's another participant in the list. Choose randomly. + while (participantlist.size()>0) { + Player p = participantlist.get((int)(Math.random()*participantlist.size())); + if (p!=null && p.isValid() && !p.isDead() && + (p.getLocation().distanceSquared(mm.getLocation())<=2500)) { + mm.setTarget(p); + les.SetTarget(p); + break; + } else { + participantlist.remove(p); + } + } + if (participantlist.size()==0 && startedfight) { + //This fight has failed. + announceFailedTakedown(); + startedfight=false; + } + } + } + + public void announceFailedTakedown() { + if (dpslist.size()>0 && !m.isDead()) { + phaseii=false; + Bukkit.getServer().broadcastMessage(GenericFunctions.getDisplayName(m)+" Takedown Failed..."); + Bukkit.getServer().broadcastMessage(ChatColor.YELLOW+"DPS Breakdown:"); + Bukkit.getServer().broadcastMessage(generateDPSReport()); + aPlugin.API.discordSendRaw(GenericFunctions.getDisplayName(m)+" Takedown Failed...\n\n"+ChatColor.YELLOW+"DPS Breakdown:"+"\n```\n"+generateDPSReport()+"\n```"); + dpslist.clear(); + healthbar.setColor(BarColor.WHITE); + + for (LivingEntity ent : bloodmites) { + if (ent!=null && ent.isValid()) { + ent.remove(); + } + } + } + } + + public void announceSuccessfulTakedown() { + if (dpslist.size()>0 && !m.isDead()) { + phaseii=false; + Bukkit.getServer().broadcastMessage(GenericFunctions.getDisplayName(m)+" Takedown Failed..."); + Bukkit.getServer().broadcastMessage(ChatColor.YELLOW+"DPS Breakdown:"); + Bukkit.getServer().broadcastMessage(generateDPSReport()); + aPlugin.API.discordSendRaw(GenericFunctions.getDisplayName(m)+" Takedown Failed...\n\n"+ChatColor.YELLOW+"DPS Breakdown:"+"\n```\n"+generateDPSReport()+"\n```"); + dpslist.clear(); + healthbar.setColor(BarColor.WHITE); + + for (LivingEntity ent : bloodmites) { + if (ent!=null && ent.isValid()) { + ent.remove(); + } + } + } + } + + public String generateDPSReport() { + //Sorts a list of players by DPS contribution. + List sorted_dmg = new ArrayList(); + List sorted_pl = new ArrayList(); + double totaldmg = 0; + for (String pl : dpslist.keySet()) { + double dmg = dpslist.get(pl); + int slot = 0; + totaldmg+=dmg; + for (int i=0;isorted_dmg.get(i)) { + break; + } else { + slot++; + } + } + sorted_pl.add(slot,pl); + sorted_dmg.add(slot,dmg); + } + StringBuilder finalstr = new StringBuilder(); + DecimalFormat df = new DecimalFormat("0.00"); + for (int i=0;i{ + m.setVelocity(m.getVelocity().multiply(0.33)); + }, 1); + healthbar.setProgress(m.getHealth()/m.getMaxHealth()); + lasthit=TwosideKeeper.getServerTickTime(); + if (!startedfight) { + startedfight=true; + healthbar.setColor(BarColor.BLUE); + } + m.setAI(true); + } + + private void addTarget(LivingEntity damager, double dmg) { + if (damager instanceof Player) { + Player p = (Player)damager; + addParticipant(p); + if (!dpslist.containsKey(p.getName())) { + dpslist.put(p.getName(), dmg); + } else { + dpslist.put(p.getName(), dpslist.get(p.getName())+dmg); + } + } + } + + public void addParticipant(Player p) { + if (!participantlist.contains(p)) { + participantlist.add(p); + } + } + + private void updateHealthbarForNearbyPlayers() { + for (Player p : healthbar.getPlayers()) { + if (p.getWorld().equals(m.getWorld()) && p.getLocation().distanceSquared(m.getLocation())>2500) { + healthbar.removePlayer(p); + } + } + for (Entity e : m.getNearbyEntities(50, 50, 50)) { + if (e instanceof Player) { + Player p = (Player)e; + healthbar.addPlayer(p); + } + } + } + + public static boolean randomlyConvertAsSniperSkeleton(LivingEntity m, boolean force) { + if ((TwosideKeeper.MINIBOSSES_ACTIVATED && + TwosideKeeper.LAST_SPECIAL_SPAWN+(6000/Math.max(Bukkit.getOnlinePlayers().size(),1))<=TwosideKeeper.getServerTickTime() && + Math.random()<=0.01) || force) { + Skeleton s = (Skeleton)m; + s.setSkeletonType(SkeletonType.NORMAL); + //Determine distance from Twoside for Difficulty. + Location compareloc = TwosideKeeper.TWOSIDE_LOCATION; + if (!compareloc.getWorld().equals(s.getWorld())) { + compareloc = new Location(s.getWorld(),0,0,0); + } + double chancer = compareloc.distanceSquared(m.getLocation()); + if (Math.random()*chancer<4000000) { + MonsterController.convertLivingEntity(m, LivingEntityDifficulty.T1_MINIBOSS); + } else + if (Math.random()*chancer<25000000) { + MonsterController.convertLivingEntity(m, LivingEntityDifficulty.T2_MINIBOSS); + } else { + MonsterController.convertLivingEntity(m, LivingEntityDifficulty.T3_MINIBOSS); + } + return true; + } + return false; + } + + public static boolean isSniperSkeleton(LivingEntity m) { + return m instanceof Skeleton && + ((Skeleton)m).getSkeletonType()==SkeletonType.NORMAL && + ( + MonsterController.getLivingEntityDifficulty(m)==LivingEntityDifficulty.T1_MINIBOSS || + MonsterController.getLivingEntityDifficulty(m)==LivingEntityDifficulty.T2_MINIBOSS || + MonsterController.getLivingEntityDifficulty(m)==LivingEntityDifficulty.T3_MINIBOSS + ); + } + + protected void increaseBarTextScroll() { + scroll++; + switch (scroll%22) { + case 11:{ + arrow=" -"; + }break; + case 12:{ + arrow=" "; + }break; + case 13:{ + arrow="> "; + }break; + case 14:{ + arrow="->"; + }break; + } + } + + enum ShotMode { + NORMAL, + POISON, + BLEED + } +} diff --git a/src/sig/plugin/TwosideKeeper/TwosideKeeper.java b/src/sig/plugin/TwosideKeeper/TwosideKeeper.java index 70d692f..6566329 100644 --- a/src/sig/plugin/TwosideKeeper/TwosideKeeper.java +++ b/src/sig/plugin/TwosideKeeper/TwosideKeeper.java @@ -88,6 +88,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.block.Action; import org.bukkit.event.block.BlockBreakEvent; +import org.bukkit.event.block.BlockBurnEvent; import org.bukkit.event.block.BlockDispenseEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.SignChangeEvent; @@ -242,6 +243,7 @@ import sig.plugin.TwosideKeeper.HelperStructures.Common.RecipeCategory; import sig.plugin.TwosideKeeper.HelperStructures.Common.RecipeLinker; import sig.plugin.TwosideKeeper.HelperStructures.Effects.DarkSlash; import sig.plugin.TwosideKeeper.HelperStructures.Effects.EarthWaveTask; +import sig.plugin.TwosideKeeper.HelperStructures.Effects.EffectPool; import sig.plugin.TwosideKeeper.HelperStructures.Effects.HighlightCircle; import sig.plugin.TwosideKeeper.HelperStructures.Effects.LavaPlume; import sig.plugin.TwosideKeeper.HelperStructures.Effects.ReplaceBlockTask; @@ -274,6 +276,7 @@ import sig.plugin.TwosideKeeper.Monster.Dummy; import sig.plugin.TwosideKeeper.Monster.HellfireGhast; import sig.plugin.TwosideKeeper.Monster.Knight; import sig.plugin.TwosideKeeper.Monster.MonsterTemplate; +import sig.plugin.TwosideKeeper.Monster.SniperSkeleton; public class TwosideKeeper extends JavaPlugin implements Listener { @@ -507,6 +510,7 @@ public class TwosideKeeper extends JavaPlugin implements Listener { public static HashMap temporaryblocks = new HashMap(); public static List channels = new ArrayList(); public static List circles = new ArrayList(); + public static List effectpools = new ArrayList(); //public static stats StatCommand = new stats(); @@ -908,6 +912,13 @@ public class TwosideKeeper extends JavaPlugin implements Listener { } TwosideKeeper.HeartbeatLogger.AddEntry("Temporary Channel Handling", (int)(System.nanoTime()-time));time=System.nanoTime(); + for (EffectPool ep : effectpools) { + if (!ep.runTick()) { + ScheduleRemoval(effectpools,ep); + } + } + TwosideKeeper.HeartbeatLogger.AddEntry("Effect Pool Handling", (int)(System.nanoTime()-time));time=System.nanoTime(); + if ((int)(System.nanoTime()-totaltime)/1000000d>50) { TwosideKeeper.log("WARNING! Structure Handling took longer than 1 tick! "+((int)(System.nanoTime()-totaltime)/1000000d)+"ms", 0); } @@ -2026,6 +2037,15 @@ public class TwosideKeeper extends JavaPlugin implements Listener { m.setHealth(m.getMaxHealth()*0.3); }, 100);*/ }break; + case "SNIPERSKELETON":{ + LivingEntity m = MonsterController.convertLivingEntity((Skeleton)p.getWorld().spawnEntity(p.getLocation(),EntityType.SKELETON), + LivingEntityDifficulty.T1_MINIBOSS); + SniperSkeleton.randomlyConvertAsSniperSkeleton(m,true); + TwosideKeeper.custommonsters.put(m.getUniqueId(),new SniperSkeleton(m)); + Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{ + m.setHealth(m.getMaxHealth()*0.3); + }, 20); + }break; case "DAMAGETEST":{ LivingEntity m = MonsterController.convertLivingEntity((Skeleton)p.getWorld().spawnEntity(p.getLocation(),EntityType.SKELETON), LivingEntityDifficulty.T1_MINIBOSS); @@ -2077,6 +2097,9 @@ public class TwosideKeeper extends JavaPlugin implements Listener { }, beamDuration); } }break; + case "EFFECTPOOL":{ + new EffectPool(p.getLocation(),2,20*10,Color.fromRGB(255, 255, 0)); + }break; } } //LivingEntity m = MonsterController.convertMonster((Monster)p.getWorld().spawnEntity(p.getLocation(),EntityType.ZOMBIE), MonsterDifficulty.ELITE); @@ -4825,7 +4848,7 @@ public class TwosideKeeper extends JavaPlugin implements Listener { return Pronouns.ChoosePronoun(2)+" and died."; } case "FIRE_TICK": - case "FIRE": { + case "FIRE":{ if (Math.random()<0.5) { return "could not handle the "+Pronouns.ChoosePronoun(3)+" flames."; } else { @@ -4905,12 +4928,26 @@ public class TwosideKeeper extends JavaPlugin implements Listener { case "Grand Slam":{ return Pronouns.ChoosePronoun(19); } + case "Piercing Arrow":{ + return Pronouns.ChoosePronoun(20); + } + case "Burning Plume":{ + return Pronouns.ChoosePronoun(21); + } default:{ return "has died by "+pd.lasthitdesc; } } } + @EventHandler(priority=EventPriority.LOW,ignoreCancelled = true) + public void onBurn(BlockBurnEvent ev) { + Block b = ev.getBlock(); + if (TemporaryBlock.isTemporaryBlock(b)) { + ev.setCancelled(true); + } + } + @EventHandler(priority=EventPriority.LOW,ignoreCancelled = true) public void onSignChange(SignChangeEvent ev) { Player p = ev.getPlayer(); @@ -6930,12 +6967,12 @@ public class TwosideKeeper extends JavaPlugin implements Listener { GenericFunctions.removeNoDamageTick((LivingEntity)ev.getEntity(), ev.getDamager()); CustomDamage.ApplyDamage(pd.vendetta_amt, ev.getDamager(), (LivingEntity)ev.getEntity(), null, "Vendetta"); pd.vendetta_amt=0.0; - GenericFunctions.sendActionBarMessage(p, ChatColor.YELLOW+"Vendetta: "+ChatColor.GREEN+Math.round(pd.vendetta_amt)+" dmg stored",true); + //GenericFunctions.sendActionBarMessage(p, ChatColor.YELLOW+"Vendetta: "+ChatColor.GREEN+Math.round(pd.vendetta_amt)+" dmg stored",true); pd.customtitle.updateSideTitleStats(p); ev.setCancelled(true); } else { if (weapon.getType()==Material.AIR && pd.weaponUsedForShooting!=null) { - TwosideKeeper.log("Using weapon "+pd.weaponUsedForShooting+" as a substitute", 0); + //TwosideKeeper.log("Using weapon "+pd.weaponUsedForShooting+" as a substitute", 0); weapon=pd.weaponUsedForShooting.clone(); pd.weaponUsedForShooting=null; } @@ -9144,7 +9181,15 @@ public class TwosideKeeper extends JavaPlugin implements Listener { if (ev.getEntity() instanceof Projectile) { Projectile arr = (Projectile)ev.getEntity(); - if (arr.getShooter() instanceof Player) { + if (arr.getShooter() instanceof LivingEntity && + custommonsters.containsKey(((LivingEntity)(arr.getShooter())).getUniqueId())) { + LivingEntity ent = (LivingEntity)(arr.getShooter()); + CustomMonster cm = CustomMonster.getCustomMonster(ent); + cm.runProjectileLaunchEvent(ev); + } + + if (arr.getShooter() instanceof Player && + arr instanceof Arrow) { Player p = (Player)(arr.getShooter()); PlayerStructure pd = PlayerStructure.GetPlayerStructure(p); int slot = p.getInventory().getHeldItemSlot(); diff --git a/src/sig/plugin/TwosideKeeper/TwosideKeeperAPI.java b/src/sig/plugin/TwosideKeeper/TwosideKeeperAPI.java index 0b32ed3..850ed4e 100644 --- a/src/sig/plugin/TwosideKeeper/TwosideKeeperAPI.java +++ b/src/sig/plugin/TwosideKeeper/TwosideKeeperAPI.java @@ -17,6 +17,7 @@ import org.bukkit.inventory.ItemStack; import sig.plugin.TwosideKeeper.HelperStructures.ArtifactAbility; import sig.plugin.TwosideKeeper.HelperStructures.ArtifactItem; +import sig.plugin.TwosideKeeper.HelperStructures.BuffTemplate; import sig.plugin.TwosideKeeper.HelperStructures.Channel; import sig.plugin.TwosideKeeper.HelperStructures.CubeType; import sig.plugin.TwosideKeeper.HelperStructures.ItemSet; @@ -640,6 +641,9 @@ public final class TwosideKeeperAPI { public static boolean isBuffActive(LivingEntity l, String buffname) { return Buff.hasBuff(l,buffname); } + public static boolean isBuffActive(LivingEntity l, BuffTemplate buff) { + return Buff.hasBuff(l,buff); + } /** * Outputs all buffs a particular LivingEntity has to the console. */ @@ -649,6 +653,9 @@ public final class TwosideKeeperAPI { public static Buff getBuff(LivingEntity l, String buffname) { return Buff.getBuff(l, buffname); } + public static Buff getBuff(LivingEntity l, BuffTemplate buff) { + return Buff.getBuff(l, buff); + } /** * Returns a HashMap containing ALL buffs, expired or not that have been * applied to this LivingEntity. Try isBuffExpired() to check which ones are @@ -686,12 +693,33 @@ public final class TwosideKeeperAPI { public static void addBuff(LivingEntity l, String name, Buff buff, boolean stacking) { Buff.addBuff(l, name, buff, stacking); } + /** + * Attempts to add a buff to the player's buff data structure, overwriting the buff if it contains + * the same name. Note that the buff will not be added if the amplifier of the buff is less than what + * is currently applied, or the amplifier is equal but the duration is less. A new Buff data structure + * has to be created and filled in when calling this (use new Buff())

+ * + * This version of the method uses a BuffTemplate, which allows you to use an already defined setup for + * a Buff's appearance and display. + */ + public static void addBuff(LivingEntity l, long duration, int amplifier, BuffTemplate buff, boolean stacking) { + Buff.addBuff(l, duration, amplifier, buff, stacking); + } /** * Removes a buff, if possible. */ public static void removeBuff(LivingEntity l, String name) { Buff.removeBuff(l, name); } + /** + * Removes a buff, if possible. + * + * This version of the method uses a BuffTemplate, which allows you to use an already defined setup for + * a Buff's appearance and display. + */ + public static void removeBuff(LivingEntity l, BuffTemplate buff) { + Buff.removeBuff(l, buff); + } /** * Returns whether or not a buff can be removed. (If it's not permanent) */ diff --git a/src/sig/plugin/TwosideKeeper/runServerHeartbeat.java b/src/sig/plugin/TwosideKeeper/runServerHeartbeat.java index 246584e..f1f8602 100644 --- a/src/sig/plugin/TwosideKeeper/runServerHeartbeat.java +++ b/src/sig/plugin/TwosideKeeper/runServerHeartbeat.java @@ -44,6 +44,7 @@ import sig.plugin.TwosideKeeper.Events.InventoryUpdateEvent; import sig.plugin.TwosideKeeper.Events.InventoryUpdateEvent.UpdateReason; import sig.plugin.TwosideKeeper.HelperStructures.ArtifactAbility; import sig.plugin.TwosideKeeper.HelperStructures.BankSession; +import sig.plugin.TwosideKeeper.HelperStructures.BuffTemplate; import sig.plugin.TwosideKeeper.HelperStructures.DamageStructure; import sig.plugin.TwosideKeeper.HelperStructures.ItemSet; import sig.plugin.TwosideKeeper.HelperStructures.MonsterDifficulty; @@ -411,6 +412,12 @@ final class runServerHeartbeat implements Runnable { private void PerformPoisonTick(LivingEntity ent) { if (ent instanceof Player) { PlayerStructure pd = PlayerStructure.GetPlayerStructure((Player)ent); + if (TemporaryBlock.isInRangeOfSpecialBlock(ent.getLocation(), 5, "POISONPOOL")) { + Buff.addBuff(ent, 20*15, 1, BuffTemplate.POISON, true); + } else + if (TemporaryBlock.isInRangeOfSpecialBlock(ent.getLocation(), 5, "BLOODPOOL")) { + Buff.addBuff(ent, 20*15, 1, BuffTemplate.BLEEDING, true); + } if (Buff.hasBuff(ent, "Poison") && pd.lastPoisonTick+getPoisonTickDelay(ent)<=TwosideKeeper.getServerTickTime()) { Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{ if (ent!=null && Buff.hasBuff(ent, "Poison")) { @@ -437,7 +444,7 @@ final class runServerHeartbeat implements Runnable { //SoundUtils.playLocalSound((Player)ent, Sound.ENTITY_GENERIC_EXTINGUISH_FIRE, 1.0f, 1.0f); //ent.getWorld().spawnParticle(Particle.LAVA, ent.getEyeLocation(), CustomDamage.GetHeartAmount(Buff.getBuff(ent, "SHRAPNEL").getAmplifier())*5); Bukkit.getScheduler().runTaskLater(TwosideKeeper.plugin, ()->{ - if (ent!=null) { + if (ent!=null && ent.isValid() && Buff.hasBuff(ent, "BLEEDING")) { CustomDamage.ApplyDamage((Buff.getBuff(ent, "BLEEDING").getAmplifier()), null, ent, null, "Bleeding", CustomDamage.IGNOREDODGE|CustomDamage.TRUEDMG|CustomDamage.IGNORE_DAMAGE_TICK); } }, 10); //Bleeding DOT is twice as fast.