Fix edge case errors with mutations
This commit is contained in:
parent
af1ebe598a
commit
e2e5027c69
|
@ -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
|
||||
|
|
|
@ -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 = 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<E extends Entity> extends KitMutation {
|
||||
|
||||
final Set<E> entities;
|
||||
final Map<Instant, Set<E>> entitiesByTime;
|
||||
final Map<MatchPlayer, Set<E>> entitiesByPlayer;
|
||||
final Map<E, MatchPlayer> 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<E> 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<E> 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<E> 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<MatchPlayer> 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<E> byTime = entitiesByTime.getOrDefault(now, new WeakHashSet<>());
|
||||
byTime.add(entity);
|
||||
entitiesByTime.put(now, byTime);
|
||||
if(owner != null) {
|
||||
final Set<E> 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<E> 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<E> 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<E>(){}.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<E>(){}.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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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<Kit> kits;
|
||||
protected final Map<MatchPlayer, List<Kit>> playerKits;
|
||||
protected final Map<MatchPlayer, Map<Slot, ItemStack>> savedSlots;
|
||||
protected final Set<Material> itemRemove;
|
||||
protected final List<KillReward> 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<Kit> 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<Slot, ItemStack> 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();
|
||||
}
|
||||
|
|
|
@ -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 extends Entity> E spawn(Location location, Class<E> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<MatchPlayer> players);
|
||||
void target(List<MatchPlayer> 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<MatchPlayer> search() {
|
||||
return match.participants()
|
||||
.filter(MatchPlayer::isSpawned)
|
||||
.collect(Collectors.toRandomSubList(entropy, targets()));
|
||||
default List<MatchPlayer> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<MatchPlayer> 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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,31 +21,28 @@ import java.util.WeakHashMap;
|
|||
public class EnchantmentMutation extends KitMutation {
|
||||
|
||||
final static ImmutableMap<Integer, Integer> LEVELS_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||
.put(1, 10)
|
||||
.put(2, 3)
|
||||
.put(1, 25)
|
||||
.put(2, 5)
|
||||
.put(3, 1)
|
||||
.build();
|
||||
|
||||
final static ImmutableMap<Enchantment, Integer> ARMOR_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||
.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<Enchantment, Integer> BOOTS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||
.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<Enchantment, Integer> WEAPONS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||
.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<Enchantment, Integer> TOOLS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||
.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());
|
||||
}
|
||||
|
|
|
@ -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<AbstractHorse> {
|
||||
|
||||
final static ImmutableMap<EntityType, Integer> TYPE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||
.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<EntityType, Integer> TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP);
|
||||
final static WeightedRandomChooser<Material, Integer> ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP);
|
||||
|
||||
final Map<MatchPlayer, AbstractHorse> 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<? extends AbstractHorse>) TYPES.choose(match.entropyForTick()).getEntityClass()));
|
||||
spawn(location, (Class<AbstractHorse>) 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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Integer> RADIUS = Range.openClosed(0, 4);
|
||||
|
||||
final WeakHashSet<TNTPrimed> 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<Kit> 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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<LivingEntity> {
|
||||
|
||||
final static ImmutableMap<EntityType, Integer> TYPE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||
.put(EntityType.ZOMBIE, 50)
|
||||
|
@ -37,17 +38,17 @@ public class MobsMutation extends KitMutation {
|
|||
final static Range<Integer> AMOUNT = Range.closed(1, 3);
|
||||
|
||||
public MobsMutation(Match match) {
|
||||
super(match, true);
|
||||
super(match, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void kits(MatchPlayer player, List<Kit> 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));
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -68,13 +68,13 @@ public class PotionMutation extends KitMutation {
|
|||
@Override
|
||||
public void kits(MatchPlayer player, List<Kit> 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<PotionEffectType, Integer> type;
|
||||
WeightedRandomChooser<Material, Integer> material;
|
||||
Range<Integer> 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();
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Integer> 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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<LivingEntity> implements TargetMutation {
|
||||
|
||||
final static ImmutableMap<Integer, Integer> AMOUNT_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||
.put(3, 25)
|
||||
|
@ -104,6 +103,7 @@ public class ApocalypseMutation extends TargetMutation {
|
|||
final static WeightedRandomChooser<EntityType, Integer> PASSENGER = new ImmutableWeightedRandomChooser<>(PASSENGER_MAP);
|
||||
final static WeightedRandomChooser<EntityType, Integer> CUBE = new ImmutableWeightedRandomChooser<>(CUBE_MAP);
|
||||
|
||||
final static Range<Integer> 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<Entity, Instant> 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(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<EntityType> 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<LivingEntity>) 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<LivingEntity> 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<MatchPlayer> players) {
|
||||
public void target(List<MatchPlayer> 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<TNTPrimed> implements TargetMutation {
|
||||
|
||||
final static Duration FREQUENCY = Duration.ofSeconds(30);
|
||||
final static Range<Integer> TARGETS = Range.closed(1, 5);
|
||||
final static Range<Integer> TARGETS = Range.closed(2, 5);
|
||||
final static Range<Integer> HEIGHT = Range.closed(30, 60);
|
||||
final static Range<Integer> TICKS = Range.closed(10, 30);
|
||||
|
||||
final WeakHashSet<TNTPrimed> falling;
|
||||
Instant next;
|
||||
|
||||
public BomberMutation(Match match) {
|
||||
super(match, FREQUENCY);
|
||||
this.falling = new WeakHashSet<>();
|
||||
super(match, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(List<MatchPlayer> players) {
|
||||
public void target(List<MatchPlayer> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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<Integer> TARGETS = Range.closed(1, 5);
|
||||
final static Range<Integer> STRIKES = Range.closed(0, 3);
|
||||
final static Range<Integer> TARGETS = Range.closed(2, 5);
|
||||
final static Range<Integer> STRIKES = Range.closed(1, 3);
|
||||
|
||||
public LightningMutation(Match match) {
|
||||
super(match, FREQUENCY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(List<MatchPlayer> players) {
|
||||
public void target(List<MatchPlayer> 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue