diff --git a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties index 6025635..00e3149 100644 --- a/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties +++ b/Commons/core/src/main/i18n/templates/pgm/PGMMessages.properties @@ -198,6 +198,7 @@ mutation.type.explosive = Explosive mutation.type.explosive.desc = tnt and fire bows mutation.type.elytra = Elytra mutation.type.elytra.desc = fly around with an elytra +mutation.type.elytra.land = Land immediately. Your elytra is being grounded in {0} seconds. mutation.type.projectile = Projectile mutation.type.projectile.desc = arrow potion effects mutation.type.enchantment = Enchantment @@ -268,6 +269,9 @@ lives.remaining.individual.plural = You have {0} lives left lives.remaining.team.singular = Your team has {0} life left lives.remaining.team.plural = Your team has {0} lives left +lives.remaining.alive.singular = Your team has {0} player left +lives.remaining.alive.plural = Your team has {0} players left + lives.status.eliminated = eliminated lives.status.alive = {0} alive lives.status.lives = {0} lives diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java index cd7bad7..8f0ae0c 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/BlitzMatchModuleImpl.java @@ -105,7 +105,11 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul } } - private void showLives(MatchPlayer player, boolean release, boolean activate) { + private void livesHotbar(MatchPlayer player) { + lives(player).map(Lives::remaining).ifPresent(player::sendHotbarMessage); + } + + private void livesTitle(MatchPlayer player, boolean release, boolean activate) { final Optional lives = lives(player); if(activated() && lives.isPresent()) { player.showTitle( @@ -118,6 +122,10 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul } } + private void update() { + match.callEvent(new BlitzEvent(match, this)); + } + @Override public boolean activated() { return activated; @@ -142,6 +150,7 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul activated = false; lives.clear(); eliminated.clear(); + update(); } @Override @@ -161,10 +170,10 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul match.participants().forEach(player -> { setup(player, false); if(match.hasStarted()) { - showLives(player, false, true); + livesTitle(player, false, true); } }); - match.callEvent(new BlitzEvent(match, this)); + update(); } } @@ -181,6 +190,7 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul if(notify) { player.showTitle(Components.blank(), life.change(lives), 0, 40, 10); } + player.competitor().ifPresent(competitor -> competitor.participants().forEach(this::livesHotbar)); if(life.empty() && immediate) { eliminate(player); return true; @@ -225,9 +235,9 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul ImmutableSet.copyOf(getMatch().getParticipatingPlayers()) .stream() .filter(this::eliminated) - .forEach(participating -> { - match.setPlayerParty(participating, match.getDefaultParty()); - world.spawnParticle(Particle.SMOKE_LARGE, player.getLocation(), 5); + .forEach(eliminated -> { + match.setPlayerParty(eliminated, match.getDefaultParty()); + world.spawnParticle(Particle.SMOKE_LARGE, eliminated.getLocation(), 5); }); victory.invalidateAndCheckEnd(); }); @@ -269,15 +279,14 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul @EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true) public void onDeath(MatchPlayerDeathEvent event) { - final MatchPlayer player = event.getVictim(); - if(player.competitor().isPresent()) { - increment(player, -1, false, true); - } + event.getVictim() + .competitor() + .ifPresent(competitor -> increment(event.getVictim(), -1, false, true)); } @EventHandler public void onRelease(ParticipantReleaseEvent event) { - showLives(event.getPlayer(), event.wasFrozen(), false); + livesTitle(event.getPlayer(), event.wasFrozen(), false); } } diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java index 77d7154..77d2482 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesBase.java @@ -27,7 +27,7 @@ public abstract class LivesBase implements Lives { update(); } - private void update() { + protected void update() { competitor().getMatch().callEvent(new LivesEvent(this)); } @@ -67,7 +67,8 @@ public abstract class LivesBase implements Lives { public BaseComponent remaining() { return new Component( new TranslatableComponent( - "lives.remaining." + type().name().toLowerCase() + "." + (current() == 1 ? "singular" : "plural"), + "lives.remaining." + type().name().toLowerCase() + "." + (current() == 1 ? "singular" + : "plural"), new Component(current(), ChatColor.YELLOW) ), ChatColor.AQUA @@ -103,7 +104,7 @@ public abstract class LivesBase implements Lives { new TranslatableComponent( (delta > 0 ? "lives.change.gained." : "lives.change.lost.") + (absDelta == 1 ? "singular" - : "plural"), + : "plural"), new Component(absDelta, ChatColor.AQUA) ), ChatColor.WHITE diff --git a/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java b/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java index f989a87..ade9827 100644 --- a/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java +++ b/PGM/src/main/java/tc/oc/pgm/blitz/LivesTeam.java @@ -1,6 +1,11 @@ package tc.oc.pgm.blitz; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TranslatableComponent; import tc.oc.api.docs.PlayerId; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.Components; import tc.oc.pgm.match.Competitor; public class LivesTeam extends LivesBase { @@ -24,6 +29,24 @@ public class LivesTeam extends LivesBase { return false; } + public int alive() { + return (int) competitor().participants().count(); + } + + @Override + public BaseComponent remaining() { + int alive = alive() - 1; + if(alive == 0) return Components.blank(); + return empty() ? new Component( + new TranslatableComponent( + "lives.remaining.alive." + (alive == 1 ? "singular" + : "plural"), + new Component(alive, ChatColor.YELLOW) + ), + ChatColor.AQUA + ) : super.remaining(); + } + @Override public int hashCode() { return competitor().hashCode(); diff --git a/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java b/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java index 500ae8b..e4bf86c 100644 --- a/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/modules/MobsMatchModule.java @@ -1,6 +1,7 @@ package tc.oc.pgm.modules; import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.CreatureSpawnEvent; import tc.oc.pgm.events.ListenerScope; @@ -9,7 +10,6 @@ import tc.oc.pgm.filters.Filter; import tc.oc.pgm.filters.query.EntitySpawnQuery; import tc.oc.pgm.match.MatchModule; import tc.oc.pgm.match.MatchScope; -import tc.oc.pgm.mutation.MutationMatchModule; @ListenerScope(MatchScope.LOADED) public class MobsMatchModule extends MatchModule implements Listener { @@ -37,10 +37,9 @@ public class MobsMatchModule extends MatchModule implements Listener { getMatch().getWorld().setSpawnFlags(false, false); } - @EventHandler(ignoreCancelled = true) + @EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST) public void checkSpawn(final CreatureSpawnEvent event) { - if(!match.module(MutationMatchModule.class).map(mmm -> mmm.allowMob(event.getSpawnReason())).orElse(false) && - event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) { + if(event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) { event.setCancelled(mobsFilter.query(new EntitySpawnQuery(event, event.getEntity(), event.getSpawnReason())).isDenied()); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java index db3046f..baf8ef7 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/MutationMatchModule.java @@ -4,7 +4,6 @@ import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Sets; -import org.bukkit.event.entity.CreatureSpawnEvent; import tc.oc.commons.core.util.MapUtils; import tc.oc.commons.core.random.RandomUtils; import tc.oc.pgm.Config; @@ -157,17 +156,4 @@ public class MutationMatchModule extends MatchModule { return mutationsActive().stream().anyMatch(m1 -> Stream.of(mutations).anyMatch(m2 -> m2.equals(m1))); } - public boolean allowMob(CreatureSpawnEvent.SpawnReason reason) { - switch(reason) { - case NATURAL: - case DEFAULT: - case CHUNK_GEN: - case JOCKEY: - case MOUNT: - return false; - default: - return true; - } - } - } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java new file mode 100644 index 0000000..19e534c --- /dev/null +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/EntityMutation.java @@ -0,0 +1,239 @@ +package tc.oc.pgm.mutation.types; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import com.google.gson.reflect.TypeToken; +import org.bukkit.Location; +import org.bukkit.entity.Entity; +import org.bukkit.entity.LivingEntity; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.CreatureSpawnEvent; +import org.bukkit.event.player.PlayerSpawnEntityEvent; +import org.bukkit.inventory.EntityEquipment; +import tc.oc.commons.core.collection.WeakHashSet; +import tc.oc.pgm.events.MatchPlayerDeathEvent; +import tc.oc.pgm.events.PlayerChangePartyEvent; +import tc.oc.pgm.match.Match; +import tc.oc.pgm.match.MatchPlayer; + +import javax.annotation.Nullable; +import java.time.Instant; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.stream.Stream; + +import static tc.oc.commons.core.util.Optionals.cast; + +/** + * A mutation module that tracks entity spawns. + */ +public class EntityMutation extends KitMutation { + + final Set entities; + final Map> entitiesByTime; + final Map> entitiesByPlayer; + final Map playersByEntity; + + public EntityMutation(Match match, boolean force) { + super(match, force); + this.entities = new WeakHashSet<>(); + this.entitiesByTime = new WeakHashMap<>(); + this.entitiesByPlayer = new WeakHashMap<>(); + this.playersByEntity = new WeakHashMap<>(); + } + + /** + * Gets an immutable stream of all registered entities. + * @return stream of entities. + */ + public Stream entities() { + return ImmutableSet.copyOf(entities).stream(); + } + + /** + * Gets an immutable stream of all registered entities + * by the time they spawned in ascending order. + * @return stream of entities. + */ + public Stream entitiesByTime() { + return ImmutableMap.copyOf(entitiesByTime) + .entrySet() + .stream() + .sorted(Map.Entry.comparingByKey(Comparator.comparing(Instant::toEpochMilli))) + .flatMap(entry -> ImmutableSet.copyOf(entry.getValue()).stream()); + } + + /** + * Gets an immutable stream of all registered entities + * that are owned by a player. + * + * If the given player is null, this will return entities + * with no owner. + * @param player the optional player. + * @return stream of entities. + */ + public Stream entitiesByPlayer(@Nullable MatchPlayer player) { + return ImmutableSet.copyOf(entitiesByPlayer.getOrDefault(player, new HashSet<>())).stream(); + } + + /** + * Gets the optional owner of an entity. + * @param entity the entity to find the owner of. + * @return the optional player. + */ + public Optional playerByEntity(Entity entity) { + return Optional.ofNullable(playersByEntity.get(entity)); + } + + /** + * Are entities with this spawn reason allowed to spawn? + * @param reason the spawn reason. + * @return whether this reason is allowed. + */ + public boolean allowed(CreatureSpawnEvent.SpawnReason reason) { + switch(reason) { + case NATURAL: + case DEFAULT: + case CHUNK_GEN: + case JOCKEY: + case MOUNT: + return false; + default: + return true; + } + } + + /** + * Register a new spawned entity with an optional owner. + * @param entity the entity to register. + * @param owner the optional owner. + * @return the entity. + */ + public E register(E entity, @Nullable MatchPlayer owner) { + entities.add(entity); + final Instant now = match().getInstantNow(); + final Set byTime = entitiesByTime.getOrDefault(now, new WeakHashSet<>()); + byTime.add(entity); + entitiesByTime.put(now, byTime); + if(owner != null) { + final Set byPlayer = entitiesByPlayer.getOrDefault(owner, new WeakHashSet<>()); + byPlayer.add(entity); + entitiesByPlayer.put(owner, byPlayer); + playersByEntity.put(entity, owner); + } + return entity; + } + + /** + * Removes the entity from the world. + * + * Typically this should be {@link Entity#remove()}, + * but it can also expire the entity after a couple of seconds. + * @param entity the entity to remove. + */ + public void remove(E entity) { + entity.remove(); + } + + /** + * Unregister and remove the given entity. + * @param entity the entity. + */ + public void despawn(E entity) { + entities.remove(entity); + playersByEntity.remove(entity); + Stream.of(entitiesByTime, entitiesByPlayer) + .flatMap(map -> ImmutableList.copyOf(map.values()).stream()) + .forEach(set -> set.remove(entity)); + remove(entity); + } + + /** + * Unregister and remove any entities owned by the given player. + * @param player the owner of the entities. + */ + public void despawn(MatchPlayer player) { + ImmutableSet.copyOf(entitiesByPlayer.getOrDefault(player, new HashSet<>())).forEach(this::despawn); + } + + /** + * Spawn an entity at the given location with no owner. + * @see #spawn(Location, Class, MatchPlayer) + * @return the entity. + */ + public E spawn(Location location, Class entityClass) { + return spawn(location, entityClass, null); + } + + /** + * Spawn an entity at the given location. + * @param location the location to spawn the entity. + * @param entityClass the class of the entity. + * @param owner the optional owner of the entity. + * @return the entity. + */ + public E spawn(Location location, Class entityClass, @Nullable MatchPlayer owner) { + E entity = world().spawn(location, entityClass); + cast(entity, LivingEntity.class).ifPresent(living -> { + living.setCanPickupItems(false); + living.setRemoveWhenFarAway(true); + EntityEquipment equipment = living.getEquipment(); + equipment.setHelmetDropChance(0); + equipment.setChestplateDropChance(0); + equipment.setLeggingsDropChance(0); + equipment.setBootsDropChance(0); + }); + return register(entity, owner); + } + + @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST) + public void onPlayerSpawnEntity(PlayerSpawnEntityEvent event) { + match().participant(event.getPlayer()) + .ifPresent(player -> cast(event.getEntity(), new TypeToken(){}.getRawType()) + .ifPresent(entity -> { + register((E) entity, player); + event.setCancelled(false); + })); + } + + @EventHandler(ignoreCancelled = false, priority = EventPriority.HIGHEST) + public void onEntitySpawn(CreatureSpawnEvent event) { + boolean allowed = allowed(event.getSpawnReason()); + event.setCancelled(!allowed); + if(allowed) { + cast(event.getEntity(), new TypeToken(){}.getRawType()) + .filter(entity -> !playerByEntity((E) entity).isPresent()) + .ifPresent(entity -> register((E) entity, null)); + } + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPlayerDeath(MatchPlayerDeathEvent event) { + despawn(event.getVictim()); + } + + @EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR) + public void onPartyChange(PlayerChangePartyEvent event) { + despawn(event.getPlayer()); + } + + @Override + public void remove(MatchPlayer player) { + despawn(player); + super.remove(player); + } + + @Override + public void disable() { + entities().forEach(this::despawn); + Stream.of(entitiesByTime, entitiesByPlayer, playersByEntity).forEach(Map::clear); + super.disable(); + } + +} diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java index 813022b..18dd1e6 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/KitMutation.java @@ -1,9 +1,16 @@ package tc.oc.pgm.mutation.types; +import org.bukkit.Material; +import org.bukkit.entity.Item; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.entity.ItemSpawnEvent; import org.bukkit.inventory.ItemStack; import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.commons.core.util.Optionals; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.killreward.KillRewardMatchModule; +import tc.oc.pgm.kits.ItemKit; import tc.oc.pgm.kits.Kit; import tc.oc.pgm.kits.KitPlayerFacet; import tc.oc.pgm.match.Match; @@ -11,19 +18,22 @@ import tc.oc.pgm.match.MatchPlayer; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.WeakHashMap; import java.util.stream.Stream; /** * A mutation module that injects special kits on spawn and/or kill. */ -public class KitMutation extends MutationModule { +public class KitMutation extends MutationModule.Impl { protected final List kits; protected final Map> playerKits; protected final Map> savedSlots; + protected final Set itemRemove; protected final List rewards; protected final boolean force; @@ -32,6 +42,7 @@ public class KitMutation extends MutationModule { this.kits = new ArrayList<>(); this.playerKits = new WeakHashMap<>(); this.savedSlots = new WeakHashMap<>(); + this.itemRemove = new HashSet<>(); this.rewards = new ArrayList<>(); this.force = force; } @@ -59,6 +70,7 @@ public class KitMutation extends MutationModule { List kits = new ArrayList<>(); kits(player, kits); playerKits.put(player, kits); + kits.forEach(kit -> Optionals.cast(kit, ItemKit.class).ifPresent(items -> itemRemove.add(items.item().getType()))); saved().forEach(slot -> { slot.item(player.getInventory()).ifPresent(item -> { Map slots = savedSlots.getOrDefault(player, new HashMap<>()); @@ -88,22 +100,34 @@ public class KitMutation extends MutationModule { return Stream.empty(); } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) + public void onItemDrop(ItemSpawnEvent event) { + Item entity = event.getEntity(); + if(entity != null) { + ItemStack item = entity.getItemStack(); + if(item != null && itemRemove.contains(item.getType())) { + entity.remove(); + } + } + } + @Override public void enable() { super.enable(); - match.module(KillRewardMatchModule.class).get().rewards().addAll(rewards); - if(match.hasStarted()) { - match.participants().forEach(this::apply); + match().module(KillRewardMatchModule.class).get().rewards().addAll(rewards); + if(match().hasStarted()) { + match().participants().forEach(this::apply); } } @Override public void disable() { - match.module(KillRewardMatchModule.class).get().rewards().removeAll(rewards); - match.participants().forEach(this::remove); + match().module(KillRewardMatchModule.class).get().rewards().removeAll(rewards); + match().participants().forEach(this::remove); kits.clear(); playerKits.clear(); savedSlots.clear(); + itemRemove.clear(); rewards.clear(); super.disable(); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java index 712073f..b3458d6 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/MutationModule.java @@ -1,12 +1,8 @@ package tc.oc.pgm.mutation.types; -import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.World; -import org.bukkit.entity.Entity; -import org.bukkit.entity.LivingEntity; import org.bukkit.event.Listener; -import org.bukkit.inventory.EntityEquipment; import org.bukkit.inventory.ItemStack; import tc.oc.commons.bukkit.item.ItemBuilder; import tc.oc.commons.core.random.AdvancingEntropy; @@ -15,7 +11,6 @@ import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.mutation.Mutation; -import tc.oc.pgm.mutation.MutationMatchModule; import java.util.Random; @@ -28,69 +23,59 @@ import java.util.Random; * of breaking the match state. */ @ListenerScope(MatchScope.RUNNING) -public abstract class MutationModule implements Listener { +public interface MutationModule extends Listener { - protected final Match match; - protected final World world; - protected final Entropy entropy; - protected final Random random; + Match match(); - /** - * Constructed when {@link MutationMatchModule#load()} - * has been called. This will only be constructed if its - * subsequent {@link Mutation} is enabled for the match. - * - * @param match the match for this module. - */ - public MutationModule(Match match) { - this.match = match; - this.world = match.getWorld(); - this.entropy = new AdvancingEntropy(); - this.random = new Random(); + default void enable() { + match().registerEventsAndRepeatables(this); } - /** - * Called when the match starts. - * - * However, this should be able to be called at any - * point before the match ends and still work as expected. - */ - public void enable() { - match.registerEventsAndRepeatables(this); + default void disable() { + match().unregisterEvents(this); + match().unregisterRepeatable(this); } - /** - * Called when the match ends. - * - * However, this should be able to be called at any - * point during a match and still work as expected. - */ - public void disable() { - match.unregisterEvents(this); - match.unregisterRepeatable(this); + default World world() { + return match().getWorld(); } - protected static ItemStack item(Material material, int amount) { - return new ItemBuilder().material(material).amount(amount).unbreakable(true).shareable(false).get(); + default Entropy entropy() { + return match().entropyForTick(); } - protected static ItemStack item(Material material) { - return item(material, 1); + default Random random() { + return match().getRandom(); } - protected E spawn(Location location, Class entityClass) { - E entity = world.spawn(location, entityClass); - if(entity instanceof LivingEntity) { - LivingEntity living = (LivingEntity) entity; - living.setCanPickupItems(false); - living.setRemoveWhenFarAway(true); - EntityEquipment equipment = living.getEquipment(); - equipment.setHelmetDropChance(0); - equipment.setChestplateDropChance(0); - equipment.setLeggingsDropChance(0); - equipment.setBootsDropChance(0); + abstract class Impl implements MutationModule { + + private final Match match; + private final Entropy entropy; + + public Impl(final Match match) { + this.match = match; + this.entropy = new AdvancingEntropy(match.entropyForTick().randomLong()); } - return entity; + + @Override + public Match match() { + return match; + } + + @Override + public Entropy entropy() { + return entropy; + } + + protected static ItemStack item(Material material, int amount) { + return new ItemBuilder().material(material).amount(amount).unbreakable(true).shareable(false).get(); + } + + protected static ItemStack item(Material material) { + return item(material, 1); + } + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java index 96f61f7..5e7371a 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/TargetMutation.java @@ -1,64 +1,114 @@ package tc.oc.pgm.mutation.types; import java.time.Duration; +import java.time.Instant; import java.util.List; -import tc.oc.commons.core.scheduler.Task; import tc.oc.commons.core.stream.Collectors; +import tc.oc.commons.core.util.TimeUtils; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.match.Repeatable; +import tc.oc.time.Time; /** * A mutation module that executes a task on random {@link MatchPlayer}s. */ -public abstract class TargetMutation extends MutationModule { - - private final Duration frequency; - private Task task; - - public TargetMutation(final Match match, Duration frequency) { - super(match); - this.frequency = frequency; - } +public interface TargetMutation extends MutationModule { /** * Execute a task on the given randomly selected players. * @param players a list of players, which size is determined by {@link #targets()}. */ - public abstract void execute(List players); + void target(List players); /** * Determine the number of random players to target. + * * If there are no enough players on the server, it is possible * that the number of targets is less than expected. * @return number of targets. */ - public abstract int targets(); + int targets(); + + /** + * Get the next time {@link #target()} will be run. + * @return next target time. + */ + Instant next(); + + /** + * Set the next time {@link #target()} will be run. + * @param time next target time. + */ + void next(Instant time); + + /** + * Get the frequency that {@link #target()} will be run. + * @return frequency between target times. + */ + Duration frequency(); /** * Generate a list of random players. * @return the random players. */ - public List search() { - return match.participants() - .filter(MatchPlayer::isSpawned) - .collect(Collectors.toRandomSubList(entropy, targets())); + default List search() { + return match().participants() + .filter(MatchPlayer::isSpawned) + .collect(Collectors.toRandomSubList(entropy(), targets())); + } + + /** + * Execute a task on randomly selected players and reset the + * next time the task will be executed. + */ + default void target() { + target(search()); + next(match().getInstantNow().plus(frequency())); } @Override - public void enable() { - super.enable(); - this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> execute(search())); + default void enable() { + MutationModule.super.enable(); + target(); } - @Override - public void disable() { - if(task != null) { - task.cancel(); - task = null; + @Repeatable(interval = @Time(seconds = 1)) + default void tick() { + Instant now = match().getInstantNow(), next = next(); + if(next == null) { + next(now.plus(frequency())); + } else if(TimeUtils.isEqualOrBeforeNow(now, next)) { + target(); } - super.disable(); + } + + abstract class Impl extends MutationModule.Impl implements TargetMutation { + + Duration frequency; + Instant next; + + public Impl(Match match, Duration frequency) { + super(match); + this.frequency = frequency; + } + + @Override + public Instant next() { + return next; + } + + @Override + public void next(Instant time) { + next = time; + } + + @Override + public Duration frequency() { + return frequency; + } + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java index 1040b95..3f832f6 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ElytraMutation.java @@ -1,17 +1,31 @@ package tc.oc.pgm.mutation.types.kit; +import com.google.common.collect.ImmutableSet; +import net.md_5.bungee.api.ChatColor; import org.bukkit.Material; +import tc.oc.commons.bukkit.chat.WarningComponent; import tc.oc.commons.bukkit.inventory.ArmorType; import tc.oc.commons.bukkit.inventory.Slot; +import tc.oc.commons.core.chat.Component; +import tc.oc.commons.core.chat.Components; +import tc.oc.commons.core.collection.WeakHashSet; +import tc.oc.commons.core.util.TimeUtils; import tc.oc.pgm.doublejump.DoubleJumpKit; import tc.oc.pgm.kits.ItemKit; -import tc.oc.pgm.kits.ItemKitApplicator; +import tc.oc.pgm.kits.KitNode; +import tc.oc.pgm.kits.KitPlayerFacet; +import tc.oc.pgm.kits.RemoveKit; import tc.oc.pgm.kits.SlotItemKit; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; +import tc.oc.pgm.match.MatchScope; +import tc.oc.pgm.mutation.Mutation; +import tc.oc.pgm.mutation.MutationMatchModule; import tc.oc.pgm.mutation.types.KitMutation; import java.time.Duration; +import java.time.Instant; +import java.util.stream.Collectors; import java.util.stream.Stream; public class ElytraMutation extends KitMutation { @@ -30,13 +44,57 @@ public class ElytraMutation extends KitMutation { @Override public void remove(MatchPlayer player) { - // If the player is mid-air, give them a totem so they don't fall and die - if(player.getBukkit().isGliding()) { - ItemKitApplicator applicator = new ItemKitApplicator(); - applicator.put(Slot.OffHand.offHand(), item(Material.TOTEM), false); - applicator.apply(player); + // Anyone left gliding will be taken care of by the ground stop order + if(!player.getBukkit().isGliding()) { + super.remove(player); } - super.remove(player); + } + + @Override + public void disable() { + new GroundStop(match()).run(); + super.disable(); + } + + /** + * A cleanup task to slowly remove elytras from players. + * + * This prevents players that are mid-glide from falling + * out of the sky and gives them time to land. + */ + private class GroundStop implements Runnable { + + Match match; + WeakHashSet gliding; + Instant end; + + GroundStop(Match match) { + this.match = match; + this.gliding = new WeakHashSet<>(match.participants().filter(player -> player.getBukkit().isGliding()).collect(Collectors.toSet())); + this.end = match.getInstantNow().plus(Duration.ofSeconds(10)); + } + + @Override + public void run() { + if(match.isRunning() && !gliding.isEmpty() && !match.module(MutationMatchModule.class).get().enabled(Mutation.ELYTRA)) { + Instant now = match().getInstantNow(); + for(MatchPlayer player : ImmutableSet.copyOf(gliding)) { + if(!player.isSpawned() || !player.getBukkit().isGliding() || TimeUtils.isEqualOrBeforeNow(now, end)) { + gliding.remove(player); + player.facet(KitPlayerFacet.class).applyKit(KitNode.of(new RemoveKit(ELYTRA), new RemoveKit(JUMP)), true); + player.sendHotbarMessage(Components.blank()); + } else { + long seconds = Duration.between(now, end).getSeconds(); + player.sendHotbarMessage(new WarningComponent("mutation.type.elytra.land", new Component(seconds, ChatColor.YELLOW))); + } + } + match.getScheduler(MatchScope.RUNNING).createDelayedTask(Duration.ofMillis(50), this::run); + } else { + gliding.clear(); + match = null; + } + } + } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java index 7675061..12f8819 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EnchantmentMutation.java @@ -21,31 +21,28 @@ import java.util.WeakHashMap; public class EnchantmentMutation extends KitMutation { final static ImmutableMap LEVELS_MAP = new ImmutableMap.Builder() - .put(1, 10) - .put(2, 3) + .put(1, 25) + .put(2, 5) .put(3, 1) .build(); final static ImmutableMap ARMOR_MAP = new ImmutableMap.Builder() .put(Enchantment.PROTECTION_ENVIRONMENTAL, 15) .put(Enchantment.PROTECTION_PROJECTILE, 10) - .put(Enchantment.DURABILITY, 10) .put(Enchantment.PROTECTION_EXPLOSIONS, 5) - .put(Enchantment.PROTECTION_FALL, 5) .put(Enchantment.PROTECTION_FIRE, 5) .put(Enchantment.THORNS, 1) .build(); final static ImmutableMap BOOTS_MAP = new ImmutableMap.Builder() .putAll(ARMOR_MAP) - .put(Enchantment.WATER_WORKER, 5) - .put(Enchantment.DEPTH_STRIDER, 3) - .put(Enchantment.FROST_WALKER, 1) + .put(Enchantment.PROTECTION_FALL, 10) + .put(Enchantment.DEPTH_STRIDER, 3) + .put(Enchantment.FROST_WALKER, 1) .build(); final static ImmutableMap WEAPONS_MAP = new ImmutableMap.Builder() .put(Enchantment.DAMAGE_ALL, 15) - .put(Enchantment.DURABILITY, 10) .put(Enchantment.KNOCKBACK, 10) .put(Enchantment.MENDING, 5) .put(Enchantment.SWEEPING_EDGE, 5) @@ -53,7 +50,6 @@ public class EnchantmentMutation extends KitMutation { .build(); final static ImmutableMap TOOLS_MAP = new ImmutableMap.Builder() - .put(Enchantment.DURABILITY, 10) .put(Enchantment.DIG_SPEED, 10) .put(Enchantment.SILK_TOUCH, 5) .put(Enchantment.LOOT_BONUS_BLOCKS, 5) @@ -116,9 +112,9 @@ public class EnchantmentMutation extends KitMutation { byEntity.put(item, ImmutableMap.copyOf(item.getEnchantments())); savedEnchantments.put(entity, byEntity); // Apply the new enchantments - int amountOfEnchants = LEVELS.choose(entropy); + int amountOfEnchants = LEVELS.choose(entropy()); for(int i = 0; i < amountOfEnchants; i++) { - item.addUnsafeEnchantment(chooser.choose(entropy), LEVELS.choose(entropy)); + item.addUnsafeEnchantment(chooser.choose(entropy()), LEVELS.choose(entropy())); } } @@ -129,7 +125,7 @@ public class EnchantmentMutation extends KitMutation { super.apply(player); player.getInventory().forEach(item -> { // Random number of enchantments on each item - int numberOfEnchants = LEVELS.choose(entropy); + int numberOfEnchants = LEVELS.choose(entropy()); for(int i = 0; i < numberOfEnchants; i++) { apply(item, player.getBukkit().getEquipment()); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java index 61d4922..026bdec 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/EquestrianMutation.java @@ -4,12 +4,11 @@ import com.google.common.collect.ImmutableMap; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.entity.AbstractHorse; -import org.bukkit.entity.Entity; import org.bukkit.entity.EntityType; import org.bukkit.entity.Horse; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.player.PlayerSpawnEntityEvent; +import org.bukkit.event.entity.EntityDamageByEntityEvent; import org.bukkit.inventory.HorseInventory; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SpawnEggMeta; @@ -17,23 +16,23 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; import tc.oc.commons.core.random.WeightedRandomChooser; -import tc.oc.pgm.events.MatchPlayerDeathEvent; -import tc.oc.pgm.events.PlayerChangePartyEvent; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.Kit; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.mutation.types.KitMutation; +import tc.oc.pgm.mutation.types.EntityMutation; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; -public class EquestrianMutation extends KitMutation { +import static tc.oc.commons.core.util.Optionals.cast; + +public class EquestrianMutation extends EntityMutation { final static ImmutableMap TYPE_MAP = new ImmutableMap.Builder() .put(EntityType.HORSE, 100) - // FIXME: Saddle do not work on these horses + // FIXME: Saddles do not work on these horses //.put(EntityType.SKELETON_HORSE, 5) //.put(EntityType.ZOMBIE_HORSE, 5) //.put(EntityType.LLAMA, 1) @@ -49,18 +48,15 @@ public class EquestrianMutation extends KitMutation { final static WeightedRandomChooser TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP); final static WeightedRandomChooser ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP); - final Map horses; - public EquestrianMutation(Match match) { super(match, false); - this.horses = new WeakHashMap<>(); } @Override - public void disable() { - super.disable(); - match.participants().forEach(this::remove); - horses.clear(); + public void enable() { + super.enable(); + this.itemRemove.add(Material.LEATHER); + this.itemRemove.addAll(ARMOR_MAP.keySet()); } @Override @@ -72,7 +68,7 @@ public class EquestrianMutation extends KitMutation { if(!spawnable(location)) { ItemStack item = item(Material.MONSTER_EGG); SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta(); - egg.setSpawnedType(TYPES.choose(entropy)); + egg.setSpawnedType(TYPES.choose(entropy())); item.setItemMeta(egg); kits.add(new FreeItemKit(item)); } @@ -83,38 +79,31 @@ public class EquestrianMutation extends KitMutation { super.apply(player); Location location = player.getLocation(); if(spawnable(location)) { - setup(player, spawn(location, (Class) TYPES.choose(match.entropyForTick()).getEntityClass())); + spawn(location, (Class) TYPES.choose(entropy()).getEntityClass(), player); } } @Override - public void remove(MatchPlayer player) { - super.remove(player); - AbstractHorse horse = horses.remove(player); - if(horse != null) { - horse.ejectAll(); - horse.remove(); - } - } - - public void setup(MatchPlayer player, AbstractHorse horse) { - horses.put(player, horse); - horse.setAdult(); - horse.setJumpStrength(2 * entropy.randomDouble()); - horse.setDomestication(1); - horse.setMaxDomestication(1); - horse.setTamed(true); - horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0)); - horse.setOwner(player.getBukkit()); - horse.setPassenger(player.getBukkit()); - if(horse instanceof Horse) { - Horse horsey = (Horse) horse; - horsey.setStyle(entropy.randomElement(Horse.Style.values())); - horsey.setColor(entropy.randomElement(Horse.Color.values())); - HorseInventory inventory = horsey.getInventory(); - inventory.setSaddle(item(Material.SADDLE)); - inventory.setArmor(item(ARMOR.choose(entropy))); + public AbstractHorse register(AbstractHorse horse, @Nullable MatchPlayer owner) { + super.register(horse, owner); + if(owner != null) { + horse.setAdult(); + horse.setJumpStrength(2 * entropy().randomDouble()); + horse.setDomestication(1); + horse.setMaxDomestication(1); + horse.setTamed(true); + horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0)); + horse.setOwner(owner.getBukkit()); + horse.setPassenger(owner.getBukkit()); + cast(horse, Horse.class).ifPresent(horsey -> { + horsey.setStyle(entropy().randomElement(Horse.Style.values())); + horsey.setColor(entropy().randomElement(Horse.Color.values())); + HorseInventory inventory = horsey.getInventory(); + inventory.setSaddle(item(Material.SADDLE)); + inventory.setArmor(item(ARMOR.choose(entropy()))); + }); } + return horse; } public boolean spawnable(Location location) { @@ -129,22 +118,11 @@ public class EquestrianMutation extends KitMutation { } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onEntitySpawn(PlayerSpawnEntityEvent event) { - Entity entity = event.getEntity(); - if(TYPE_MAP.containsKey(entity.getType())) { - match.participant(event.getPlayer()) - .ifPresent(player -> setup(player, (AbstractHorse) event.getEntity())); + public void onEntityDamage(EntityDamageByEntityEvent event) { + if(playerByEntity(event.getEntity()).flatMap(MatchPlayer::competitor) + .equals(match().participant(event.getDamager()).flatMap(MatchPlayer::competitor))) { + event.setCancelled(true); } } - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onPlayerDeath(MatchPlayerDeathEvent event) { - remove(event.getVictim()); - } - - @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onPartyChange(PlayerChangePartyEvent event) { - remove(event.getPlayer()); - } - } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java index d71e969..147f99d 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ExplosiveMutation.java @@ -3,13 +3,11 @@ package tc.oc.pgm.mutation.types.kit; import com.google.common.collect.Range; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.TNTPrimed; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.entity.ExplosionPrimeEvent; import org.bukkit.inventory.PlayerInventory; import tc.oc.commons.bukkit.item.ItemBuilder; -import tc.oc.commons.core.collection.WeakHashSet; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.ItemKit; @@ -30,26 +28,17 @@ public class ExplosiveMutation extends KitMutation { final static Range RADIUS = Range.openClosed(0, 4); - final WeakHashSet tracked; - public ExplosiveMutation(Match match) { super(match, false); - this.tracked = new WeakHashSet<>(); this.rewards.add(new KillReward(TNT)); this.rewards.add(new KillReward(ARROWS)); } - @Override - public void disable() { - super.disable(); - tracked.clear(); - } - @Override public void kits(MatchPlayer player, List kits) { super.kits(player, kits); PlayerInventory inv = player.getInventory(); - if(random.nextBoolean()) { // tnt and lighter kit + if(random().nextBoolean()) { // tnt and lighter kit if(!inv.contains(Material.TNT)) kits.add(TNT); if(!inv.contains(Material.FLINT_AND_STEEL)) kits.add(LIGHTER); } else { // fire bow and arrows kit @@ -62,9 +51,15 @@ public class ExplosiveMutation extends KitMutation { } } + @Override + public void remove(MatchPlayer player) { + player.getInventory().all(Material.BOW).values().forEach(bow -> FIRE_BOW.item().getEnchantments().keySet().forEach(enchantment -> bow.removeEnchantment(enchantment))); + super.remove(player); + } + @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) public void onExplosionPrime(ExplosionPrimeEvent event) { - event.setRadius(event.getRadius() + entropy.randomInt(RADIUS)); + event.setRadius(event.getRadius() + entropy().randomInt(RADIUS)); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java index e5e5196..f136c81 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HardcoreMutation.java @@ -1,7 +1,6 @@ package tc.oc.pgm.mutation.types.kit; import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import tc.oc.pgm.gamerules.GameRulesMatchModule; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; @@ -26,13 +25,13 @@ public class HardcoreMutation extends KitMutation { } public GameRulesMatchModule rules() { - return match.module(GameRulesMatchModule.class).get(); + return match().module(GameRulesMatchModule.class).get(); } @Override public void enable() { super.enable(); - previous = world.getGameRuleValue(KEY); + previous = world().getGameRuleValue(KEY); rules().gameRules().put(KEY, "false"); } @@ -40,7 +39,7 @@ public class HardcoreMutation extends KitMutation { public void disable() { rules().gameRules().remove(KEY); if(previous != null) { - world.setGameRuleValue(KEY, previous); + world().setGameRuleValue(KEY, previous); } super.disable(); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java index afcdfba..72e1343 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/HealthMutation.java @@ -1,7 +1,6 @@ package tc.oc.pgm.mutation.types.kit; import org.bukkit.Material; -import org.bukkit.inventory.ItemStack; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.HealthKit; diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java index 9855a17..32c9e24 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/MobsMutation.java @@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.Range; import org.bukkit.Material; import org.bukkit.entity.EntityType; +import org.bukkit.entity.LivingEntity; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SpawnEggMeta; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; @@ -12,11 +13,11 @@ import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.Kit; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.mutation.types.KitMutation; +import tc.oc.pgm.mutation.types.EntityMutation; import java.util.List; -public class MobsMutation extends KitMutation { +public class MobsMutation extends EntityMutation { final static ImmutableMap TYPE_MAP = new ImmutableMap.Builder() .put(EntityType.ZOMBIE, 50) @@ -37,17 +38,17 @@ public class MobsMutation extends KitMutation { final static Range AMOUNT = Range.closed(1, 3); public MobsMutation(Match match) { - super(match, true); + super(match, false); } @Override public void kits(MatchPlayer player, List kits) { super.kits(player, kits); - int eggs = entropy.randomInt(AMOUNT); + int eggs = entropy().randomInt(AMOUNT); for(int i = 0; i < eggs; i++) { - ItemStack item = item(Material.MONSTER_EGG, entropy.randomInt(AMOUNT)); + ItemStack item = item(Material.MONSTER_EGG, entropy().randomInt(AMOUNT)); SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta(); - egg.setSpawnedType(TYPES.choose(entropy)); + egg.setSpawnedType(TYPES.choose(entropy())); item.setItemMeta(egg); kits.add(new FreeItemKit(item)); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java index eb3363b..6f1a095 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/NoFallMutation.java @@ -22,7 +22,7 @@ public abstract class NoFallMutation extends KitMutation { } public DisableDamageMatchModule damage() { - return match.module(DisableDamageMatchModule.class).get(); + return match().module(DisableDamageMatchModule.class).get(); } @Override diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java index 6a51913..87f7a4e 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/PotionMutation.java @@ -68,13 +68,13 @@ public class PotionMutation extends KitMutation { @Override public void kits(MatchPlayer player, List kits) { super.kits(player, kits); - int numberOfPotions = entropy.randomInt(AMOUNT_RANGE); + int numberOfPotions = entropy().randomInt(AMOUNT_RANGE); for(int i = 0; i < numberOfPotions; i++) { WeightedRandomChooser type; WeightedRandomChooser material; Range range; // Determine whether the potion will be "good" or "bad" - if(random.nextBoolean()) { + if(random().nextBoolean()) { type = BAD; material = BAD_BOTTLE; range = BAD_DURATION_RANGE; @@ -84,10 +84,10 @@ public class PotionMutation extends KitMutation { range = GOOD_DURATION_RANGE; } // Choose all the random attributes - PotionEffectType effect = type.choose(entropy); - Material bottle = material.choose(entropy); - int duration = 20 * entropy.randomInt(range); - int amplifier = entropy.randomInt(AMPLIFIER_RANGE); + PotionEffectType effect = type.choose(entropy()); + Material bottle = material.choose(entropy()); + int duration = 20 * entropy().randomInt(range); + int amplifier = entropy().randomInt(AMPLIFIER_RANGE); // Apply the attributes to the item stack ItemStack potion = item(bottle); PotionMeta meta = (PotionMeta) potion.getItemMeta(); diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java index 60cf9cc..8fa7cdc 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/kit/ProjectileMutation.java @@ -3,20 +3,17 @@ package tc.oc.pgm.mutation.types.kit; import com.google.common.collect.Range; import org.bukkit.Material; import org.bukkit.enchantments.Enchantment; -import org.bukkit.entity.Arrow; -import org.bukkit.entity.Entity; -import org.bukkit.entity.TippedArrow; +import org.bukkit.entity.LivingEntity; +import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; -import org.bukkit.event.entity.EntityShootBowEvent; -import org.bukkit.event.entity.ProjectileLaunchEvent; +import org.bukkit.event.entity.ProjectileHitEvent; import org.bukkit.inventory.Inventory; -import org.bukkit.metadata.FixedMetadataValue; import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import tc.oc.commons.core.random.ImmutableWeightedRandomChooser; import tc.oc.commons.core.random.WeightedRandomChooser; -import tc.oc.pgm.PGM; +import tc.oc.commons.core.util.Optionals; import tc.oc.pgm.killreward.KillReward; import tc.oc.pgm.kits.FreeItemKit; import tc.oc.pgm.kits.ItemKit; @@ -26,6 +23,7 @@ import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.mutation.types.KitMutation; import java.util.List; +import java.util.Optional; public class ProjectileMutation extends KitMutation { @@ -39,8 +37,6 @@ public class ProjectileMutation extends KitMutation { final static ItemKit BOW = new FreeItemKit(item(Material.BOW)); final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 16)); - final static String KEY = "is_modified_arrow"; - public ProjectileMutation(Match match) { super(match, false); this.rewards.add(new KillReward(ARROWS)); @@ -50,7 +46,7 @@ public class ProjectileMutation extends KitMutation { public void apply(MatchPlayer player) { super.apply(player); Inventory inventory = player.getInventory(); - inventory.all(Material.BOW).values().forEach(arrow -> arrow.addUnsafeEnchantment(ENCHANTMENTS.choose(entropy), entropy.randomInt(ENCHANT_RANGE))); + inventory.all(Material.BOW).values().forEach(arrow -> arrow.addUnsafeEnchantment(ENCHANTMENTS.choose(entropy()), entropy().randomInt(ENCHANT_RANGE))); } @Override @@ -62,23 +58,10 @@ public class ProjectileMutation extends KitMutation { } @EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST) - public void onBowShoot(EntityShootBowEvent event) { - Entity projectile = event.getProjectile(); - if(projectile instanceof Arrow && (!projectile.hasMetadata(KEY) || !projectile.getMetadata(KEY, PGM.get()).asBoolean())) { - Arrow arrow = (Arrow) projectile; - TippedArrow tipped = world.spawn(projectile.getLocation(), TippedArrow.class); - tipped.setMetadata(KEY, new FixedMetadataValue(PGM.get(), true)); - tipped.setCritical(arrow.isCritical()); - tipped.setKnockbackStrength(arrow.getKnockbackStrength()); - tipped.setDamage(arrow.getDamage()); - tipped.setShooter(arrow.getShooter()); - tipped.setVelocity(projectile.getVelocity()); - tipped.setPickupRule(Arrow.PickupRule.DISALLOWED); - tipped.addCustomEffect(new PotionEffect(POTIONS.choose(entropy), 20 * entropy.randomInt(DURATION_RANGE), entropy.randomInt(AMPLIFIER_RANGE)), true); - arrow.remove(); - event.setCancelled(true); - match.callEvent(new ProjectileLaunchEvent(tipped)); - } + public void onProjectileHit(ProjectileHitEvent event) { + Optionals.cast(Optional.ofNullable(event.getHitEntity()), LivingEntity.class) + .filter(entity -> Optional.ofNullable(event.getEntity().getShooter()).filter(shooter -> shooter instanceof Player).isPresent()) + .ifPresent(entity -> entity.addPotionEffect(new PotionEffect(POTIONS.choose(entropy()), 20 * entropy().randomInt(DURATION_RANGE), entropy().randomInt(AMPLIFIER_RANGE)), true)); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java index 7062f61..1a80629 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/BlitzMutation.java @@ -11,7 +11,7 @@ import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.mutation.types.MutationModule; import tc.oc.pgm.teams.TeamMatchModule; -public class BlitzMutation extends MutationModule { +public class BlitzMutation extends MutationModule.Impl { final static Range LIVES = Range.closed(1, 3); final static Fraction TEAM_CHANCE = Fraction.ONE_QUARTER; @@ -23,22 +23,22 @@ public class BlitzMutation extends MutationModule { @Override public void enable() { super.enable(); - int lives = match.entropyForTick().randomInt(LIVES); + int lives = entropy().randomInt(LIVES); Lives.Type type; - if(match.module(TeamMatchModule.class).isPresent() && RandomUtils.nextBoolean(random, TEAM_CHANCE)) { + if(match().module(TeamMatchModule.class).isPresent() && RandomUtils.nextBoolean(random(), TEAM_CHANCE)) { type = Lives.Type.TEAM; - lives *= match.module(TeamMatchModule.class).get().getFullestTeam().getSize(); + lives *= match().module(TeamMatchModule.class).get().getFullestTeam().getSize(); } else { type = Lives.Type.INDIVIDUAL; } - match.module(BlitzMatchModuleImpl.class).get().activate(BlitzProperties.create(match, lives, type)); + match().module(BlitzMatchModuleImpl.class).get().activate(BlitzProperties.create(match(), lives, type)); } @Override public void disable() { - match.getScheduler(MatchScope.LOADED).createTask(() -> { - if(!match.isFinished()) { - match.module(BlitzMatchModuleImpl.class).get().deactivate(); + match().getScheduler(MatchScope.LOADED).createTask(() -> { + if(!match().isFinished()) { + match().module(BlitzMatchModuleImpl.class).get().deactivate(); } }); super.disable(); diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java index 66988d2..94af87d 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/other/RageMutation.java @@ -7,7 +7,7 @@ import tc.oc.pgm.match.Match; import tc.oc.pgm.mutation.types.MutationModule; import tc.oc.pgm.rage.RageMatchModule; -public class RageMutation extends MutationModule { +public class RageMutation extends MutationModule.Impl { RageMatchModule rage; @@ -26,4 +26,5 @@ public class RageMutation extends MutationModule { super.disable(); rage = null; } + } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java index 7733a81..c835cc9 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/ApocalypseMutation.java @@ -21,6 +21,7 @@ import tc.oc.commons.core.random.WeightedRandomChooser; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; import tc.oc.pgm.match.Repeatable; +import tc.oc.pgm.mutation.types.EntityMutation; import tc.oc.pgm.mutation.types.kit.EnchantmentMutation; import tc.oc.pgm.mutation.types.TargetMutation; import tc.oc.pgm.points.PointProviderAttributes; @@ -28,19 +29,17 @@ import tc.oc.pgm.points.RandomPointProvider; import tc.oc.pgm.points.RegionPointProvider; import tc.oc.pgm.regions.CuboidRegion; +import javax.annotation.Nullable; import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; -import java.util.Comparator; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.WeakHashMap; import static tc.oc.commons.core.random.RandomUtils.nextBoolean; -public class ApocalypseMutation extends TargetMutation { +public class ApocalypseMutation extends EntityMutation implements TargetMutation { final static ImmutableMap AMOUNT_MAP = new ImmutableMap.Builder() .put(3, 25) @@ -104,6 +103,7 @@ public class ApocalypseMutation extends TargetMutation { final static WeightedRandomChooser PASSENGER = new ImmutableWeightedRandomChooser<>(PASSENGER_MAP); final static WeightedRandomChooser CUBE = new ImmutableWeightedRandomChooser<>(CUBE_MAP); + final static Range FREQUENCY = Range.closed(5, 30); // Seconds between entity spawns final static int DISTANCE = 15; // Max distance entities spawn from players final static int PARTICIPANT_ENTITIES = 25; // Max entities on the field per participant final static int MAX_ENTITIES = 500; // Max total entities on the field @@ -111,33 +111,34 @@ public class ApocalypseMutation extends TargetMutation { final static Fraction SPECIAL_CHANCE = Fraction.ONE_FIFTH; // Chance of a special attribute occuring in an entity final static int SPECIAL_MULTIPLIER = 3; // Multiplier for special attributes - final WeakHashMap entities; - long time; + long time; // world time + Instant next; // next time to spawn entities + final PointProviderAttributes attributes; // attributes to choosing random points public ApocalypseMutation(Match match) { - super(match, Duration.ofSeconds(20)); - this.entities = new WeakHashMap<>(); + super(match, false); + this.attributes = new PointProviderAttributes(null, null, true, false); } /** * Get the maximum amount of entities that can be spawned. */ public int entitiesMax() { - return Math.min((int) match.participants().count() * PARTICIPANT_ENTITIES, MAX_ENTITIES); + return Math.min((int) match().participants().count() * PARTICIPANT_ENTITIES, MAX_ENTITIES); } /** * Get the number of available slots are left for additional entities to spawn. */ public int entitiesLeft() { - return entitiesMax() - world.getLivingEntities().size() + (int) match.participants().count(); + return entitiesMax() - world().getLivingEntities().size() + (int) match().participants().count(); } /** * Generate a random spawn point given two locations. */ public Optional location(Location start, Location end) { - return Optional.ofNullable(new RandomPointProvider(Collections.singleton(new RegionPointProvider(new CuboidRegion(start.position(), end.position()), new PointProviderAttributes()))).getPoint(match, null)); + return Optional.ofNullable(new RandomPointProvider(Collections.singleton(new RegionPointProvider(new CuboidRegion(start.position(), end.position()), attributes))).getPoint(match(), null)); } /** @@ -147,15 +148,15 @@ public class ApocalypseMutation extends TargetMutation { */ public void spawn(Location location, boolean ground) { int slots = entitiesLeft(); - int queued = AMOUNT.choose(entropy); + int queued = AMOUNT.choose(entropy()); // Remove any entities that may be over the max limit despawn(queued - slots); // Determine whether the entities should be airborn - int stack = STACK.choose(entropy); - boolean air = !ground || nextBoolean(random, SPECIAL_CHANCE); + int stack = STACK.choose(entropy()); + boolean air = !ground || nextBoolean(random(), SPECIAL_CHANCE); if(air) { - stack += (stack == 1 && random.nextBoolean() ? 1 : 0); - location.add(0, entropy.randomInt(AIR_OFFSET), 0); + stack += (stack == 1 && random().nextBoolean() ? 1 : 0); + location.add(0, entropy().randomInt(AIR_OFFSET), 0); } // Select the random entity chooser based on ground, air, and stacked boolean stacked = stack > 1; @@ -163,7 +164,7 @@ public class ApocalypseMutation extends TargetMutation { if(air) { if(stacked) { if(ground) { - chooser = nextBoolean(random, SPECIAL_CHANCE) ? CUBE : FLYABLE; + chooser = nextBoolean(random(), SPECIAL_CHANCE) ? CUBE : FLYABLE; } else { chooser = FLYABLE; } @@ -174,7 +175,7 @@ public class ApocalypseMutation extends TargetMutation { if(stacked) { chooser = GROUND; } else { - chooser = random.nextBoolean() ? GROUND : RANGED; + chooser = random().nextBoolean() ? GROUND : RANGED; } } // Select the specific entity types for the spawn, @@ -182,13 +183,13 @@ public class ApocalypseMutation extends TargetMutation { // but may have variations (like armor) between them. List types = new ArrayList<>(); for(int i = 0; i < stack; i++) { - types.add((i == 0 ? chooser : PASSENGER).choose(entropy)); + types.add((i == 0 ? chooser : PASSENGER).choose(entropy())); } // Spawn the mobs and stack them if required for(int i = 0; i < queued; i++) { Entity last = null; for(EntityType type : types) { - Entity entity = spawn(location, type); + Entity entity = spawn(location, (Class) type.getEntityClass()); if(last != null) { last.setPassenger(entity); } @@ -198,20 +199,14 @@ public class ApocalypseMutation extends TargetMutation { } - /** - * Spawn an individual entitiy at a location given an entity type. - * @param location the location to spawn at. - * @param type the type of entity. - * @return the constructed entity. - */ - public LivingEntity spawn(Location location, EntityType type) { - EnchantmentMutation enchant = new EnchantmentMutation(match); - LivingEntity entity = (LivingEntity) spawn(location, type.getEntityClass()); + @Override + public LivingEntity spawn(Location location, Class entityClass, @Nullable MatchPlayer owner) { + LivingEntity entity = super.spawn(location, entityClass, owner); + EnchantmentMutation enchant = new EnchantmentMutation(match()); EntityEquipment equipment = entity.getEquipment(); entity.setVelocity(Vector.getRandom()); - entity.setAbsorption(5); ItemStack held = null; - switch(type) { + switch(entity.getType()) { case SKELETON: case WITHER_SKELETON: case STRAY: @@ -221,7 +216,7 @@ public class ApocalypseMutation extends TargetMutation { case ZOMBIE_VILLAGER: case HUSK: Zombie zombie = (Zombie) entity; - zombie.setBaby(nextBoolean(random, SPECIAL_CHANCE)); + zombie.setBaby(nextBoolean(random(), SPECIAL_CHANCE)); break; case PIG_ZOMBIE: PigZombie pigZombie = (PigZombie) entity; @@ -231,8 +226,8 @@ public class ApocalypseMutation extends TargetMutation { break; case CREEPER: Creeper creeper = (Creeper) entity; - creeper.setPowered(nextBoolean(random, SPECIAL_CHANCE)); - world.strikeLightningEffect(location); + creeper.setPowered(nextBoolean(random(), SPECIAL_CHANCE)); + world().strikeLightningEffect(location); break; case PRIMED_TNT: TNTPrimed tnt = (TNTPrimed) entity; @@ -244,14 +239,13 @@ public class ApocalypseMutation extends TargetMutation { slime.setSize(slime.getSize() * SPECIAL_MULTIPLIER); break; case SKELETON_HORSE: - world.strikeLightning(location); + world().strikeLightning(location); break; } - if(held != null && random.nextBoolean()) { + if(held != null && random().nextBoolean()) { enchant.apply(held, equipment); equipment.setItemInMainHand(held); } - entities.put(entity, Instant.now()); return entity; } @@ -260,17 +254,12 @@ public class ApocalypseMutation extends TargetMutation { * to make room for new entities. * @param amount the amount of entities to despawn. */ - public void despawn(int amount) { - entities.entrySet() - .stream() - .sorted(Map.Entry.comparingByValue(Comparator.comparing(Instant::toEpochMilli))) - .limit(Math.max(0, amount)) - .map(Map.Entry::getKey) - .forEach(Entity::remove); + public void despawn(long amount) { + entitiesByTime().limit(Math.max(0, amount)).forEachOrdered(this::despawn); } @Override - public void execute(List players) { + public void target(List players) { // At least one player is required to spawn mobs if(players.size() >= 1) { Location start, end; @@ -284,7 +273,7 @@ public class ApocalypseMutation extends TargetMutation { if(location.isPresent()) { // if the location is safe (on ground) spawn(location.get(), true); } else { // if the location was not safe, generate a simple midpoint location - spawn(start.position().midpoint(end.position()).toLocation(world), false); + spawn(start.position().midpoint(end.position()).toLocation(world()), false); } } } @@ -294,21 +283,38 @@ public class ApocalypseMutation extends TargetMutation { return 2; // Always require 2 targets to generate a spawn location between them } + @Override + public Instant next() { + return next; + } + + @Override + public void next(Instant time) { + next = time; + } + + @Override + public Duration frequency() { + return Duration.ofSeconds(entropy().randomInt(FREQUENCY)); + } + @Override public void enable() { super.enable(); - time = world.getTime(); + TargetMutation.super.enable(); + time = world().getTime(); } @Repeatable public void tick() { - world.setTime(16000); // Night time to prevent flaming entities + TargetMutation.super.tick(); + world().setTime(16000); // Night time to prevent flaming entities } @Override public void disable() { - world.setTime(time); - despawn(entities.size()); + world().setTime(time); + despawn(entities().count()); super.disable(); } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java index 6cd8b40..7c095ad 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/BomberMutation.java @@ -4,63 +4,85 @@ import com.google.common.collect.Range; import org.bukkit.Location; import org.bukkit.entity.TNTPrimed; import org.bukkit.util.Vector; -import tc.oc.commons.core.collection.WeakHashSet; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; -import tc.oc.pgm.match.Repeatable; +import tc.oc.pgm.mutation.types.EntityMutation; import tc.oc.pgm.mutation.types.TargetMutation; import java.time.Duration; +import java.time.Instant; import java.util.List; -public class BomberMutation extends TargetMutation { +public class BomberMutation extends EntityMutation implements TargetMutation { final static Duration FREQUENCY = Duration.ofSeconds(30); - final static Range TARGETS = Range.closed(1, 5); + final static Range TARGETS = Range.closed(2, 5); final static Range HEIGHT = Range.closed(30, 60); final static Range TICKS = Range.closed(10, 30); - final WeakHashSet falling; + Instant next; public BomberMutation(Match match) { - super(match, FREQUENCY); - this.falling = new WeakHashSet<>(); + super(match, false); } @Override - public void execute(List players) { + public void target(List players) { players.forEach(player -> { - int bombs = entropy.randomInt(TARGETS); - int height = entropy.randomInt(HEIGHT); + int bombs = entropy().randomInt(TARGETS); + int height = entropy().randomInt(HEIGHT); Location location = player.getLocation().clone().add(0, height, 0); for(int i = 0; i < bombs; i++) { - TNTPrimed tnt = world.spawn(location, TNTPrimed.class); + TNTPrimed tnt = spawn(location, TNTPrimed.class); tnt.setGlowing(true); tnt.setIsIncendiary(false); - tnt.setFuseTicks(Integer.MAX_VALUE); + tnt.setFuseTicks(200); tnt.setVelocity( new Vector( - (random.nextBoolean() ? .5 : -.5) * entropy.randomDouble(), - -entropy.randomDouble(), - (random.nextBoolean() ? .5 : -.5) * entropy.randomDouble() + (random().nextBoolean() ? .5 : -.5) * entropy().randomDouble(), + -entropy().randomDouble(), + (random().nextBoolean() ? .5 : -.5) * entropy().randomDouble() ) ); - falling.add(tnt); } }); } @Override public int targets() { - return match.entropyForTick().randomInt(TARGETS); + return match().entropyForTick().randomInt(TARGETS); } - @Repeatable + @Override + public Instant next() { + return next; + } + + @Override + public void next(Instant time) { + next = time; + } + + @Override + public Duration frequency() { + return FREQUENCY; + } + + @Override + public void remove(TNTPrimed entity) { + entity.setFuseTicks(entropy().randomInt(TICKS)); + } + + @Override + public void enable() { + super.enable(); + TargetMutation.super.enable(); + } + + @Override public void tick() { - falling.stream() - .filter(TNTPrimed::isOnGround) - .forEach(tnt -> tnt.setFuseTicks(entropy.randomInt(TICKS))); - falling.removeIf(TNTPrimed::isOnGround); + TargetMutation.super.tick(); + entities().filter(TNTPrimed::isOnGround).forEach(this::despawn); } } diff --git a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java index 583d931..42b1f89 100644 --- a/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java +++ b/PGM/src/main/java/tc/oc/pgm/mutation/types/targetable/LightningMutation.java @@ -2,7 +2,6 @@ package tc.oc.pgm.mutation.types.targetable; import com.google.common.collect.Range; import org.bukkit.Location; -import org.bukkit.World; import org.bukkit.util.Vector; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchPlayer; @@ -11,32 +10,31 @@ import tc.oc.pgm.mutation.types.TargetMutation; import java.time.Duration; import java.util.List; -public class LightningMutation extends TargetMutation { +public class LightningMutation extends TargetMutation.Impl { final static Duration FREQUENCY = Duration.ofSeconds(30); - final static Range TARGETS = Range.closed(1, 5); - final static Range STRIKES = Range.closed(0, 3); + final static Range TARGETS = Range.closed(2, 5); + final static Range STRIKES = Range.closed(1, 3); public LightningMutation(Match match) { super(match, FREQUENCY); } @Override - public void execute(List players) { + public void target(List players) { players.forEach(player -> { - World world = match.getWorld(); Location location = player.getLocation(); - world.strikeLightning(location.clone().add(Vector.getRandom())); - int strikes = match.entropyForTick().randomInt(STRIKES); + world().strikeLightning(location.clone().add(Vector.getRandom())); + int strikes = entropy().randomInt(STRIKES); for(int i = 0; i < strikes; i++) { - world.strikeLightningEffect(location.clone().add(Vector.getRandom().multiply(Math.pow(i + 1, 2)))); + world().strikeLightningEffect(location.clone().add(Vector.getRandom().multiply(Math.pow(i + 1, 2)))); } }); } @Override public int targets() { - return match.entropyForTick().randomInt(TARGETS); + return match().entropyForTick().randomInt(TARGETS); } }