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.explosive.desc = tnt and fire bows
|
||||||
mutation.type.elytra = Elytra
|
mutation.type.elytra = Elytra
|
||||||
mutation.type.elytra.desc = fly around with an 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 = Projectile
|
||||||
mutation.type.projectile.desc = arrow potion effects
|
mutation.type.projectile.desc = arrow potion effects
|
||||||
mutation.type.enchantment = Enchantment
|
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.singular = Your team has {0} life left
|
||||||
lives.remaining.team.plural = Your team has {0} lives 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.eliminated = eliminated
|
||||||
lives.status.alive = {0} alive
|
lives.status.alive = {0} alive
|
||||||
lives.status.lives = {0} lives
|
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);
|
final Optional<Lives> lives = lives(player);
|
||||||
if(activated() && lives.isPresent()) {
|
if(activated() && lives.isPresent()) {
|
||||||
player.showTitle(
|
player.showTitle(
|
||||||
|
@ -118,6 +122,10 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void update() {
|
||||||
|
match.callEvent(new BlitzEvent(match, this));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean activated() {
|
public boolean activated() {
|
||||||
return activated;
|
return activated;
|
||||||
|
@ -142,6 +150,7 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul
|
||||||
activated = false;
|
activated = false;
|
||||||
lives.clear();
|
lives.clear();
|
||||||
eliminated.clear();
|
eliminated.clear();
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -161,10 +170,10 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul
|
||||||
match.participants().forEach(player -> {
|
match.participants().forEach(player -> {
|
||||||
setup(player, false);
|
setup(player, false);
|
||||||
if(match.hasStarted()) {
|
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) {
|
if(notify) {
|
||||||
player.showTitle(Components.blank(), life.change(lives), 0, 40, 10);
|
player.showTitle(Components.blank(), life.change(lives), 0, 40, 10);
|
||||||
}
|
}
|
||||||
|
player.competitor().ifPresent(competitor -> competitor.participants().forEach(this::livesHotbar));
|
||||||
if(life.empty() && immediate) {
|
if(life.empty() && immediate) {
|
||||||
eliminate(player);
|
eliminate(player);
|
||||||
return true;
|
return true;
|
||||||
|
@ -225,9 +235,9 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul
|
||||||
ImmutableSet.copyOf(getMatch().getParticipatingPlayers())
|
ImmutableSet.copyOf(getMatch().getParticipatingPlayers())
|
||||||
.stream()
|
.stream()
|
||||||
.filter(this::eliminated)
|
.filter(this::eliminated)
|
||||||
.forEach(participating -> {
|
.forEach(eliminated -> {
|
||||||
match.setPlayerParty(participating, match.getDefaultParty());
|
match.setPlayerParty(eliminated, match.getDefaultParty());
|
||||||
world.spawnParticle(Particle.SMOKE_LARGE, player.getLocation(), 5);
|
world.spawnParticle(Particle.SMOKE_LARGE, eliminated.getLocation(), 5);
|
||||||
});
|
});
|
||||||
victory.invalidateAndCheckEnd();
|
victory.invalidateAndCheckEnd();
|
||||||
});
|
});
|
||||||
|
@ -269,15 +279,14 @@ public class BlitzMatchModuleImpl extends MatchModule implements BlitzMatchModul
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
public void onDeath(MatchPlayerDeathEvent event) {
|
public void onDeath(MatchPlayerDeathEvent event) {
|
||||||
final MatchPlayer player = event.getVictim();
|
event.getVictim()
|
||||||
if(player.competitor().isPresent()) {
|
.competitor()
|
||||||
increment(player, -1, false, true);
|
.ifPresent(competitor -> increment(event.getVictim(), -1, false, true));
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onRelease(ParticipantReleaseEvent event) {
|
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();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void update() {
|
protected void update() {
|
||||||
competitor().getMatch().callEvent(new LivesEvent(this));
|
competitor().getMatch().callEvent(new LivesEvent(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +67,8 @@ public abstract class LivesBase implements Lives {
|
||||||
public BaseComponent remaining() {
|
public BaseComponent remaining() {
|
||||||
return new Component(
|
return new Component(
|
||||||
new TranslatableComponent(
|
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)
|
new Component(current(), ChatColor.YELLOW)
|
||||||
),
|
),
|
||||||
ChatColor.AQUA
|
ChatColor.AQUA
|
||||||
|
@ -103,7 +104,7 @@ public abstract class LivesBase implements Lives {
|
||||||
new TranslatableComponent(
|
new TranslatableComponent(
|
||||||
(delta > 0 ? "lives.change.gained."
|
(delta > 0 ? "lives.change.gained."
|
||||||
: "lives.change.lost.") + (absDelta == 1 ? "singular"
|
: "lives.change.lost.") + (absDelta == 1 ? "singular"
|
||||||
: "plural"),
|
: "plural"),
|
||||||
new Component(absDelta, ChatColor.AQUA)
|
new Component(absDelta, ChatColor.AQUA)
|
||||||
),
|
),
|
||||||
ChatColor.WHITE
|
ChatColor.WHITE
|
||||||
|
|
|
@ -1,6 +1,11 @@
|
||||||
package tc.oc.pgm.blitz;
|
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.api.docs.PlayerId;
|
||||||
|
import tc.oc.commons.core.chat.Component;
|
||||||
|
import tc.oc.commons.core.chat.Components;
|
||||||
import tc.oc.pgm.match.Competitor;
|
import tc.oc.pgm.match.Competitor;
|
||||||
|
|
||||||
public class LivesTeam extends LivesBase {
|
public class LivesTeam extends LivesBase {
|
||||||
|
@ -24,6 +29,24 @@ public class LivesTeam extends LivesBase {
|
||||||
return false;
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return competitor().hashCode();
|
return competitor().hashCode();
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package tc.oc.pgm.modules;
|
package tc.oc.pgm.modules;
|
||||||
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||||
import tc.oc.pgm.events.ListenerScope;
|
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.filters.query.EntitySpawnQuery;
|
||||||
import tc.oc.pgm.match.MatchModule;
|
import tc.oc.pgm.match.MatchModule;
|
||||||
import tc.oc.pgm.match.MatchScope;
|
import tc.oc.pgm.match.MatchScope;
|
||||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
|
||||||
|
|
||||||
@ListenerScope(MatchScope.LOADED)
|
@ListenerScope(MatchScope.LOADED)
|
||||||
public class MobsMatchModule extends MatchModule implements Listener {
|
public class MobsMatchModule extends MatchModule implements Listener {
|
||||||
|
@ -37,10 +37,9 @@ public class MobsMatchModule extends MatchModule implements Listener {
|
||||||
getMatch().getWorld().setSpawnFlags(false, false);
|
getMatch().getWorld().setSpawnFlags(false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.LOWEST)
|
||||||
public void checkSpawn(final CreatureSpawnEvent event) {
|
public void checkSpawn(final CreatureSpawnEvent event) {
|
||||||
if(!match.module(MutationMatchModule.class).map(mmm -> mmm.allowMob(event.getSpawnReason())).orElse(false) &&
|
if(event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) {
|
||||||
event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) {
|
|
||||||
event.setCancelled(mobsFilter.query(new EntitySpawnQuery(event, event.getEntity(), event.getSpawnReason())).isDenied());
|
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.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
|
||||||
import tc.oc.commons.core.util.MapUtils;
|
import tc.oc.commons.core.util.MapUtils;
|
||||||
import tc.oc.commons.core.random.RandomUtils;
|
import tc.oc.commons.core.random.RandomUtils;
|
||||||
import tc.oc.pgm.Config;
|
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)));
|
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;
|
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 org.bukkit.inventory.ItemStack;
|
||||||
import tc.oc.commons.bukkit.inventory.Slot;
|
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.KillReward;
|
||||||
import tc.oc.pgm.killreward.KillRewardMatchModule;
|
import tc.oc.pgm.killreward.KillRewardMatchModule;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
import tc.oc.pgm.kits.Kit;
|
import tc.oc.pgm.kits.Kit;
|
||||||
import tc.oc.pgm.kits.KitPlayerFacet;
|
import tc.oc.pgm.kits.KitPlayerFacet;
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
|
@ -11,19 +18,22 @@ import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A mutation module that injects special kits on spawn and/or kill.
|
* 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 List<Kit> kits;
|
||||||
protected final Map<MatchPlayer, List<Kit>> playerKits;
|
protected final Map<MatchPlayer, List<Kit>> playerKits;
|
||||||
protected final Map<MatchPlayer, Map<Slot, ItemStack>> savedSlots;
|
protected final Map<MatchPlayer, Map<Slot, ItemStack>> savedSlots;
|
||||||
|
protected final Set<Material> itemRemove;
|
||||||
protected final List<KillReward> rewards;
|
protected final List<KillReward> rewards;
|
||||||
protected final boolean force;
|
protected final boolean force;
|
||||||
|
|
||||||
|
@ -32,6 +42,7 @@ public class KitMutation extends MutationModule {
|
||||||
this.kits = new ArrayList<>();
|
this.kits = new ArrayList<>();
|
||||||
this.playerKits = new WeakHashMap<>();
|
this.playerKits = new WeakHashMap<>();
|
||||||
this.savedSlots = new WeakHashMap<>();
|
this.savedSlots = new WeakHashMap<>();
|
||||||
|
this.itemRemove = new HashSet<>();
|
||||||
this.rewards = new ArrayList<>();
|
this.rewards = new ArrayList<>();
|
||||||
this.force = force;
|
this.force = force;
|
||||||
}
|
}
|
||||||
|
@ -59,6 +70,7 @@ public class KitMutation extends MutationModule {
|
||||||
List<Kit> kits = new ArrayList<>();
|
List<Kit> kits = new ArrayList<>();
|
||||||
kits(player, kits);
|
kits(player, kits);
|
||||||
playerKits.put(player, kits);
|
playerKits.put(player, kits);
|
||||||
|
kits.forEach(kit -> Optionals.cast(kit, ItemKit.class).ifPresent(items -> itemRemove.add(items.item().getType())));
|
||||||
saved().forEach(slot -> {
|
saved().forEach(slot -> {
|
||||||
slot.item(player.getInventory()).ifPresent(item -> {
|
slot.item(player.getInventory()).ifPresent(item -> {
|
||||||
Map<Slot, ItemStack> slots = savedSlots.getOrDefault(player, new HashMap<>());
|
Map<Slot, ItemStack> slots = savedSlots.getOrDefault(player, new HashMap<>());
|
||||||
|
@ -88,22 +100,34 @@ public class KitMutation extends MutationModule {
|
||||||
return Stream.empty();
|
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
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
super.enable();
|
super.enable();
|
||||||
match.module(KillRewardMatchModule.class).get().rewards().addAll(rewards);
|
match().module(KillRewardMatchModule.class).get().rewards().addAll(rewards);
|
||||||
if(match.hasStarted()) {
|
if(match().hasStarted()) {
|
||||||
match.participants().forEach(this::apply);
|
match().participants().forEach(this::apply);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
match.module(KillRewardMatchModule.class).get().rewards().removeAll(rewards);
|
match().module(KillRewardMatchModule.class).get().rewards().removeAll(rewards);
|
||||||
match.participants().forEach(this::remove);
|
match().participants().forEach(this::remove);
|
||||||
kits.clear();
|
kits.clear();
|
||||||
playerKits.clear();
|
playerKits.clear();
|
||||||
savedSlots.clear();
|
savedSlots.clear();
|
||||||
|
itemRemove.clear();
|
||||||
rewards.clear();
|
rewards.clear();
|
||||||
super.disable();
|
super.disable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,8 @@
|
||||||
package tc.oc.pgm.mutation.types;
|
package tc.oc.pgm.mutation.types;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.LivingEntity;
|
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
import org.bukkit.inventory.EntityEquipment;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import tc.oc.commons.bukkit.item.ItemBuilder;
|
import tc.oc.commons.bukkit.item.ItemBuilder;
|
||||||
import tc.oc.commons.core.random.AdvancingEntropy;
|
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.Match;
|
||||||
import tc.oc.pgm.match.MatchScope;
|
import tc.oc.pgm.match.MatchScope;
|
||||||
import tc.oc.pgm.mutation.Mutation;
|
import tc.oc.pgm.mutation.Mutation;
|
||||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
|
||||||
|
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
|
@ -28,69 +23,59 @@ import java.util.Random;
|
||||||
* of breaking the match state.
|
* of breaking the match state.
|
||||||
*/
|
*/
|
||||||
@ListenerScope(MatchScope.RUNNING)
|
@ListenerScope(MatchScope.RUNNING)
|
||||||
public abstract class MutationModule implements Listener {
|
public interface MutationModule extends Listener {
|
||||||
|
|
||||||
protected final Match match;
|
Match match();
|
||||||
protected final World world;
|
|
||||||
protected final Entropy entropy;
|
|
||||||
protected final Random random;
|
|
||||||
|
|
||||||
/**
|
default void enable() {
|
||||||
* Constructed when {@link MutationMatchModule#load()}
|
match().registerEventsAndRepeatables(this);
|
||||||
* 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 disable() {
|
||||||
* Called when the match starts.
|
match().unregisterEvents(this);
|
||||||
*
|
match().unregisterRepeatable(this);
|
||||||
* 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 World world() {
|
||||||
* Called when the match ends.
|
return match().getWorld();
|
||||||
*
|
|
||||||
* 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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static ItemStack item(Material material, int amount) {
|
default Entropy entropy() {
|
||||||
return new ItemBuilder().material(material).amount(amount).unbreakable(true).shareable(false).get();
|
return match().entropyForTick();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static ItemStack item(Material material) {
|
default Random random() {
|
||||||
return item(material, 1);
|
return match().getRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected <E extends Entity> E spawn(Location location, Class<E> entityClass) {
|
abstract class Impl implements MutationModule {
|
||||||
E entity = world.spawn(location, entityClass);
|
|
||||||
if(entity instanceof LivingEntity) {
|
private final Match match;
|
||||||
LivingEntity living = (LivingEntity) entity;
|
private final Entropy entropy;
|
||||||
living.setCanPickupItems(false);
|
|
||||||
living.setRemoveWhenFarAway(true);
|
public Impl(final Match match) {
|
||||||
EntityEquipment equipment = living.getEquipment();
|
this.match = match;
|
||||||
equipment.setHelmetDropChance(0);
|
this.entropy = new AdvancingEntropy(match.entropyForTick().randomLong());
|
||||||
equipment.setChestplateDropChance(0);
|
|
||||||
equipment.setLeggingsDropChance(0);
|
|
||||||
equipment.setBootsDropChance(0);
|
|
||||||
}
|
}
|
||||||
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;
|
package tc.oc.pgm.mutation.types;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import tc.oc.commons.core.scheduler.Task;
|
|
||||||
import tc.oc.commons.core.stream.Collectors;
|
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.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
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.
|
* A mutation module that executes a task on random {@link MatchPlayer}s.
|
||||||
*/
|
*/
|
||||||
public abstract class TargetMutation extends MutationModule {
|
public interface TargetMutation extends MutationModule {
|
||||||
|
|
||||||
private final Duration frequency;
|
|
||||||
private Task task;
|
|
||||||
|
|
||||||
public TargetMutation(final Match match, Duration frequency) {
|
|
||||||
super(match);
|
|
||||||
this.frequency = frequency;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Execute a task on the given randomly selected players.
|
* Execute a task on the given randomly selected players.
|
||||||
* @param players a list of players, which size is determined by {@link #targets()}.
|
* @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.
|
* Determine the number of random players to target.
|
||||||
|
*
|
||||||
* If there are no enough players on the server, it is possible
|
* If there are no enough players on the server, it is possible
|
||||||
* that the number of targets is less than expected.
|
* that the number of targets is less than expected.
|
||||||
* @return number of targets.
|
* @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.
|
* Generate a list of random players.
|
||||||
* @return the random players.
|
* @return the random players.
|
||||||
*/
|
*/
|
||||||
public List<MatchPlayer> search() {
|
default List<MatchPlayer> search() {
|
||||||
return match.participants()
|
return match().participants()
|
||||||
.filter(MatchPlayer::isSpawned)
|
.filter(MatchPlayer::isSpawned)
|
||||||
.collect(Collectors.toRandomSubList(entropy, targets()));
|
.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
|
@Override
|
||||||
public void enable() {
|
default void enable() {
|
||||||
super.enable();
|
MutationModule.super.enable();
|
||||||
this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> execute(search()));
|
target();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Repeatable(interval = @Time(seconds = 1))
|
||||||
public void disable() {
|
default void tick() {
|
||||||
if(task != null) {
|
Instant now = match().getInstantNow(), next = next();
|
||||||
task.cancel();
|
if(next == null) {
|
||||||
task = 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;
|
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 org.bukkit.Material;
|
||||||
|
import tc.oc.commons.bukkit.chat.WarningComponent;
|
||||||
import tc.oc.commons.bukkit.inventory.ArmorType;
|
import tc.oc.commons.bukkit.inventory.ArmorType;
|
||||||
import tc.oc.commons.bukkit.inventory.Slot;
|
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.doublejump.DoubleJumpKit;
|
||||||
import tc.oc.pgm.kits.ItemKit;
|
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.kits.SlotItemKit;
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
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 tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public class ElytraMutation extends KitMutation {
|
public class ElytraMutation extends KitMutation {
|
||||||
|
@ -30,13 +44,57 @@ public class ElytraMutation extends KitMutation {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(MatchPlayer player) {
|
public void remove(MatchPlayer player) {
|
||||||
// If the player is mid-air, give them a totem so they don't fall and die
|
// Anyone left gliding will be taken care of by the ground stop order
|
||||||
if(player.getBukkit().isGliding()) {
|
if(!player.getBukkit().isGliding()) {
|
||||||
ItemKitApplicator applicator = new ItemKitApplicator();
|
super.remove(player);
|
||||||
applicator.put(Slot.OffHand.offHand(), item(Material.TOTEM), false);
|
|
||||||
applicator.apply(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 {
|
public class EnchantmentMutation extends KitMutation {
|
||||||
|
|
||||||
final static ImmutableMap<Integer, Integer> LEVELS_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
final static ImmutableMap<Integer, Integer> LEVELS_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||||
.put(1, 10)
|
.put(1, 25)
|
||||||
.put(2, 3)
|
.put(2, 5)
|
||||||
.put(3, 1)
|
.put(3, 1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final static ImmutableMap<Enchantment, Integer> ARMOR_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
final static ImmutableMap<Enchantment, Integer> ARMOR_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
.put(Enchantment.PROTECTION_ENVIRONMENTAL, 15)
|
.put(Enchantment.PROTECTION_ENVIRONMENTAL, 15)
|
||||||
.put(Enchantment.PROTECTION_PROJECTILE, 10)
|
.put(Enchantment.PROTECTION_PROJECTILE, 10)
|
||||||
.put(Enchantment.DURABILITY, 10)
|
|
||||||
.put(Enchantment.PROTECTION_EXPLOSIONS, 5)
|
.put(Enchantment.PROTECTION_EXPLOSIONS, 5)
|
||||||
.put(Enchantment.PROTECTION_FALL, 5)
|
|
||||||
.put(Enchantment.PROTECTION_FIRE, 5)
|
.put(Enchantment.PROTECTION_FIRE, 5)
|
||||||
.put(Enchantment.THORNS, 1)
|
.put(Enchantment.THORNS, 1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final static ImmutableMap<Enchantment, Integer> BOOTS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
final static ImmutableMap<Enchantment, Integer> BOOTS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
.putAll(ARMOR_MAP)
|
.putAll(ARMOR_MAP)
|
||||||
.put(Enchantment.WATER_WORKER, 5)
|
.put(Enchantment.PROTECTION_FALL, 10)
|
||||||
.put(Enchantment.DEPTH_STRIDER, 3)
|
.put(Enchantment.DEPTH_STRIDER, 3)
|
||||||
.put(Enchantment.FROST_WALKER, 1)
|
.put(Enchantment.FROST_WALKER, 1)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final static ImmutableMap<Enchantment, Integer> WEAPONS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
final static ImmutableMap<Enchantment, Integer> WEAPONS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
.put(Enchantment.DAMAGE_ALL, 15)
|
.put(Enchantment.DAMAGE_ALL, 15)
|
||||||
.put(Enchantment.DURABILITY, 10)
|
|
||||||
.put(Enchantment.KNOCKBACK, 10)
|
.put(Enchantment.KNOCKBACK, 10)
|
||||||
.put(Enchantment.MENDING, 5)
|
.put(Enchantment.MENDING, 5)
|
||||||
.put(Enchantment.SWEEPING_EDGE, 5)
|
.put(Enchantment.SWEEPING_EDGE, 5)
|
||||||
|
@ -53,7 +50,6 @@ public class EnchantmentMutation extends KitMutation {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
final static ImmutableMap<Enchantment, Integer> TOOLS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
final static ImmutableMap<Enchantment, Integer> TOOLS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
.put(Enchantment.DURABILITY, 10)
|
|
||||||
.put(Enchantment.DIG_SPEED, 10)
|
.put(Enchantment.DIG_SPEED, 10)
|
||||||
.put(Enchantment.SILK_TOUCH, 5)
|
.put(Enchantment.SILK_TOUCH, 5)
|
||||||
.put(Enchantment.LOOT_BONUS_BLOCKS, 5)
|
.put(Enchantment.LOOT_BONUS_BLOCKS, 5)
|
||||||
|
@ -116,9 +112,9 @@ public class EnchantmentMutation extends KitMutation {
|
||||||
byEntity.put(item, ImmutableMap.copyOf(item.getEnchantments()));
|
byEntity.put(item, ImmutableMap.copyOf(item.getEnchantments()));
|
||||||
savedEnchantments.put(entity, byEntity);
|
savedEnchantments.put(entity, byEntity);
|
||||||
// Apply the new enchantments
|
// Apply the new enchantments
|
||||||
int amountOfEnchants = LEVELS.choose(entropy);
|
int amountOfEnchants = LEVELS.choose(entropy());
|
||||||
for(int i = 0; i < amountOfEnchants; i++) {
|
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);
|
super.apply(player);
|
||||||
player.getInventory().forEach(item -> {
|
player.getInventory().forEach(item -> {
|
||||||
// Random number of enchantments on each 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++) {
|
for(int i = 0; i < numberOfEnchants; i++) {
|
||||||
apply(item, player.getBukkit().getEquipment());
|
apply(item, player.getBukkit().getEquipment());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,11 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.AbstractHorse;
|
import org.bukkit.entity.AbstractHorse;
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
import org.bukkit.entity.Horse;
|
import org.bukkit.entity.Horse;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
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.HorseInventory;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.SpawnEggMeta;
|
import org.bukkit.inventory.meta.SpawnEggMeta;
|
||||||
|
@ -17,23 +16,23 @@ import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
import tc.oc.commons.core.random.WeightedRandomChooser;
|
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.FreeItemKit;
|
||||||
import tc.oc.pgm.kits.Kit;
|
import tc.oc.pgm.kits.Kit;
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
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.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>()
|
final static ImmutableMap<EntityType, Integer> TYPE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
.put(EntityType.HORSE, 100)
|
.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.SKELETON_HORSE, 5)
|
||||||
//.put(EntityType.ZOMBIE_HORSE, 5)
|
//.put(EntityType.ZOMBIE_HORSE, 5)
|
||||||
//.put(EntityType.LLAMA, 1)
|
//.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<EntityType, Integer> TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP);
|
||||||
final static WeightedRandomChooser<Material, Integer> ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP);
|
final static WeightedRandomChooser<Material, Integer> ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP);
|
||||||
|
|
||||||
final Map<MatchPlayer, AbstractHorse> horses;
|
|
||||||
|
|
||||||
public EquestrianMutation(Match match) {
|
public EquestrianMutation(Match match) {
|
||||||
super(match, false);
|
super(match, false);
|
||||||
this.horses = new WeakHashMap<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void enable() {
|
||||||
super.disable();
|
super.enable();
|
||||||
match.participants().forEach(this::remove);
|
this.itemRemove.add(Material.LEATHER);
|
||||||
horses.clear();
|
this.itemRemove.addAll(ARMOR_MAP.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -72,7 +68,7 @@ public class EquestrianMutation extends KitMutation {
|
||||||
if(!spawnable(location)) {
|
if(!spawnable(location)) {
|
||||||
ItemStack item = item(Material.MONSTER_EGG);
|
ItemStack item = item(Material.MONSTER_EGG);
|
||||||
SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta();
|
SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta();
|
||||||
egg.setSpawnedType(TYPES.choose(entropy));
|
egg.setSpawnedType(TYPES.choose(entropy()));
|
||||||
item.setItemMeta(egg);
|
item.setItemMeta(egg);
|
||||||
kits.add(new FreeItemKit(item));
|
kits.add(new FreeItemKit(item));
|
||||||
}
|
}
|
||||||
|
@ -83,38 +79,31 @@ public class EquestrianMutation extends KitMutation {
|
||||||
super.apply(player);
|
super.apply(player);
|
||||||
Location location = player.getLocation();
|
Location location = player.getLocation();
|
||||||
if(spawnable(location)) {
|
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
|
@Override
|
||||||
public void remove(MatchPlayer player) {
|
public AbstractHorse register(AbstractHorse horse, @Nullable MatchPlayer owner) {
|
||||||
super.remove(player);
|
super.register(horse, owner);
|
||||||
AbstractHorse horse = horses.remove(player);
|
if(owner != null) {
|
||||||
if(horse != null) {
|
horse.setAdult();
|
||||||
horse.ejectAll();
|
horse.setJumpStrength(2 * entropy().randomDouble());
|
||||||
horse.remove();
|
horse.setDomestication(1);
|
||||||
}
|
horse.setMaxDomestication(1);
|
||||||
}
|
horse.setTamed(true);
|
||||||
|
horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0));
|
||||||
public void setup(MatchPlayer player, AbstractHorse horse) {
|
horse.setOwner(owner.getBukkit());
|
||||||
horses.put(player, horse);
|
horse.setPassenger(owner.getBukkit());
|
||||||
horse.setAdult();
|
cast(horse, Horse.class).ifPresent(horsey -> {
|
||||||
horse.setJumpStrength(2 * entropy.randomDouble());
|
horsey.setStyle(entropy().randomElement(Horse.Style.values()));
|
||||||
horse.setDomestication(1);
|
horsey.setColor(entropy().randomElement(Horse.Color.values()));
|
||||||
horse.setMaxDomestication(1);
|
HorseInventory inventory = horsey.getInventory();
|
||||||
horse.setTamed(true);
|
inventory.setSaddle(item(Material.SADDLE));
|
||||||
horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0));
|
inventory.setArmor(item(ARMOR.choose(entropy())));
|
||||||
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)));
|
|
||||||
}
|
}
|
||||||
|
return horse;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean spawnable(Location location) {
|
public boolean spawnable(Location location) {
|
||||||
|
@ -129,22 +118,11 @@ public class EquestrianMutation extends KitMutation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
public void onEntitySpawn(PlayerSpawnEntityEvent event) {
|
public void onEntityDamage(EntityDamageByEntityEvent event) {
|
||||||
Entity entity = event.getEntity();
|
if(playerByEntity(event.getEntity()).flatMap(MatchPlayer::competitor)
|
||||||
if(TYPE_MAP.containsKey(entity.getType())) {
|
.equals(match().participant(event.getDamager()).flatMap(MatchPlayer::competitor))) {
|
||||||
match.participant(event.getPlayer())
|
event.setCancelled(true);
|
||||||
.ifPresent(player -> setup(player, (AbstractHorse) event.getEntity()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@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 com.google.common.collect.Range;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.enchantments.Enchantment;
|
import org.bukkit.enchantments.Enchantment;
|
||||||
import org.bukkit.entity.TNTPrimed;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.entity.ExplosionPrimeEvent;
|
import org.bukkit.event.entity.ExplosionPrimeEvent;
|
||||||
import org.bukkit.inventory.PlayerInventory;
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
import tc.oc.commons.bukkit.item.ItemBuilder;
|
import tc.oc.commons.bukkit.item.ItemBuilder;
|
||||||
import tc.oc.commons.core.collection.WeakHashSet;
|
|
||||||
import tc.oc.pgm.killreward.KillReward;
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
import tc.oc.pgm.kits.FreeItemKit;
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
import tc.oc.pgm.kits.ItemKit;
|
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 static Range<Integer> RADIUS = Range.openClosed(0, 4);
|
||||||
|
|
||||||
final WeakHashSet<TNTPrimed> tracked;
|
|
||||||
|
|
||||||
public ExplosiveMutation(Match match) {
|
public ExplosiveMutation(Match match) {
|
||||||
super(match, false);
|
super(match, false);
|
||||||
this.tracked = new WeakHashSet<>();
|
|
||||||
this.rewards.add(new KillReward(TNT));
|
this.rewards.add(new KillReward(TNT));
|
||||||
this.rewards.add(new KillReward(ARROWS));
|
this.rewards.add(new KillReward(ARROWS));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disable() {
|
|
||||||
super.disable();
|
|
||||||
tracked.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kits(MatchPlayer player, List<Kit> kits) {
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
super.kits(player, kits);
|
super.kits(player, kits);
|
||||||
PlayerInventory inv = player.getInventory();
|
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.TNT)) kits.add(TNT);
|
||||||
if(!inv.contains(Material.FLINT_AND_STEEL)) kits.add(LIGHTER);
|
if(!inv.contains(Material.FLINT_AND_STEEL)) kits.add(LIGHTER);
|
||||||
} else { // fire bow and arrows kit
|
} 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)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
public void onExplosionPrime(ExplosionPrimeEvent event) {
|
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;
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import tc.oc.pgm.gamerules.GameRulesMatchModule;
|
import tc.oc.pgm.gamerules.GameRulesMatchModule;
|
||||||
import tc.oc.pgm.killreward.KillReward;
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
import tc.oc.pgm.kits.FreeItemKit;
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
@ -26,13 +25,13 @@ public class HardcoreMutation extends KitMutation {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameRulesMatchModule rules() {
|
public GameRulesMatchModule rules() {
|
||||||
return match.module(GameRulesMatchModule.class).get();
|
return match().module(GameRulesMatchModule.class).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
super.enable();
|
super.enable();
|
||||||
previous = world.getGameRuleValue(KEY);
|
previous = world().getGameRuleValue(KEY);
|
||||||
rules().gameRules().put(KEY, "false");
|
rules().gameRules().put(KEY, "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,7 +39,7 @@ public class HardcoreMutation extends KitMutation {
|
||||||
public void disable() {
|
public void disable() {
|
||||||
rules().gameRules().remove(KEY);
|
rules().gameRules().remove(KEY);
|
||||||
if(previous != null) {
|
if(previous != null) {
|
||||||
world.setGameRuleValue(KEY, previous);
|
world().setGameRuleValue(KEY, previous);
|
||||||
}
|
}
|
||||||
super.disable();
|
super.disable();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package tc.oc.pgm.mutation.types.kit;
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import tc.oc.pgm.killreward.KillReward;
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
import tc.oc.pgm.kits.FreeItemKit;
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
import tc.oc.pgm.kits.HealthKit;
|
import tc.oc.pgm.kits.HealthKit;
|
||||||
|
|
|
@ -4,6 +4,7 @@ import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.entity.EntityType;
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import org.bukkit.inventory.meta.SpawnEggMeta;
|
import org.bukkit.inventory.meta.SpawnEggMeta;
|
||||||
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
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.kits.Kit;
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
import tc.oc.pgm.mutation.types.KitMutation;
|
import tc.oc.pgm.mutation.types.EntityMutation;
|
||||||
|
|
||||||
import java.util.List;
|
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>()
|
final static ImmutableMap<EntityType, Integer> TYPE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
.put(EntityType.ZOMBIE, 50)
|
.put(EntityType.ZOMBIE, 50)
|
||||||
|
@ -37,17 +38,17 @@ public class MobsMutation extends KitMutation {
|
||||||
final static Range<Integer> AMOUNT = Range.closed(1, 3);
|
final static Range<Integer> AMOUNT = Range.closed(1, 3);
|
||||||
|
|
||||||
public MobsMutation(Match match) {
|
public MobsMutation(Match match) {
|
||||||
super(match, true);
|
super(match, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void kits(MatchPlayer player, List<Kit> kits) {
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
super.kits(player, kits);
|
super.kits(player, kits);
|
||||||
int eggs = entropy.randomInt(AMOUNT);
|
int eggs = entropy().randomInt(AMOUNT);
|
||||||
for(int i = 0; i < eggs; i++) {
|
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();
|
SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta();
|
||||||
egg.setSpawnedType(TYPES.choose(entropy));
|
egg.setSpawnedType(TYPES.choose(entropy()));
|
||||||
item.setItemMeta(egg);
|
item.setItemMeta(egg);
|
||||||
kits.add(new FreeItemKit(item));
|
kits.add(new FreeItemKit(item));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ public abstract class NoFallMutation extends KitMutation {
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisableDamageMatchModule damage() {
|
public DisableDamageMatchModule damage() {
|
||||||
return match.module(DisableDamageMatchModule.class).get();
|
return match().module(DisableDamageMatchModule.class).get();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -68,13 +68,13 @@ public class PotionMutation extends KitMutation {
|
||||||
@Override
|
@Override
|
||||||
public void kits(MatchPlayer player, List<Kit> kits) {
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
super.kits(player, kits);
|
super.kits(player, kits);
|
||||||
int numberOfPotions = entropy.randomInt(AMOUNT_RANGE);
|
int numberOfPotions = entropy().randomInt(AMOUNT_RANGE);
|
||||||
for(int i = 0; i < numberOfPotions; i++) {
|
for(int i = 0; i < numberOfPotions; i++) {
|
||||||
WeightedRandomChooser<PotionEffectType, Integer> type;
|
WeightedRandomChooser<PotionEffectType, Integer> type;
|
||||||
WeightedRandomChooser<Material, Integer> material;
|
WeightedRandomChooser<Material, Integer> material;
|
||||||
Range<Integer> range;
|
Range<Integer> range;
|
||||||
// Determine whether the potion will be "good" or "bad"
|
// Determine whether the potion will be "good" or "bad"
|
||||||
if(random.nextBoolean()) {
|
if(random().nextBoolean()) {
|
||||||
type = BAD;
|
type = BAD;
|
||||||
material = BAD_BOTTLE;
|
material = BAD_BOTTLE;
|
||||||
range = BAD_DURATION_RANGE;
|
range = BAD_DURATION_RANGE;
|
||||||
|
@ -84,10 +84,10 @@ public class PotionMutation extends KitMutation {
|
||||||
range = GOOD_DURATION_RANGE;
|
range = GOOD_DURATION_RANGE;
|
||||||
}
|
}
|
||||||
// Choose all the random attributes
|
// Choose all the random attributes
|
||||||
PotionEffectType effect = type.choose(entropy);
|
PotionEffectType effect = type.choose(entropy());
|
||||||
Material bottle = material.choose(entropy);
|
Material bottle = material.choose(entropy());
|
||||||
int duration = 20 * entropy.randomInt(range);
|
int duration = 20 * entropy().randomInt(range);
|
||||||
int amplifier = entropy.randomInt(AMPLIFIER_RANGE);
|
int amplifier = entropy().randomInt(AMPLIFIER_RANGE);
|
||||||
// Apply the attributes to the item stack
|
// Apply the attributes to the item stack
|
||||||
ItemStack potion = item(bottle);
|
ItemStack potion = item(bottle);
|
||||||
PotionMeta meta = (PotionMeta) potion.getItemMeta();
|
PotionMeta meta = (PotionMeta) potion.getItemMeta();
|
||||||
|
|
|
@ -3,20 +3,17 @@ package tc.oc.pgm.mutation.types.kit;
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
import org.bukkit.enchantments.Enchantment;
|
import org.bukkit.enchantments.Enchantment;
|
||||||
import org.bukkit.entity.Arrow;
|
import org.bukkit.entity.LivingEntity;
|
||||||
import org.bukkit.entity.Entity;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.entity.TippedArrow;
|
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.EventPriority;
|
import org.bukkit.event.EventPriority;
|
||||||
import org.bukkit.event.entity.EntityShootBowEvent;
|
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||||
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
|
||||||
import org.bukkit.inventory.Inventory;
|
import org.bukkit.inventory.Inventory;
|
||||||
import org.bukkit.metadata.FixedMetadataValue;
|
|
||||||
import org.bukkit.potion.PotionEffect;
|
import org.bukkit.potion.PotionEffect;
|
||||||
import org.bukkit.potion.PotionEffectType;
|
import org.bukkit.potion.PotionEffectType;
|
||||||
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
import tc.oc.commons.core.random.WeightedRandomChooser;
|
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.killreward.KillReward;
|
||||||
import tc.oc.pgm.kits.FreeItemKit;
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
import tc.oc.pgm.kits.ItemKit;
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
|
@ -26,6 +23,7 @@ import tc.oc.pgm.match.MatchPlayer;
|
||||||
import tc.oc.pgm.mutation.types.KitMutation;
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
public class ProjectileMutation extends KitMutation {
|
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 BOW = new FreeItemKit(item(Material.BOW));
|
||||||
final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 16));
|
final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 16));
|
||||||
|
|
||||||
final static String KEY = "is_modified_arrow";
|
|
||||||
|
|
||||||
public ProjectileMutation(Match match) {
|
public ProjectileMutation(Match match) {
|
||||||
super(match, false);
|
super(match, false);
|
||||||
this.rewards.add(new KillReward(ARROWS));
|
this.rewards.add(new KillReward(ARROWS));
|
||||||
|
@ -50,7 +46,7 @@ public class ProjectileMutation extends KitMutation {
|
||||||
public void apply(MatchPlayer player) {
|
public void apply(MatchPlayer player) {
|
||||||
super.apply(player);
|
super.apply(player);
|
||||||
Inventory inventory = player.getInventory();
|
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
|
@Override
|
||||||
|
@ -62,23 +58,10 @@ public class ProjectileMutation extends KitMutation {
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
public void onBowShoot(EntityShootBowEvent event) {
|
public void onProjectileHit(ProjectileHitEvent event) {
|
||||||
Entity projectile = event.getProjectile();
|
Optionals.cast(Optional.ofNullable(event.getHitEntity()), LivingEntity.class)
|
||||||
if(projectile instanceof Arrow && (!projectile.hasMetadata(KEY) || !projectile.getMetadata(KEY, PGM.get()).asBoolean())) {
|
.filter(entity -> Optional.ofNullable(event.getEntity().getShooter()).filter(shooter -> shooter instanceof Player).isPresent())
|
||||||
Arrow arrow = (Arrow) projectile;
|
.ifPresent(entity -> entity.addPotionEffect(new PotionEffect(POTIONS.choose(entropy()), 20 * entropy().randomInt(DURATION_RANGE), entropy().randomInt(AMPLIFIER_RANGE)), true));
|
||||||
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));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import tc.oc.pgm.match.MatchScope;
|
||||||
import tc.oc.pgm.mutation.types.MutationModule;
|
import tc.oc.pgm.mutation.types.MutationModule;
|
||||||
import tc.oc.pgm.teams.TeamMatchModule;
|
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 Range<Integer> LIVES = Range.closed(1, 3);
|
||||||
final static Fraction TEAM_CHANCE = Fraction.ONE_QUARTER;
|
final static Fraction TEAM_CHANCE = Fraction.ONE_QUARTER;
|
||||||
|
@ -23,22 +23,22 @@ public class BlitzMutation extends MutationModule {
|
||||||
@Override
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
super.enable();
|
super.enable();
|
||||||
int lives = match.entropyForTick().randomInt(LIVES);
|
int lives = entropy().randomInt(LIVES);
|
||||||
Lives.Type type;
|
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;
|
type = Lives.Type.TEAM;
|
||||||
lives *= match.module(TeamMatchModule.class).get().getFullestTeam().getSize();
|
lives *= match().module(TeamMatchModule.class).get().getFullestTeam().getSize();
|
||||||
} else {
|
} else {
|
||||||
type = Lives.Type.INDIVIDUAL;
|
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
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
match.getScheduler(MatchScope.LOADED).createTask(() -> {
|
match().getScheduler(MatchScope.LOADED).createTask(() -> {
|
||||||
if(!match.isFinished()) {
|
if(!match().isFinished()) {
|
||||||
match.module(BlitzMatchModuleImpl.class).get().deactivate();
|
match().module(BlitzMatchModuleImpl.class).get().deactivate();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
super.disable();
|
super.disable();
|
||||||
|
|
|
@ -7,7 +7,7 @@ import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.mutation.types.MutationModule;
|
import tc.oc.pgm.mutation.types.MutationModule;
|
||||||
import tc.oc.pgm.rage.RageMatchModule;
|
import tc.oc.pgm.rage.RageMatchModule;
|
||||||
|
|
||||||
public class RageMutation extends MutationModule {
|
public class RageMutation extends MutationModule.Impl {
|
||||||
|
|
||||||
RageMatchModule rage;
|
RageMatchModule rage;
|
||||||
|
|
||||||
|
@ -26,4 +26,5 @@ public class RageMutation extends MutationModule {
|
||||||
super.disable();
|
super.disable();
|
||||||
rage = null;
|
rage = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@ import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
import tc.oc.pgm.match.Repeatable;
|
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.kit.EnchantmentMutation;
|
||||||
import tc.oc.pgm.mutation.types.TargetMutation;
|
import tc.oc.pgm.mutation.types.TargetMutation;
|
||||||
import tc.oc.pgm.points.PointProviderAttributes;
|
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.points.RegionPointProvider;
|
||||||
import tc.oc.pgm.regions.CuboidRegion;
|
import tc.oc.pgm.regions.CuboidRegion;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.WeakHashMap;
|
|
||||||
|
|
||||||
import static tc.oc.commons.core.random.RandomUtils.nextBoolean;
|
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>()
|
final static ImmutableMap<Integer, Integer> AMOUNT_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||||
.put(3, 25)
|
.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> PASSENGER = new ImmutableWeightedRandomChooser<>(PASSENGER_MAP);
|
||||||
final static WeightedRandomChooser<EntityType, Integer> CUBE = new ImmutableWeightedRandomChooser<>(CUBE_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 DISTANCE = 15; // Max distance entities spawn from players
|
||||||
final static int PARTICIPANT_ENTITIES = 25; // Max entities on the field per participant
|
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
|
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 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 static int SPECIAL_MULTIPLIER = 3; // Multiplier for special attributes
|
||||||
|
|
||||||
final WeakHashMap<Entity, Instant> entities;
|
long time; // world time
|
||||||
long time;
|
Instant next; // next time to spawn entities
|
||||||
|
final PointProviderAttributes attributes; // attributes to choosing random points
|
||||||
|
|
||||||
public ApocalypseMutation(Match match) {
|
public ApocalypseMutation(Match match) {
|
||||||
super(match, Duration.ofSeconds(20));
|
super(match, false);
|
||||||
this.entities = new WeakHashMap<>();
|
this.attributes = new PointProviderAttributes(null, null, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the maximum amount of entities that can be spawned.
|
* Get the maximum amount of entities that can be spawned.
|
||||||
*/
|
*/
|
||||||
public int entitiesMax() {
|
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.
|
* Get the number of available slots are left for additional entities to spawn.
|
||||||
*/
|
*/
|
||||||
public int entitiesLeft() {
|
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.
|
* Generate a random spawn point given two locations.
|
||||||
*/
|
*/
|
||||||
public Optional<Location> location(Location start, Location end) {
|
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) {
|
public void spawn(Location location, boolean ground) {
|
||||||
int slots = entitiesLeft();
|
int slots = entitiesLeft();
|
||||||
int queued = AMOUNT.choose(entropy);
|
int queued = AMOUNT.choose(entropy());
|
||||||
// Remove any entities that may be over the max limit
|
// Remove any entities that may be over the max limit
|
||||||
despawn(queued - slots);
|
despawn(queued - slots);
|
||||||
// Determine whether the entities should be airborn
|
// Determine whether the entities should be airborn
|
||||||
int stack = STACK.choose(entropy);
|
int stack = STACK.choose(entropy());
|
||||||
boolean air = !ground || nextBoolean(random, SPECIAL_CHANCE);
|
boolean air = !ground || nextBoolean(random(), SPECIAL_CHANCE);
|
||||||
if(air) {
|
if(air) {
|
||||||
stack += (stack == 1 && random.nextBoolean() ? 1 : 0);
|
stack += (stack == 1 && random().nextBoolean() ? 1 : 0);
|
||||||
location.add(0, entropy.randomInt(AIR_OFFSET), 0);
|
location.add(0, entropy().randomInt(AIR_OFFSET), 0);
|
||||||
}
|
}
|
||||||
// Select the random entity chooser based on ground, air, and stacked
|
// Select the random entity chooser based on ground, air, and stacked
|
||||||
boolean stacked = stack > 1;
|
boolean stacked = stack > 1;
|
||||||
|
@ -163,7 +164,7 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
if(air) {
|
if(air) {
|
||||||
if(stacked) {
|
if(stacked) {
|
||||||
if(ground) {
|
if(ground) {
|
||||||
chooser = nextBoolean(random, SPECIAL_CHANCE) ? CUBE : FLYABLE;
|
chooser = nextBoolean(random(), SPECIAL_CHANCE) ? CUBE : FLYABLE;
|
||||||
} else {
|
} else {
|
||||||
chooser = FLYABLE;
|
chooser = FLYABLE;
|
||||||
}
|
}
|
||||||
|
@ -174,7 +175,7 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
if(stacked) {
|
if(stacked) {
|
||||||
chooser = GROUND;
|
chooser = GROUND;
|
||||||
} else {
|
} else {
|
||||||
chooser = random.nextBoolean() ? GROUND : RANGED;
|
chooser = random().nextBoolean() ? GROUND : RANGED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Select the specific entity types for the spawn,
|
// 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.
|
// but may have variations (like armor) between them.
|
||||||
List<EntityType> types = new ArrayList<>();
|
List<EntityType> types = new ArrayList<>();
|
||||||
for(int i = 0; i < stack; i++) {
|
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
|
// Spawn the mobs and stack them if required
|
||||||
for(int i = 0; i < queued; i++) {
|
for(int i = 0; i < queued; i++) {
|
||||||
Entity last = null;
|
Entity last = null;
|
||||||
for(EntityType type : types) {
|
for(EntityType type : types) {
|
||||||
Entity entity = spawn(location, type);
|
Entity entity = spawn(location, (Class<LivingEntity>) type.getEntityClass());
|
||||||
if(last != null) {
|
if(last != null) {
|
||||||
last.setPassenger(entity);
|
last.setPassenger(entity);
|
||||||
}
|
}
|
||||||
|
@ -198,20 +199,14 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Spawn an individual entitiy at a location given an entity type.
|
public LivingEntity spawn(Location location, Class<LivingEntity> entityClass, @Nullable MatchPlayer owner) {
|
||||||
* @param location the location to spawn at.
|
LivingEntity entity = super.spawn(location, entityClass, owner);
|
||||||
* @param type the type of entity.
|
EnchantmentMutation enchant = new EnchantmentMutation(match());
|
||||||
* @return the constructed entity.
|
|
||||||
*/
|
|
||||||
public LivingEntity spawn(Location location, EntityType type) {
|
|
||||||
EnchantmentMutation enchant = new EnchantmentMutation(match);
|
|
||||||
LivingEntity entity = (LivingEntity) spawn(location, type.getEntityClass());
|
|
||||||
EntityEquipment equipment = entity.getEquipment();
|
EntityEquipment equipment = entity.getEquipment();
|
||||||
entity.setVelocity(Vector.getRandom());
|
entity.setVelocity(Vector.getRandom());
|
||||||
entity.setAbsorption(5);
|
|
||||||
ItemStack held = null;
|
ItemStack held = null;
|
||||||
switch(type) {
|
switch(entity.getType()) {
|
||||||
case SKELETON:
|
case SKELETON:
|
||||||
case WITHER_SKELETON:
|
case WITHER_SKELETON:
|
||||||
case STRAY:
|
case STRAY:
|
||||||
|
@ -221,7 +216,7 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
case ZOMBIE_VILLAGER:
|
case ZOMBIE_VILLAGER:
|
||||||
case HUSK:
|
case HUSK:
|
||||||
Zombie zombie = (Zombie) entity;
|
Zombie zombie = (Zombie) entity;
|
||||||
zombie.setBaby(nextBoolean(random, SPECIAL_CHANCE));
|
zombie.setBaby(nextBoolean(random(), SPECIAL_CHANCE));
|
||||||
break;
|
break;
|
||||||
case PIG_ZOMBIE:
|
case PIG_ZOMBIE:
|
||||||
PigZombie pigZombie = (PigZombie) entity;
|
PigZombie pigZombie = (PigZombie) entity;
|
||||||
|
@ -231,8 +226,8 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
break;
|
break;
|
||||||
case CREEPER:
|
case CREEPER:
|
||||||
Creeper creeper = (Creeper) entity;
|
Creeper creeper = (Creeper) entity;
|
||||||
creeper.setPowered(nextBoolean(random, SPECIAL_CHANCE));
|
creeper.setPowered(nextBoolean(random(), SPECIAL_CHANCE));
|
||||||
world.strikeLightningEffect(location);
|
world().strikeLightningEffect(location);
|
||||||
break;
|
break;
|
||||||
case PRIMED_TNT:
|
case PRIMED_TNT:
|
||||||
TNTPrimed tnt = (TNTPrimed) entity;
|
TNTPrimed tnt = (TNTPrimed) entity;
|
||||||
|
@ -244,14 +239,13 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
slime.setSize(slime.getSize() * SPECIAL_MULTIPLIER);
|
slime.setSize(slime.getSize() * SPECIAL_MULTIPLIER);
|
||||||
break;
|
break;
|
||||||
case SKELETON_HORSE:
|
case SKELETON_HORSE:
|
||||||
world.strikeLightning(location);
|
world().strikeLightning(location);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(held != null && random.nextBoolean()) {
|
if(held != null && random().nextBoolean()) {
|
||||||
enchant.apply(held, equipment);
|
enchant.apply(held, equipment);
|
||||||
equipment.setItemInMainHand(held);
|
equipment.setItemInMainHand(held);
|
||||||
}
|
}
|
||||||
entities.put(entity, Instant.now());
|
|
||||||
return entity;
|
return entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,17 +254,12 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
* to make room for new entities.
|
* to make room for new entities.
|
||||||
* @param amount the amount of entities to despawn.
|
* @param amount the amount of entities to despawn.
|
||||||
*/
|
*/
|
||||||
public void despawn(int amount) {
|
public void despawn(long amount) {
|
||||||
entities.entrySet()
|
entitiesByTime().limit(Math.max(0, amount)).forEachOrdered(this::despawn);
|
||||||
.stream()
|
|
||||||
.sorted(Map.Entry.comparingByValue(Comparator.comparing(Instant::toEpochMilli)))
|
|
||||||
.limit(Math.max(0, amount))
|
|
||||||
.map(Map.Entry::getKey)
|
|
||||||
.forEach(Entity::remove);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(List<MatchPlayer> players) {
|
public void target(List<MatchPlayer> players) {
|
||||||
// At least one player is required to spawn mobs
|
// At least one player is required to spawn mobs
|
||||||
if(players.size() >= 1) {
|
if(players.size() >= 1) {
|
||||||
Location start, end;
|
Location start, end;
|
||||||
|
@ -284,7 +273,7 @@ public class ApocalypseMutation extends TargetMutation {
|
||||||
if(location.isPresent()) { // if the location is safe (on ground)
|
if(location.isPresent()) { // if the location is safe (on ground)
|
||||||
spawn(location.get(), true);
|
spawn(location.get(), true);
|
||||||
} else { // if the location was not safe, generate a simple midpoint location
|
} 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
|
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
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
super.enable();
|
super.enable();
|
||||||
time = world.getTime();
|
TargetMutation.super.enable();
|
||||||
|
time = world().getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Repeatable
|
@Repeatable
|
||||||
public void tick() {
|
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
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
world.setTime(time);
|
world().setTime(time);
|
||||||
despawn(entities.size());
|
despawn(entities().count());
|
||||||
super.disable();
|
super.disable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,63 +4,85 @@ import com.google.common.collect.Range;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.entity.TNTPrimed;
|
import org.bukkit.entity.TNTPrimed;
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import tc.oc.commons.core.collection.WeakHashSet;
|
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
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 tc.oc.pgm.mutation.types.TargetMutation;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
import java.util.List;
|
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 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> HEIGHT = Range.closed(30, 60);
|
||||||
final static Range<Integer> TICKS = Range.closed(10, 30);
|
final static Range<Integer> TICKS = Range.closed(10, 30);
|
||||||
|
|
||||||
final WeakHashSet<TNTPrimed> falling;
|
Instant next;
|
||||||
|
|
||||||
public BomberMutation(Match match) {
|
public BomberMutation(Match match) {
|
||||||
super(match, FREQUENCY);
|
super(match, false);
|
||||||
this.falling = new WeakHashSet<>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(List<MatchPlayer> players) {
|
public void target(List<MatchPlayer> players) {
|
||||||
players.forEach(player -> {
|
players.forEach(player -> {
|
||||||
int bombs = entropy.randomInt(TARGETS);
|
int bombs = entropy().randomInt(TARGETS);
|
||||||
int height = entropy.randomInt(HEIGHT);
|
int height = entropy().randomInt(HEIGHT);
|
||||||
Location location = player.getLocation().clone().add(0, height, 0);
|
Location location = player.getLocation().clone().add(0, height, 0);
|
||||||
for(int i = 0; i < bombs; i++) {
|
for(int i = 0; i < bombs; i++) {
|
||||||
TNTPrimed tnt = world.spawn(location, TNTPrimed.class);
|
TNTPrimed tnt = spawn(location, TNTPrimed.class);
|
||||||
tnt.setGlowing(true);
|
tnt.setGlowing(true);
|
||||||
tnt.setIsIncendiary(false);
|
tnt.setIsIncendiary(false);
|
||||||
tnt.setFuseTicks(Integer.MAX_VALUE);
|
tnt.setFuseTicks(200);
|
||||||
tnt.setVelocity(
|
tnt.setVelocity(
|
||||||
new Vector(
|
new Vector(
|
||||||
(random.nextBoolean() ? .5 : -.5) * entropy.randomDouble(),
|
(random().nextBoolean() ? .5 : -.5) * entropy().randomDouble(),
|
||||||
-entropy.randomDouble(),
|
-entropy().randomDouble(),
|
||||||
(random.nextBoolean() ? .5 : -.5) * entropy.randomDouble()
|
(random().nextBoolean() ? .5 : -.5) * entropy().randomDouble()
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
falling.add(tnt);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int targets() {
|
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() {
|
public void tick() {
|
||||||
falling.stream()
|
TargetMutation.super.tick();
|
||||||
.filter(TNTPrimed::isOnGround)
|
entities().filter(TNTPrimed::isOnGround).forEach(this::despawn);
|
||||||
.forEach(tnt -> tnt.setFuseTicks(entropy.randomInt(TICKS)));
|
|
||||||
falling.removeIf(TNTPrimed::isOnGround);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package tc.oc.pgm.mutation.types.targetable;
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.bukkit.util.Vector;
|
import org.bukkit.util.Vector;
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
@ -11,32 +10,31 @@ import tc.oc.pgm.mutation.types.TargetMutation;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class LightningMutation extends TargetMutation {
|
public class LightningMutation extends TargetMutation.Impl {
|
||||||
|
|
||||||
final static Duration FREQUENCY = Duration.ofSeconds(30);
|
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> STRIKES = Range.closed(0, 3);
|
final static Range<Integer> STRIKES = Range.closed(1, 3);
|
||||||
|
|
||||||
public LightningMutation(Match match) {
|
public LightningMutation(Match match) {
|
||||||
super(match, FREQUENCY);
|
super(match, FREQUENCY);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void execute(List<MatchPlayer> players) {
|
public void target(List<MatchPlayer> players) {
|
||||||
players.forEach(player -> {
|
players.forEach(player -> {
|
||||||
World world = match.getWorld();
|
|
||||||
Location location = player.getLocation();
|
Location location = player.getLocation();
|
||||||
world.strikeLightning(location.clone().add(Vector.getRandom()));
|
world().strikeLightning(location.clone().add(Vector.getRandom()));
|
||||||
int strikes = match.entropyForTick().randomInt(STRIKES);
|
int strikes = entropy().randomInt(STRIKES);
|
||||||
for(int i = 0; i < strikes; i++) {
|
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
|
@Override
|
||||||
public int targets() {
|
public int targets() {
|
||||||
return match.entropyForTick().randomInt(TARGETS);
|
return match().entropyForTick().randomInt(TARGETS);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue