diff --git a/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/ArmorStandEffect.java b/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/ArmorStandEffect.java index caf4d503b..74e430355 100644 --- a/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/ArmorStandEffect.java +++ b/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/ArmorStandEffect.java @@ -1,6 +1,7 @@ package com.nisovin.magicspells.spelleffects.effecttypes; import org.bukkit.Location; +import org.bukkit.entity.Entity; import org.bukkit.entity.ArmorStand; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.EquipmentSlot; @@ -54,7 +55,6 @@ protected void loadFromConfig(ConfigurationSection config) { protected ArmorStand playArmorStandEffectLocation(Location location, SpellData data) { return entityData.spawn(location, data, ArmorStand.class, stand -> { stand.setSilent(true); - stand.addScoreboardTag(ENTITY_TAG); stand.setGravity(gravity.get(data)); if (disableSlots.get(data)) stand.setDisabledSlots(EquipmentSlot.values()); @@ -63,9 +63,14 @@ protected ArmorStand playArmorStandEffectLocation(Location location, SpellData d stand.setItem(EquipmentSlot.HAND, mainhandItem); stand.setItem(EquipmentSlot.OFF_HAND, offhandItem); }, stand -> { - stand.setPersistent(false); - Util.forEachPassenger(stand, e -> e.setPersistent(false)); + postSpawn(stand); + Util.forEachPassenger(stand, this::postSpawn); }); } + private void postSpawn(Entity entity) { + entity.setPersistent(false); + entity.addScoreboardTag(ENTITY_TAG); + } + } diff --git a/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/EntityEffect.java b/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/EntityEffect.java index 339135c84..91977d47b 100644 --- a/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/EntityEffect.java +++ b/core/src/main/java/com/nisovin/magicspells/spelleffects/effecttypes/EntityEffect.java @@ -44,14 +44,18 @@ protected void loadFromConfig(ConfigurationSection config) { @Override protected Entity playEntityEffectLocation(Location location, SpellData data) { return entityData.spawn(location, data, entity -> { - entity.addScoreboardTag(ENTITY_TAG); entity.setGravity(gravity.get(data)); }, entity -> { - entity.setPersistent(false); - Util.forEachPassenger(entity, e -> e.setPersistent(false)); + postSpawn(entity); + Util.forEachPassenger(entity, this::postSpawn); }); } + private void postSpawn(Entity entity) { + entity.setPersistent(false); + entity.addScoreboardTag(ENTITY_TAG); + } + @Override public Runnable playEffectLocation(Location location, SpellData data) { Entity entity = playEntityEffectLocation(location, data); diff --git a/core/src/main/java/com/nisovin/magicspells/spells/instant/ProjectileSpell.java b/core/src/main/java/com/nisovin/magicspells/spells/instant/ProjectileSpell.java index f8efc8e92..2ae55668c 100644 --- a/core/src/main/java/com/nisovin/magicspells/spells/instant/ProjectileSpell.java +++ b/core/src/main/java/com/nisovin/magicspells/spells/instant/ProjectileSpell.java @@ -21,7 +21,6 @@ import com.nisovin.magicspells.util.config.ConfigData; import com.nisovin.magicspells.spelleffects.SpellEffect; import com.nisovin.magicspells.castmodifiers.ModifierSet; -import com.nisovin.magicspells.util.config.ConfigDataUtil; import com.nisovin.magicspells.spelleffects.EffectPosition; import com.nisovin.magicspells.spells.TargetedLocationSpell; import com.nisovin.magicspells.util.trackers.ProjectileTracker; @@ -70,6 +69,7 @@ public class ProjectileSpell extends InstantSpell implements TargetedLocationSpe private final ConfigData projectileName; private final ConfigData arrowColor; + private final ConfigData potionColor; private Subspell hitSpell; private Subspell tickSpell; @@ -122,7 +122,8 @@ public ProjectileSpell(MagicConfig config, String spellName) { projectileName = getConfigDataComponent("projectile-name", null); - arrowColor = ConfigDataUtil.getColor(config.getMainConfig(), internalKey + "arrow-color", null); + arrowColor = getConfigDataColor("arrow-color", null); + potionColor = getConfigDataColor("potion-color", null); projectileModifiersStrings = getConfigStringList("projectile-modifiers", null); } @@ -221,6 +222,7 @@ private void setupTracker(ProjectileTracker tracker, SpellData data) { tracker.setProjectileName(projectileName.get(data)); tracker.setArrowColor(arrowColor.get(data)); + tracker.setPotionColor(potionColor.get(data)); tracker.setHitSpell(hitSpell); tracker.setTickSpell(tickSpell); diff --git a/core/src/main/java/com/nisovin/magicspells/spells/targeted/HomingProjectileSpell.java b/core/src/main/java/com/nisovin/magicspells/spells/targeted/HomingProjectileSpell.java index cdb496686..c531ad48f 100644 --- a/core/src/main/java/com/nisovin/magicspells/spells/targeted/HomingProjectileSpell.java +++ b/core/src/main/java/com/nisovin/magicspells/spells/targeted/HomingProjectileSpell.java @@ -25,7 +25,6 @@ import com.nisovin.magicspells.castmodifiers.ModifierSet; import com.nisovin.magicspells.events.SpellPreImpactEvent; import com.nisovin.magicspells.spells.TargetedEntitySpell; -import com.nisovin.magicspells.util.config.ConfigDataUtil; import com.nisovin.magicspells.spelleffects.EffectPosition; import com.nisovin.magicspells.util.projectile.ProjectileManager; import com.nisovin.magicspells.util.projectile.ProjectileManagers; @@ -63,6 +62,7 @@ public class HomingProjectileSpell extends TargetedSpell implements TargetedEnti private final ConfigData projectileName; private final ConfigData arrowColor; + private final ConfigData potionColor; private final String hitSpellName; private final String airSpellName; @@ -86,7 +86,8 @@ public HomingProjectileSpell(MagicConfig config, String spellName) { projectileType = getConfigDataString("projectile-type", "arrow"); - arrowColor = ConfigDataUtil.getColor(config.getMainConfig(), internalKey + "arrow-color", null); + arrowColor = getConfigDataColor("arrow-color", null); + potionColor = getConfigDataColor("potion-color", null); relativeOffset = getConfigDataVector("relative-offset", new Vector(0.5, 0.5, 0)); targetRelativeOffset = getConfigDataVector("target-relative-offset", new Vector(0, 0.5, 0)); @@ -279,7 +280,7 @@ private HomingProjectileMonitor(SpellData data) { if (proj instanceof WitherSkull witherSkull) witherSkull.setCharged(charged.get(finalData)); if (proj instanceof Explosive explosive) explosive.setIsIncendiary(incendiary.get(finalData)); if (proj instanceof ProjectileManagerThrownPotion potion) { - ((ThrownPotion) proj).setItem(potion.getItem()); + ((ThrownPotion) proj).setItem(potion.getPotion(potionColor.get(finalData))); } }); diff --git a/core/src/main/java/com/nisovin/magicspells/spells/targeted/ParticleCloudSpell.java b/core/src/main/java/com/nisovin/magicspells/spells/targeted/ParticleCloudSpell.java index 8936c2454..99fa0c0e4 100644 --- a/core/src/main/java/com/nisovin/magicspells/spells/targeted/ParticleCloudSpell.java +++ b/core/src/main/java/com/nisovin/magicspells/spells/targeted/ParticleCloudSpell.java @@ -1,12 +1,14 @@ package com.nisovin.magicspells.spells.targeted; +import java.util.Map; +import java.util.UUID; import java.util.List; +import java.util.HashMap; -import org.bukkit.Color; -import org.bukkit.Material; -import org.bukkit.Location; -import org.bukkit.Particle; +import org.bukkit.*; import org.bukkit.util.Vector; +import org.bukkit.entity.Entity; +import org.bukkit.event.EventHandler; import org.bukkit.entity.LivingEntity; import org.bukkit.inventory.ItemStack; import org.bukkit.potion.PotionEffect; @@ -14,6 +16,7 @@ import org.bukkit.block.data.BlockData; import org.bukkit.entity.AreaEffectCloud; import org.bukkit.Particle.DustTransition; +import org.bukkit.event.entity.AreaEffectCloudApplyEvent; import org.jetbrains.annotations.NotNull; @@ -23,12 +26,17 @@ import com.nisovin.magicspells.MagicSpells; import com.nisovin.magicspells.spells.TargetedSpell; import com.nisovin.magicspells.util.config.ConfigData; +import com.nisovin.magicspells.events.SpellTargetEvent; import com.nisovin.magicspells.spells.TargetedEntitySpell; import com.nisovin.magicspells.util.config.ConfigDataUtil; import com.nisovin.magicspells.spells.TargetedLocationSpell; +import com.destroystokyo.paper.event.entity.EntityRemoveFromWorldEvent; + public class ParticleCloudSpell extends TargetedSpell implements TargetedLocationSpell, TargetedEntitySpell { + private final Map clouds = new HashMap<>(); + private final ConfigData relativeOffset; private final ConfigData customName; @@ -67,6 +75,8 @@ public class ParticleCloudSpell extends TargetedSpell implements TargetedLocatio private final ConfigData canTargetEntities; private final ConfigData canTargetLocation; + private final boolean removeCloud; + private final List> potionEffects; public ParticleCloudSpell(MagicConfig config, String spellName) { @@ -129,6 +139,8 @@ public ParticleCloudSpell(MagicConfig config, String spellName) { radiusOnUse = getConfigDataFloat("radius-on-use", 0F); radiusPerTick = getConfigDataFloat("radius-per-tick", 0F); + removeCloud = getConfigBoolean("remove-cloud", false); + useGravity = getConfigDataBoolean("use-gravity", false); canTargetEntities = getConfigDataBoolean("can-target-entities", true); canTargetLocation = getConfigDataBoolean("can-target-location", true); @@ -136,6 +148,17 @@ public ParticleCloudSpell(MagicConfig config, String spellName) { potionEffects = Util.getPotionEffects(getConfigList("potion-effects", null), internalName); } + @Override + protected void turnOff() { + if (removeCloud) { + for (UUID uuid : clouds.keySet()) { + Entity cloud = Bukkit.getEntity(uuid); + if (cloud != null) cloud.remove(); + } + } + clouds.clear(); + } + @Override public CastResult cast(SpellData data) { if (canTargetEntities.get(data)) { @@ -203,12 +226,30 @@ private CastResult spawnCloud(SpellData data) { cloud.customName(customName); cloud.setCustomNameVisible(true); } + + clouds.put(cloud.getUniqueId(), finalData); }); playSpellEffects(data); return new CastResult(PostCastAction.HANDLE_NORMALLY, data); } + @EventHandler(ignoreCancelled = true) + public void onEffectApply(AreaEffectCloudApplyEvent event) { + SpellData data = clouds.get(event.getEntity().getUniqueId()); + if (data == null) return; + + event.getAffectedEntities().removeIf(entity -> { + SpellTargetEvent targetEvent = new SpellTargetEvent(this, data.target(entity)); + return !targetEvent.callEvent(); + }); + } + + @EventHandler(ignoreCancelled = true) + public void onRemove(EntityRemoveFromWorldEvent event) { + clouds.remove(event.getEntity().getUniqueId()); + } + private Object getParticleData(@NotNull Particle particle, @NotNull SpellData data) { Object nmsData = MagicSpells.getVolatileCodeHandler().getVolatileParticleData( particle, diff --git a/core/src/main/java/com/nisovin/magicspells/util/EntityData.java b/core/src/main/java/com/nisovin/magicspells/util/EntityData.java index 161f9b73c..132e30393 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/EntityData.java +++ b/core/src/main/java/com/nisovin/magicspells/util/EntityData.java @@ -65,6 +65,7 @@ public class EntityData { private final Multimap> options = MultimapBuilder.enumKeys(EntityType.class).arrayListValues().build(); private final List delayedEntityData = new ArrayList<>(); + private final List passengers = new ArrayList<>(); private ConfigData entityType; @@ -797,14 +798,7 @@ public EntityData(ConfigurationSection config, boolean forceOptional) { // Passengers for (Object object : config.getList("passengers", new ArrayList<>())) { if (!(object instanceof Map map)) continue; - EntityData passengerData = new EntityData(ConfigReaderUtil.mapToSection(map)); - - transformers.put(Entity.class, (Entity entity, SpellData data) -> { - passengerData.spawn(entity.getLocation(), data, passenger -> { - entity.addPassenger(passenger); - passenger.getPersistentDataContainer().set(MS_PASSENGER, PersistentDataType.BOOLEAN, true); - }); - }); + passengers.add(new EntityData(ConfigReaderUtil.mapToSection(map))); } // Mob Goals @@ -956,6 +950,13 @@ public T spawn(@NotNull Location location, @NotNull SpellData spawnLocation.setPitch(pitch.get(data).apply(spawnLocation.getPitch())); return spawnLocation.getWorld().spawn(spawnLocation, entityClass, entity -> { + for (EntityData passengerData : passengers) { + passengerData.spawn(entity.getLocation(), data, passenger -> { + entity.addPassenger(passenger); + passenger.getPersistentDataContainer().set(MS_PASSENGER, PersistentDataType.BOOLEAN, true); + }); + } + if (preConsumer != null) preConsumer.accept(entity); apply(entity, data); diff --git a/core/src/main/java/com/nisovin/magicspells/util/managers/BuffManager.java b/core/src/main/java/com/nisovin/magicspells/util/managers/BuffManager.java index c2bbd1ec6..e9fced70c 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/managers/BuffManager.java +++ b/core/src/main/java/com/nisovin/magicspells/util/managers/BuffManager.java @@ -1,9 +1,6 @@ package com.nisovin.magicspells.util.managers; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.HashMap; +import java.util.*; import java.util.function.Consumer; import com.google.common.collect.SetMultimap; @@ -83,18 +80,16 @@ public void stop() { public void accept(ScheduledTask scheduledTask) { NoMagicZoneManager zoneManager = MagicSpells.getNoMagicZoneManager(); - activeBuffs.entries().removeIf(entry -> { + for (Map.Entry entry : new HashSet<>(activeBuffs.entries())) { UUID uuid = entry.getKey(); LivingEntity entity = Bukkit.getEntity(uuid) instanceof LivingEntity le ? le : lastEntity.get(uuid); BuffSpell buff = entry.getValue(); if ((entity instanceof Player || entity.isValid()) && !buff.isExpired(entity) && !zoneManager.willFizzle(entity, buff)) - return false; + continue; - buff.turnOff(entity, false); - new BuffEndEvent(entity, buff).callEvent(); - return true; - }); + endBuff(entity, buff); + } } @EventHandler diff --git a/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerLingeringPotion.java b/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerLingeringPotion.java index 63e853735..c21ea392a 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerLingeringPotion.java +++ b/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerLingeringPotion.java @@ -7,12 +7,10 @@ public class ProjectileManagerLingeringPotion extends ProjectileManagerThrownPotion { - private static final ItemStack POTION = new ItemStack(Material.LINGERING_POTION); - @NotNull @Override - public ItemStack getItem() { - return POTION; + protected ItemStack getItem() { + return new ItemStack(Material.LINGERING_POTION); } } diff --git a/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerSplashPotion.java b/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerSplashPotion.java index 4b505dbc6..3823cfddd 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerSplashPotion.java +++ b/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerSplashPotion.java @@ -7,12 +7,10 @@ public class ProjectileManagerSplashPotion extends ProjectileManagerThrownPotion { - private static final ItemStack POTION = new ItemStack(Material.SPLASH_POTION); - @NotNull @Override - public ItemStack getItem() { - return POTION; + protected ItemStack getItem() { + return new ItemStack(Material.SPLASH_POTION); } } diff --git a/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerThrownPotion.java b/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerThrownPotion.java index 82bd45a20..e07740dec 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerThrownPotion.java +++ b/core/src/main/java/com/nisovin/magicspells/util/projectile/ProjectileManagerThrownPotion.java @@ -1,24 +1,32 @@ package com.nisovin.magicspells.util.projectile; +import org.bukkit.Color; import org.bukkit.Material; import org.bukkit.entity.Projectile; import org.bukkit.entity.ThrownPotion; import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.PotionMeta; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class ProjectileManagerThrownPotion extends ProjectileManager { - private static final ItemStack POTION = new ItemStack(Material.POTION); - @Override public Class getProjectileClass() { return ThrownPotion.class; } @NotNull - public ItemStack getItem() { - return POTION; + protected ItemStack getItem() { + return new ItemStack(Material.POTION); + } + + @NotNull + public final ItemStack getPotion(@Nullable Color color) { + ItemStack potion = getItem(); + if (color != null) potion.editMeta(PotionMeta.class, p -> p.setColor(color)); + return potion; } } diff --git a/core/src/main/java/com/nisovin/magicspells/util/trackers/ProjectileTracker.java b/core/src/main/java/com/nisovin/magicspells/util/trackers/ProjectileTracker.java index b873fc227..83629da93 100644 --- a/core/src/main/java/com/nisovin/magicspells/util/trackers/ProjectileTracker.java +++ b/core/src/main/java/com/nisovin/magicspells/util/trackers/ProjectileTracker.java @@ -71,6 +71,7 @@ public class ProjectileTracker implements Runnable, Tracker { private Component projectileName; private Color arrowColor; + private Color potionColor; private Subspell hitSpell; private Subspell tickSpell; @@ -138,7 +139,7 @@ public void initialize() { if (proj instanceof WitherSkull witherSkull) witherSkull.setCharged(charged); if (proj instanceof Explosive explosive) explosive.setIsIncendiary(incendiary); if (projectileManager instanceof ProjectileManagerThrownPotion potion) { - ((ThrownPotion) proj).setItem(potion.getItem()); + ((ThrownPotion) proj).setItem(potion.getPotion(potionColor)); } }); @@ -512,6 +513,14 @@ public void setArrowColor(Color arrowColor) { this.arrowColor = arrowColor; } + public Color getPotionColor() { + return potionColor; + } + + public void setPotionColor(Color potionColor) { + this.potionColor = potionColor; + } + public boolean shouldStopOnModifierFail() { return stopOnModifierFail; }