Mutation overhaul
This commit is contained in:
parent
b883ef5799
commit
e83ec05e0d
|
@ -30,11 +30,7 @@ public interface MatchDoc extends Model {
|
||||||
Collection<String> winning_team_ids();
|
Collection<String> winning_team_ids();
|
||||||
Collection<String> winning_user_ids();
|
Collection<String> winning_user_ids();
|
||||||
|
|
||||||
enum Mutation {
|
Set<String> mutations();
|
||||||
BLITZ, UHC, EXPLOSIVES, NO_FALL, MOBS, STRENGTH, DOUBLE_JUMP, INVISIBILITY, LIGHTNING, RAGE, ELYTRA;
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<Mutation> mutations();
|
|
||||||
|
|
||||||
@Serialize
|
@Serialize
|
||||||
interface Team extends MapDoc.Team, CompetitorDoc {
|
interface Team extends MapDoc.Team, CompetitorDoc {
|
||||||
|
|
|
@ -138,7 +138,7 @@ public interface ServerDoc {
|
||||||
|
|
||||||
@Serialize
|
@Serialize
|
||||||
interface Mutation extends Partial {
|
interface Mutation extends Partial {
|
||||||
Set<MatchDoc.Mutation> queued_mutations();
|
Set<String> queued_mutations();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -33,7 +33,5 @@ public class TypeAdaptersManifest extends Manifest {
|
||||||
|
|
||||||
gson.bindAdapter(new TypeLiteral<Set<MapDoc.Gamemode>>(){})
|
gson.bindAdapter(new TypeLiteral<Set<MapDoc.Gamemode>>(){})
|
||||||
.to(new TypeLiteral<LenientEnumSetTypeAdapter<MapDoc.Gamemode>>(){});
|
.to(new TypeLiteral<LenientEnumSetTypeAdapter<MapDoc.Gamemode>>(){});
|
||||||
gson.bindAdapter(new TypeLiteral<Set<MatchDoc.Mutation>>(){})
|
|
||||||
.to(new TypeLiteral<LenientEnumSetTypeAdapter<MatchDoc.Mutation>>(){});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,7 +279,7 @@ public class LocalServerDocument extends StartupServerDocument implements Server
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<MatchDoc.Mutation> queued_mutations() {
|
public Set<String> queued_mutations() {
|
||||||
return mutations != null ? mutations.queued_mutations() : Collections.emptySet();
|
return mutations != null ? mutations.queued_mutations() : Collections.emptySet();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,9 +90,19 @@ command.mutation.enable.later.plural = You have enabled the {0} mutations for th
|
||||||
command.mutation.disable.later.singular = You have disabled the {0} mutation for the next match
|
command.mutation.disable.later.singular = You have disabled the {0} mutation for the next match
|
||||||
command.mutation.disable.later.plural = You have disabled the {0} mutations for the next match
|
command.mutation.disable.later.plural = You have disabled the {0} mutations for the next match
|
||||||
|
|
||||||
|
# {0} = command sender
|
||||||
|
# {1} = mutation name(s)
|
||||||
|
command.mutation.enable.now.singular = {0} has enabled the {1} mutation
|
||||||
|
command.mutation.enable.now.plural = {0} has enabled the {1} mutations
|
||||||
|
command.mutation.disable.now.singular = {0} has disabled the {1} mutation
|
||||||
|
command.mutation.disable.now.plural = {0} has disabled the {1} mutations
|
||||||
|
|
||||||
|
command.mutation.error.mutate = An internal error occured with the '{0}' mutation
|
||||||
command.mutation.error.find = Unable to find mutation named '{0}'
|
command.mutation.error.find = Unable to find mutation named '{0}'
|
||||||
command.mutation.error.enabled = All mutations have already been enabled
|
command.mutation.error.enabled = {0} mutation is already enabled
|
||||||
command.mutation.error.disabled = All mutations have already been disabled
|
command.mutation.error.enabled.all = All mutations have already been enabled
|
||||||
|
command.mutation.error.disabled = {0} mutation is already disabled
|
||||||
|
command.mutation.error.disabled.all = All mutations have already been disabled
|
||||||
|
|
||||||
command.mutation.list.current = Current Mutations
|
command.mutation.list.current = Current Mutations
|
||||||
command.mutation.list.queued = Queued Mutations
|
command.mutation.list.queued = Queued Mutations
|
||||||
|
@ -177,28 +187,41 @@ skillRequirement.fail.general = Play on unranked servers to improve your skill a
|
||||||
huddle.instructions = Your team now has {0} to strategize before the match starts
|
huddle.instructions = Your team now has {0} to strategize before the match starts
|
||||||
|
|
||||||
mutation.type.blitz = Blitz
|
mutation.type.blitz = Blitz
|
||||||
mutation.type.uhc = UHC
|
|
||||||
mutation.type.explosives = Explosives
|
|
||||||
mutation.type.no_fall = No Fall
|
|
||||||
mutation.type.mobs = Mobs
|
|
||||||
mutation.type.strength = Strength
|
|
||||||
mutation.type.double_jump = Double Jump
|
|
||||||
mutation.type.invisibility = Invisibility
|
|
||||||
mutation.type.lightning = Lightning
|
|
||||||
mutation.type.rage = Rage
|
|
||||||
mutation.type.elytra = Elytra
|
|
||||||
|
|
||||||
mutation.type.blitz.desc = no respawning
|
mutation.type.blitz.desc = no respawning
|
||||||
mutation.type.uhc.desc = no natural regeneration
|
mutation.type.rage = Rage
|
||||||
mutation.type.explosives.desc = stronger and more powerful explosions
|
|
||||||
mutation.type.no_fall.desc = fall all you want
|
|
||||||
mutation.type.mobs.desc = natural mob spawning
|
|
||||||
mutation.type.strength.desc = strength potions everywhere
|
|
||||||
mutation.type.double_jump.desc = super jump powers
|
|
||||||
mutation.type.invisibility.desc = enemy players cannot be seen
|
|
||||||
mutation.type.lightning.desc = lightning strikes from the sky
|
|
||||||
mutation.type.rage.desc = instant kills
|
mutation.type.rage.desc = instant kills
|
||||||
|
mutation.type.hardcore = Hardcore
|
||||||
|
mutation.type.hardcore.desc = no natural regeneration
|
||||||
|
mutation.type.jump = Jump
|
||||||
|
mutation.type.jump.desc = double jump and no fall
|
||||||
|
mutation.type.explosive = Explosive
|
||||||
|
mutation.type.explosive.desc = tnt and fire bows
|
||||||
|
mutation.type.elytra = Elytra
|
||||||
mutation.type.elytra.desc = fly around with an elytra
|
mutation.type.elytra.desc = fly around with an elytra
|
||||||
|
mutation.type.projectile = Projectile
|
||||||
|
mutation.type.projectile.desc = arrow potion effects
|
||||||
|
mutation.type.enchantment = Enchantment
|
||||||
|
mutation.type.enchantment.desc = random item enchantments
|
||||||
|
mutation.type.potion = Potion
|
||||||
|
mutation.type.potion.desc = random special potions
|
||||||
|
mutation.type.equestrian = Equestrian
|
||||||
|
mutation.type.equestrian.desc = ride a custom horse
|
||||||
|
mutation.type.health = Health
|
||||||
|
mutation.type.health.desc = double health and gold apples
|
||||||
|
mutation.type.glow = Glow
|
||||||
|
mutation.type.glow.desc = glowing effect on all players
|
||||||
|
mutation.type.stealth = Stealth
|
||||||
|
mutation.type.stealth.desc = invisibility with no armor
|
||||||
|
mutation.type.armor = Armor
|
||||||
|
mutation.type.armor.desc = heavy diamond armor
|
||||||
|
mutation.type.mobs = Mobs
|
||||||
|
mutation.type.mobs.desc = mob spawning eggs
|
||||||
|
mutation.type.lightning = Lightning
|
||||||
|
mutation.type.lightning.desc = lightning strikes from the sky
|
||||||
|
mutation.type.bomber = Bomber
|
||||||
|
mutation.type.bomber.desc = tnt rain
|
||||||
|
mutation.type.apocalypse = Apocalypse
|
||||||
|
mutation.type.apocalypse.desc = mob spawning chaos
|
||||||
|
|
||||||
tnt.license.info.alreadyHas = You have a TNT license. You can surrender your license by typing {0}
|
tnt.license.info.alreadyHas = You have a TNT license. You can surrender your license by typing {0}
|
||||||
tnt.license.info.doesNotHave = You do not have a TNT license. You can request one by typing {0}
|
tnt.license.info.doesNotHave = You do not have a TNT license. You can request one by typing {0}
|
||||||
|
|
|
@ -120,10 +120,10 @@ public class MatchDocument extends AbstractModel implements MatchDoc {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<Mutation> mutations() {
|
public Set<String> mutations() {
|
||||||
return mutations.map(mmm -> mmm.getHistoricalMutations()
|
return mutations.map(mmm -> mmm.mutationsHistorical()
|
||||||
.stream()
|
.stream()
|
||||||
.map(tc.oc.pgm.mutation.Mutation::toApi)
|
.map(tc.oc.pgm.mutation.Mutation::name)
|
||||||
.collect(Collectors.toImmutableSet()))
|
.collect(Collectors.toImmutableSet()))
|
||||||
.orElse(ImmutableSet.of());
|
.orElse(ImmutableSet.of());
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class BlitzMatchModule extends MatchModule implements Listener, JoinHandl
|
||||||
|
|
||||||
public BlitzMatchModule(Match match, BlitzConfig config) {
|
public BlitzMatchModule(Match match, BlitzConfig config) {
|
||||||
super(match);
|
super(match);
|
||||||
this.config = MutationMatchModule.check(match, Mutation.BLITZ) ? new BlitzConfig(1, true) : config;
|
this.config = match.module(MutationMatchModule.class).get().enabled(Mutation.BLITZ) ? new BlitzConfig(1, true) : config;
|
||||||
this.lifeManager = new LifeManager(this.config.getNumLives());
|
this.lifeManager = new LifeManager(this.config.getNumLives());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package tc.oc.pgm.damage;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableSetMultimap;
|
||||||
import com.google.common.collect.SetMultimap;
|
import com.google.common.collect.SetMultimap;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
@ -36,12 +37,12 @@ public class DisableDamageMatchModule extends MatchModule implements Listener {
|
||||||
this.causes = causes;
|
this.causes = causes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public SetMultimap<DamageCause, PlayerRelation> causes() {
|
||||||
public void load() {
|
return causes;
|
||||||
super.load();
|
}
|
||||||
if(MutationMatchModule.check(match, Mutation.NO_FALL)) {
|
|
||||||
this.causes.putAll(DamageCause.FALL, Sets.newHashSet(PlayerRelation.values()));
|
public ImmutableSetMultimap<DamageCause, PlayerRelation> causesImmutable() {
|
||||||
}
|
return ImmutableSetMultimap.copyOf(causes());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DamageCause getBlockDamageCause(Block block) {
|
private static DamageCause getBlockDamageCause(Block block) {
|
||||||
|
@ -59,11 +60,11 @@ public class DisableDamageMatchModule extends MatchModule implements Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canDamage(DamageCause cause, MatchPlayer victim, @Nullable ParticipantState damager) {
|
private boolean canDamage(DamageCause cause, MatchPlayer victim, @Nullable ParticipantState damager) {
|
||||||
return !this.causes.containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), damager));
|
return !causesImmutable().containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), damager));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canDamage(DamageCause cause, MatchPlayer victim, DamageInfo info) {
|
private boolean canDamage(DamageCause cause, MatchPlayer victim, DamageInfo info) {
|
||||||
return !this.causes.containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), info.getAttacker()));
|
return !causesImmutable().containsEntry(cause, PlayerRelation.get(victim.getParticipantState(), info.getAttacker()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.NORMAL, ignoreCancelled = true)
|
||||||
|
|
|
@ -21,10 +21,23 @@ public class DoubleJumpKit extends Kit.Impl {
|
||||||
this.rechargeInAir = rechargeInAir;
|
this.rechargeInAir = rechargeInAir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DoubleJumpKit() {
|
||||||
|
this(true, DEFAULT_POWER, DEFAULT_RECHARGE, true);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||||
DoubleJumpMatchModule djmm = player.getMatch().getMatchModule(DoubleJumpMatchModule.class);
|
player.getMatch().module(DoubleJumpMatchModule.class).ifPresent(jump -> jump.setKit(player.getBukkit(), this));
|
||||||
if(djmm != null) djmm.setKit(player.getBukkit(), this);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRemovable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
player.getMatch().module(DoubleJumpMatchModule.class).ifPresent(jump -> jump.setKit(player.getBukkit(), null));
|
||||||
}
|
}
|
||||||
|
|
||||||
public float chargePerTick() {
|
public float chargePerTick() {
|
||||||
|
|
|
@ -57,7 +57,7 @@ public class ProjectileTrailMatchModule extends MatchModule implements Listener
|
||||||
return Math.max(0.001, rgb / 255.0);
|
return Math.max(0.001, rgb / 255.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
||||||
public void onProjectileLaunch(ProjectileLaunchEvent event) {
|
public void onProjectileLaunch(ProjectileLaunchEvent event) {
|
||||||
match.player(event.getActor()).ifPresent(shooter -> {
|
match.player(event.getActor()).ifPresent(shooter -> {
|
||||||
final Projectile projectile = event.getEntity();
|
final Projectile projectile = event.getEntity();
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class MatchMutationFilter extends TypedFilter.Impl<IMatchQuery> {
|
||||||
@Override
|
@Override
|
||||||
public boolean matches(IMatchQuery query) {
|
public boolean matches(IMatchQuery query) {
|
||||||
return query.module(MutationMatchModule.class)
|
return query.module(MutationMatchModule.class)
|
||||||
.filter(mmm -> mmm.getActiveMutations().contains(mutation))
|
.filter(mmm -> mmm.enabled(mutation))
|
||||||
.isPresent();
|
.isPresent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
package tc.oc.pgm.gamerules;
|
|
||||||
|
|
||||||
public enum GameRule {
|
|
||||||
DO_FIRE_TICK("doFireTick"),
|
|
||||||
DO_MOB_LOOT("doMobLoot"),
|
|
||||||
DO_TILE_DROPS("doTileDrops"),
|
|
||||||
MOB_GRIEFING("mobGriefing"),
|
|
||||||
NATURAL_REGENERATION("naturalRegeneration"),
|
|
||||||
DO_DAYLIGHT_CYCLE("doDaylightCycle");
|
|
||||||
|
|
||||||
private String value;
|
|
||||||
|
|
||||||
GameRule(String value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getValue() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GameRule forName(String query) {
|
|
||||||
for (GameRule gamerule : values()) {
|
|
||||||
if (gamerule.getValue().equalsIgnoreCase(query)) {
|
|
||||||
return gamerule;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,29 +6,38 @@ import com.google.common.collect.ImmutableMap;
|
||||||
|
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.match.MatchModule;
|
import tc.oc.pgm.match.MatchModule;
|
||||||
import tc.oc.pgm.mutation.Mutation;
|
import tc.oc.pgm.match.Repeatable;
|
||||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
import tc.oc.time.Time;
|
||||||
|
|
||||||
public class GameRulesMatchModule extends MatchModule {
|
public class GameRulesMatchModule extends MatchModule {
|
||||||
|
|
||||||
private final Map<GameRule, Boolean> gameRules;
|
private final Map<String, String> gameRules;
|
||||||
|
|
||||||
public GameRulesMatchModule(Match match, Map<GameRule, Boolean> gameRules) {
|
public GameRulesMatchModule(Match match, Map<String, String> gameRules) {
|
||||||
super(match);
|
super(match);
|
||||||
this.gameRules = Preconditions.checkNotNull(gameRules, "gamerules");
|
this.gameRules = Preconditions.checkNotNull(gameRules, "gamerules");
|
||||||
if(MutationMatchModule.check(match, Mutation.UHC)) {
|
|
||||||
this.gameRules.put(GameRule.NATURAL_REGENERATION, Boolean.FALSE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
for (Map.Entry<GameRule, Boolean> gameRule : this.gameRules.entrySet()) {
|
update();
|
||||||
this.match.getWorld().setGameRuleValue(gameRule.getKey().getValue(), gameRule.getValue().toString());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableMap<GameRule, Boolean> getGameRules() {
|
@Repeatable(interval = @Time(seconds = 1))
|
||||||
return ImmutableMap.copyOf(gameRules);
|
public void tick() {
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update() {
|
||||||
|
gameRulesImmutable().forEach((String rule, String val) -> match.getWorld().setGameRuleValue(rule, val));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<String, String> gameRules() {
|
||||||
|
return gameRules;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableMap<String, String> gameRulesImmutable() {
|
||||||
|
return ImmutableMap.copyOf(gameRules());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,9 +18,9 @@ import tc.oc.pgm.xml.InvalidXMLException;
|
||||||
@ModuleDescription(name="Gamerules", follows = MutationMapModule.class)
|
@ModuleDescription(name="Gamerules", follows = MutationMapModule.class)
|
||||||
public class GameRulesModule implements MapModule, MatchModuleFactory<GameRulesMatchModule> {
|
public class GameRulesModule implements MapModule, MatchModuleFactory<GameRulesMatchModule> {
|
||||||
|
|
||||||
private Map<GameRule, Boolean> gameRules;
|
private Map<String, String> gameRules;
|
||||||
|
|
||||||
private GameRulesModule(Map<GameRule, Boolean> gamerules) {
|
private GameRulesModule(Map<String, String> gamerules) {
|
||||||
this.gameRules = gamerules;
|
this.gameRules = gamerules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,32 +33,32 @@ public class GameRulesModule implements MapModule, MatchModuleFactory<GameRulesM
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
public static GameRulesModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException {
|
public static GameRulesModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException {
|
||||||
Map<GameRule, Boolean> gameRules = new HashMap<>();
|
Map<String, String> gameRules = new HashMap<>();
|
||||||
|
|
||||||
for (Element gameRulesElement : doc.getRootElement().getChildren("gamerules")) {
|
for (Element gameRulesElement : doc.getRootElement().getChildren("gamerules")) {
|
||||||
for (Element gameRuleElement : gameRulesElement.getChildren()) {
|
for (Element gameRuleElement : gameRulesElement.getChildren()) {
|
||||||
GameRule gameRule = GameRule.forName(gameRuleElement.getName());
|
String gameRule = gameRuleElement.getName();
|
||||||
String value = gameRuleElement.getValue();
|
String value = gameRuleElement.getValue();
|
||||||
|
|
||||||
if (gameRule == null) {
|
if(gameRule == null) {
|
||||||
throw new InvalidXMLException(gameRuleElement.getName() + " is not a valid gamerule", gameRuleElement);
|
throw new InvalidXMLException(gameRuleElement.getName() + " is not a valid gamerule", gameRuleElement);
|
||||||
}
|
}
|
||||||
if (value == null) {
|
if(value == null) {
|
||||||
throw new InvalidXMLException("Missing value for gamerule " + gameRule.getValue(), gameRuleElement);
|
throw new InvalidXMLException("Missing value for gamerule " + gameRule, gameRuleElement);
|
||||||
} else if (!(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))) {
|
} else if (!(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))) {
|
||||||
throw new InvalidXMLException(gameRuleElement.getValue() + " is not a valid gamerule value", gameRuleElement);
|
throw new InvalidXMLException(gameRuleElement.getValue() + " is not a valid gamerule value", gameRuleElement);
|
||||||
}
|
}
|
||||||
if (gameRules.containsKey(gameRule)){
|
if(gameRules.containsKey(gameRule)){
|
||||||
throw new InvalidXMLException(gameRule.getValue() + " has already been specified", gameRuleElement);
|
throw new InvalidXMLException(gameRule + " has already been specified", gameRuleElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
gameRules.put(gameRule, Boolean.valueOf(value));
|
gameRules.put(gameRule, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return new GameRulesModule(gameRules);
|
return new GameRulesModule(gameRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ImmutableMap<GameRule, Boolean> getGameRules() {
|
public ImmutableMap<String, String> getGameRules() {
|
||||||
return ImmutableMap.copyOf(this.gameRules);
|
return ImmutableMap.copyOf(this.gameRules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@ package tc.oc.pgm.killreward;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
import tc.oc.pgm.filters.Filter;
|
import tc.oc.pgm.filters.Filter;
|
||||||
|
import tc.oc.pgm.filters.matcher.StaticFilter;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
import tc.oc.pgm.kits.Kit;
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
|
||||||
public class KillReward {
|
public class KillReward {
|
||||||
|
@ -15,4 +17,8 @@ public class KillReward {
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.kit = kit;
|
this.kit = kit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KillReward(ItemKit kit) {
|
||||||
|
this(ImmutableList.of(kit.item()), StaticFilter.ALLOW, kit);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,10 @@ package tc.oc.pgm.killreward;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Collections2;
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import org.bukkit.event.Event;
|
import org.bukkit.event.Event;
|
||||||
|
@ -27,34 +26,38 @@ import tc.oc.pgm.tracker.damage.DamageInfo;
|
||||||
|
|
||||||
@ListenerScope(MatchScope.RUNNING)
|
@ListenerScope(MatchScope.RUNNING)
|
||||||
public class KillRewardMatchModule extends MatchModule implements Listener {
|
public class KillRewardMatchModule extends MatchModule implements Listener {
|
||||||
protected final ImmutableList<KillReward> killRewards;
|
|
||||||
protected final Multimap<MatchPlayer, KillReward> deadPlayerRewards = ArrayListMultimap.create();
|
private final List<KillReward> killRewards;
|
||||||
|
private final Multimap<MatchPlayer, KillReward> deadPlayerRewards = ArrayListMultimap.create();
|
||||||
|
|
||||||
public KillRewardMatchModule(Match match, List<KillReward> killRewards) {
|
public KillRewardMatchModule(Match match, List<KillReward> killRewards) {
|
||||||
super(match);
|
super(match);
|
||||||
this.killRewards = ImmutableList.copyOf(killRewards);
|
this.killRewards = killRewards;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<KillReward> getRewards(@Nullable Event event, ParticipantState victim, DamageInfo damageInfo) {
|
public List<KillReward> rewards() {
|
||||||
|
return killRewards;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ImmutableList<KillReward> rewardsImmutable() {
|
||||||
|
return ImmutableList.copyOf(killRewards);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<KillReward> rewards(@Nullable Event event, ParticipantState victim, DamageInfo damageInfo) {
|
||||||
final DamageQuery query = DamageQuery.attackerDefault(event, victim, damageInfo);
|
final DamageQuery query = DamageQuery.attackerDefault(event, victim, damageInfo);
|
||||||
return Collections2.filter(killRewards, new Predicate<KillReward>() {
|
return rewardsImmutable().stream().filter(reward -> reward.filter.query(query).isAllowed()).collect(Collectors.toList());
|
||||||
@Override
|
|
||||||
public boolean apply(KillReward killReward) {
|
|
||||||
return killReward.filter.query(query).isAllowed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Collection<KillReward> getRewards(MatchPlayerDeathEvent event) {
|
public List<KillReward> rewards(MatchPlayerDeathEvent event) {
|
||||||
return getRewards(event, event.getVictim().getParticipantState(), event.getDamageInfo());
|
return rewards(event, event.getVictim().getParticipantState(), event.getDamageInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void giveRewards(MatchPlayer killer, Collection<KillReward> rewards) {
|
public void giveRewards(MatchPlayer killer, Collection<KillReward> rewards) {
|
||||||
for(KillReward reward : rewards) {
|
rewards.forEach(reward -> {
|
||||||
// Apply kit first so it can not override reward items
|
// Apply kit first so it can not override reward items
|
||||||
reward.kit.apply(killer);
|
reward.kit.apply(killer);
|
||||||
reward.items.forEach(stack -> ItemKitApplicator.fireEventAndTransfer(killer, stack));
|
reward.items.forEach(stack -> ItemKitApplicator.fireEventAndTransfer(killer, stack));
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||||
|
@ -63,7 +66,7 @@ public class KillRewardMatchModule extends MatchModule implements Listener {
|
||||||
MatchPlayer killer = event.getOnlineKiller();
|
MatchPlayer killer = event.getOnlineKiller();
|
||||||
if(killer == null) return;
|
if(killer == null) return;
|
||||||
|
|
||||||
Collection<KillReward> rewards = getRewards(event);
|
List<KillReward> rewards = rewards(event);
|
||||||
|
|
||||||
if(killer.isDead()) {
|
if(killer.isDead()) {
|
||||||
// If a player earns a KW while dead, give it to them when they respawn. Rationale: If they click respawn
|
// If a player earns a KW while dead, give it to them when they respawn. Rationale: If they click respawn
|
||||||
|
@ -87,4 +90,5 @@ public class KillRewardMatchModule extends MatchModule implements Listener {
|
||||||
public void onPartyChange(PlayerPartyChangeEvent event) {
|
public void onPartyChange(PlayerPartyChangeEvent event) {
|
||||||
deadPlayerRewards.removeAll(event.getPlayer());
|
deadPlayerRewards.removeAll(event.getPlayer());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package tc.oc.pgm.killreward;
|
package tc.oc.pgm.killreward;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
@ -27,10 +28,10 @@ import tc.oc.pgm.xml.InvalidXMLException;
|
||||||
|
|
||||||
@ModuleDescription(name="Kill Reward")
|
@ModuleDescription(name="Kill Reward")
|
||||||
public class KillRewardModule implements MapModule, MatchModuleFactory<KillRewardMatchModule> {
|
public class KillRewardModule implements MapModule, MatchModuleFactory<KillRewardMatchModule> {
|
||||||
protected final ImmutableList<KillReward> rewards;
|
protected final List<KillReward> rewards;
|
||||||
|
|
||||||
public KillRewardModule(List<KillReward> rewards) {
|
public KillRewardModule(List<KillReward> rewards) {
|
||||||
this.rewards = ImmutableList.copyOf(rewards);
|
this.rewards = rewards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -43,7 +44,7 @@ public class KillRewardModule implements MapModule, MatchModuleFactory<KillRewar
|
||||||
// ---------------------
|
// ---------------------
|
||||||
|
|
||||||
public static KillRewardModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException {
|
public static KillRewardModule parse(MapModuleContext context, Logger logger, Document doc) throws InvalidXMLException {
|
||||||
ImmutableList.Builder<KillReward> rewards = ImmutableList.builder();
|
List<KillReward> rewards = new ArrayList<>();
|
||||||
final ItemParser itemParser = context.needModule(ItemParser.class);
|
final ItemParser itemParser = context.needModule(ItemParser.class);
|
||||||
final Optional<ItemModifyModule> itemModifier = context.module(ItemModifyModule.class);
|
final Optional<ItemModifyModule> itemModifier = context.module(ItemModifyModule.class);
|
||||||
|
|
||||||
|
@ -61,11 +62,6 @@ public class KillRewardModule implements MapModule, MatchModuleFactory<KillRewar
|
||||||
rewards.add(new KillReward(items.build(), filter, kit));
|
rewards.add(new KillReward(items.build(), filter, kit));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImmutableList<KillReward> list = rewards.build();
|
return new KillRewardModule(rewards);
|
||||||
if(list.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
return new KillRewardModule(list);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,15 @@
|
||||||
package tc.oc.pgm.kits;
|
package tc.oc.pgm.kits;
|
||||||
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
import tc.oc.commons.bukkit.inventory.Slot;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class FreeItemKit extends BaseItemKit {
|
public class FreeItemKit extends BaseItemKit {
|
||||||
|
|
||||||
protected final ItemStack item;
|
protected final ItemStack item;
|
||||||
|
@ -20,4 +27,36 @@ public class FreeItemKit extends BaseItemKit {
|
||||||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||||
items.add(item);
|
items.add(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRemovable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
int left = item.getAmount();
|
||||||
|
PlayerInventory inv = player.getInventory();
|
||||||
|
for(Map.Entry<Slot.Player, Optional<ItemStack>> entry : Slot.Player.player()
|
||||||
|
.collect(Collectors.toMap(Function.identity(), slot -> slot.item(inv))).entrySet()) {
|
||||||
|
Slot.Player slot = entry.getKey();
|
||||||
|
Optional<ItemStack> itemMaybe = entry.getValue();
|
||||||
|
if(itemMaybe.isPresent() && this.item.isSimilar(itemMaybe.get())) {
|
||||||
|
ItemStack item = itemMaybe.get();
|
||||||
|
int delta = item.getAmount() - left;
|
||||||
|
if(delta > 0) {
|
||||||
|
ItemStack replaced = item.clone();
|
||||||
|
replaced.setAmount(delta);
|
||||||
|
slot.putItem(inv, replaced);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
slot.putItem(inv, null);
|
||||||
|
if(delta < 0) {
|
||||||
|
left = -delta;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,11 @@
|
||||||
package tc.oc.pgm.kits;
|
package tc.oc.pgm.kits;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
|
||||||
public class HealthKit extends Kit.Impl {
|
public class HealthKit extends Kit.Impl {
|
||||||
protected final int halfHearts;
|
protected final int halfHearts;
|
||||||
|
|
||||||
public HealthKit(int halfHearts) {
|
public HealthKit(int halfHearts) {
|
||||||
Preconditions.checkArgument(0 < halfHearts && halfHearts <= 20, "halfHearts must be greater than 0 and less than or equal to 20");
|
|
||||||
this.halfHearts = halfHearts;
|
this.halfHearts = halfHearts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,12 +11,27 @@ public class NaturalRegenerationKit extends Kit.Impl {
|
||||||
this.enabled = enabled;
|
this.enabled = enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void toggle(MatchPlayer player, boolean enabled) {
|
||||||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
|
||||||
if(fast) {
|
if(fast) {
|
||||||
player.getBukkit().setFastNaturalRegeneration(enabled);
|
player.getBukkit().setFastNaturalRegeneration(enabled);
|
||||||
} else {
|
} else {
|
||||||
player.getBukkit().setSlowNaturalRegeneration(enabled);
|
player.getBukkit().setSlowNaturalRegeneration(enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||||
|
toggle(player, enabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRemovable() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
toggle(player, !enabled);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,5 @@ public class SlotItemKit extends FreeItemKit {
|
||||||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||||
items.put(slot, item, force);
|
items.put(slot, item, force);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,11 +164,11 @@ public class MatchAnnouncer implements PluginFacet, Listener {
|
||||||
}
|
}
|
||||||
|
|
||||||
final MutationMatchModule mmm = viewer.getMatch().getMatchModule(MutationMatchModule.class);
|
final MutationMatchModule mmm = viewer.getMatch().getMatchModule(MutationMatchModule.class);
|
||||||
if(mmm != null && mmm.getActiveMutations().size() > 0) {
|
if(mmm != null && mmm.mutationsActive().size() > 0) {
|
||||||
viewer.sendMessage(
|
viewer.sendMessage(
|
||||||
new Component(" ", ChatColor.DARK_GRAY).extra(
|
new Component(" ", ChatColor.DARK_GRAY).extra(
|
||||||
new TranslatableComponent("broadcast.welcomeMessage.mutations",
|
new TranslatableComponent("broadcast.welcomeMessage.mutations",
|
||||||
new ListComponent(Collections2.transform(mmm.getActiveMutations(), Mutation.toComponent(ChatColor.GREEN)))
|
new ListComponent(Collections2.transform(mmm.mutationsActive(), Mutation.toComponent(ChatColor.GREEN)))
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -25,7 +25,6 @@ import tc.oc.pgm.Config;
|
||||||
import tc.oc.pgm.events.MatchBeginEvent;
|
import tc.oc.pgm.events.MatchBeginEvent;
|
||||||
import tc.oc.pgm.events.MatchEndEvent;
|
import tc.oc.pgm.events.MatchEndEvent;
|
||||||
import tc.oc.pgm.events.MatchLoadEvent;
|
import tc.oc.pgm.events.MatchLoadEvent;
|
||||||
import tc.oc.pgm.gamerules.GameRule;
|
|
||||||
import tc.oc.pgm.gamerules.GameRulesModule;
|
import tc.oc.pgm.gamerules.GameRulesModule;
|
||||||
import tc.oc.pgm.match.MatchManager;
|
import tc.oc.pgm.match.MatchManager;
|
||||||
import tc.oc.pgm.modules.TimeLockModule;
|
import tc.oc.pgm.modules.TimeLockModule;
|
||||||
|
@ -99,9 +98,11 @@ public class PGMListener implements PluginFacet, Listener {
|
||||||
// Time Lock
|
// Time Lock
|
||||||
// lock time before, during (if time lock enabled), and after the match
|
// lock time before, during (if time lock enabled), and after the match
|
||||||
//
|
//
|
||||||
|
static final String DO_DAYLIGHT_CYCLE = "doDaylightCycle";
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void lockTime(final MatchLoadEvent event) {
|
public void lockTime(final MatchLoadEvent event) {
|
||||||
event.getMatch().getWorld().setGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE.getValue(), Boolean.toString(false));
|
event.getMatch().getWorld().setGameRuleValue(DO_DAYLIGHT_CYCLE, Boolean.toString(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
@ -113,16 +114,16 @@ public class PGMListener implements PluginFacet, Listener {
|
||||||
|
|
||||||
GameRulesModule gameRulesModule = event.getMatch().getModuleContext().getModule(GameRulesModule.class);
|
GameRulesModule gameRulesModule = event.getMatch().getModuleContext().getModule(GameRulesModule.class);
|
||||||
|
|
||||||
if (gameRulesModule != null && gameRulesModule.getGameRules().containsKey(GameRule.DO_DAYLIGHT_CYCLE)) {
|
if (gameRulesModule != null && gameRulesModule.getGameRules().containsKey(DO_DAYLIGHT_CYCLE)) {
|
||||||
unlockTime = gameRulesModule.getGameRules().get(GameRule.DO_DAYLIGHT_CYCLE);
|
unlockTime = Boolean.valueOf(gameRulesModule.getGameRules().get(DO_DAYLIGHT_CYCLE));
|
||||||
}
|
}
|
||||||
|
|
||||||
event.getMatch().getWorld().setGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE.getValue(), Boolean.toString(unlockTime));
|
event.getMatch().getWorld().setGameRuleValue(DO_DAYLIGHT_CYCLE, Boolean.toString(unlockTime));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void lockTime(final MatchEndEvent event) {
|
public void lockTime(final MatchEndEvent event) {
|
||||||
event.getMatch().getWorld().setGameRuleValue(GameRule.DO_DAYLIGHT_CYCLE.getValue(), Boolean.toString(false));
|
event.getMatch().getWorld().setGameRuleValue(DO_DAYLIGHT_CYCLE, Boolean.toString(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
|
|
|
@ -4,22 +4,21 @@ import org.bukkit.event.EventHandler;
|
||||||
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;
|
||||||
import tc.oc.pgm.filters.matcher.StaticFilter;
|
|
||||||
import tc.oc.pgm.match.Match;
|
import tc.oc.pgm.match.Match;
|
||||||
import tc.oc.pgm.filters.Filter;
|
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.Mutation;
|
|
||||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
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 {
|
||||||
|
|
||||||
private final Filter mobsFilter;
|
private final Filter mobsFilter;
|
||||||
|
|
||||||
public MobsMatchModule(Match match, Filter mobsFilter) {
|
public MobsMatchModule(Match match, Filter mobsFilter) {
|
||||||
super(match);
|
super(match);
|
||||||
this.mobsFilter = MutationMatchModule.check(match, Mutation.MOBS) ? StaticFilter.ALLOW : mobsFilter;
|
this.mobsFilter = mobsFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -40,8 +39,10 @@ public class MobsMatchModule extends MatchModule implements Listener {
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true)
|
@EventHandler(ignoreCancelled = true)
|
||||||
public void checkSpawn(final CreatureSpawnEvent event) {
|
public void checkSpawn(final CreatureSpawnEvent event) {
|
||||||
if(event.getSpawnReason() != CreatureSpawnEvent.SpawnReason.CUSTOM) {
|
if(!match.module(MutationMatchModule.class).map(mmm -> mmm.allowMob(event.getSpawnReason())).orElse(false) &&
|
||||||
|
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());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,58 +7,61 @@ import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
import net.md_5.bungee.api.chat.HoverEvent;
|
import net.md_5.bungee.api.chat.HoverEvent;
|
||||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||||
import tc.oc.api.docs.virtual.MatchDoc;
|
|
||||||
import tc.oc.commons.core.chat.Component;
|
import tc.oc.commons.core.chat.Component;
|
||||||
import tc.oc.pgm.mutation.submodule.MutationModule;
|
import tc.oc.pgm.PGM;
|
||||||
|
import tc.oc.pgm.mutation.types.MutationModule;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.ArmorMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.ElytraMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.EnchantmentMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.EquestrianMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.ExplosiveMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.GlowMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.HardcoreMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.HealthMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.JumpMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.MobsMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.PotionMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.ProjectileMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.StealthMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.other.RageMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.targetable.ApocalypseMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.targetable.BomberMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.targetable.LightningMutation;
|
||||||
|
|
||||||
import static tc.oc.pgm.mutation.submodule.MutationModules.*;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
public enum Mutation {
|
public enum Mutation {
|
||||||
|
|
||||||
BLITZ (null, false),
|
BLITZ (null),
|
||||||
UHC (null, false),
|
RAGE (RageMutation.class),
|
||||||
EXPLOSIVES (Explosives.class, true),
|
HARDCORE (HardcoreMutation.class),
|
||||||
NO_FALL (null, false),
|
JUMP (JumpMutation.class),
|
||||||
MOBS (null, false),
|
EXPLOSIVE (ExplosiveMutation.class),
|
||||||
STRENGTH (Strength.class, true),
|
ELYTRA (ElytraMutation.class),
|
||||||
DOUBLE_JUMP (DoubleJump.class, true),
|
PROJECTILE (ProjectileMutation.class),
|
||||||
INVISIBILITY(Invisibility.class, true),
|
ENCHANTMENT(EnchantmentMutation.class),
|
||||||
LIGHTNING (Lightning.class, true),
|
POTION (PotionMutation.class),
|
||||||
RAGE (Rage.class, true),
|
EQUESTRIAN (EquestrianMutation.class),
|
||||||
ELYTRA (Elytra.class, true);
|
HEALTH (HealthMutation.class),
|
||||||
|
GLOW (GlowMutation.class),
|
||||||
|
STEALTH (StealthMutation.class),
|
||||||
|
ARMOR (ArmorMutation.class),
|
||||||
|
MOBS (MobsMutation.class),
|
||||||
|
LIGHTNING (LightningMutation.class),
|
||||||
|
BOMBER (BomberMutation.class),
|
||||||
|
APOCALYPSE (ApocalypseMutation.class);
|
||||||
|
|
||||||
public static final String TYPE_KEY = "mutation.type.";
|
public static final String TYPE_KEY = "mutation.type.";
|
||||||
public static final String DESCRIPTION_KEY = ".desc";
|
public static final String DESCRIPTION_KEY = ".desc";
|
||||||
|
|
||||||
/**
|
private final @Nullable Class<? extends MutationModule> loader;
|
||||||
* The module class that handles this mutation.
|
|
||||||
*/
|
|
||||||
private final @Nullable Class<? extends MutationModule> clazz;
|
|
||||||
|
|
||||||
/**
|
Mutation(@Nullable Class<? extends MutationModule> loader) {
|
||||||
* Whether this mutation be changed during a match.
|
this.loader = loader;
|
||||||
*/
|
|
||||||
private final boolean change;
|
|
||||||
|
|
||||||
Mutation(@Nullable Class<? extends MutationModule> clazz, boolean change) {
|
|
||||||
this.clazz = clazz;
|
|
||||||
this.change = change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Mutation fromApi(MatchDoc.Mutation mutation) {
|
public Class<? extends MutationModule> loader() {
|
||||||
return values()[mutation.ordinal()];
|
return loader;
|
||||||
}
|
|
||||||
|
|
||||||
public MatchDoc.Mutation toApi() {
|
|
||||||
return MatchDoc.Mutation.values()[ordinal()];
|
|
||||||
}
|
|
||||||
|
|
||||||
public Class<? extends MutationModule> getModuleClass() {
|
|
||||||
return clazz;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isChangeable() {
|
|
||||||
return change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -76,4 +79,14 @@ public enum Mutation {
|
||||||
public static Function<Mutation, BaseComponent> toComponent(final ChatColor color) {
|
public static Function<Mutation, BaseComponent> toComponent(final ChatColor color) {
|
||||||
return mutation -> mutation.getComponent(color);
|
return mutation -> mutation.getComponent(color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Stream<Mutation> fromString(final String name) {
|
||||||
|
try {
|
||||||
|
return Stream.of(Mutation.valueOf(name));
|
||||||
|
} catch(IllegalArgumentException iae) {
|
||||||
|
PGM.get().getLogger().warning("Unable to find mutation named '" + name + "'");
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
package tc.oc.pgm.mutation;
|
package tc.oc.pgm.mutation;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.collect.Collections2;
|
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;
|
||||||
import tc.oc.pgm.match.*;
|
import tc.oc.pgm.match.*;
|
||||||
import tc.oc.pgm.mutation.command.MutationCommands;
|
import tc.oc.pgm.mutation.command.MutationCommands;
|
||||||
import tc.oc.pgm.mutation.submodule.MutationModule;
|
import tc.oc.pgm.mutation.types.MutationModule;
|
||||||
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
public class MutationMatchModule extends MatchModule {
|
public class MutationMatchModule extends MatchModule {
|
||||||
// TODO: send remote mutation alerts via an AMQP message
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Chance that a mutation event will occur.
|
* Chance that a mutation event will occur.
|
||||||
|
@ -63,39 +63,36 @@ public class MutationMatchModule extends MatchModule {
|
||||||
this.chance = options.chance;
|
this.chance = options.chance;
|
||||||
this.amount = options.amount;
|
this.amount = options.amount;
|
||||||
this.weightedSelector = new ImmutableWeightedRandomChooser<>(options.weights);
|
this.weightedSelector = new ImmutableWeightedRandomChooser<>(options.weights);
|
||||||
this.mutations = getDefaultMutations();
|
this.mutations = mutationsDefault();
|
||||||
this.history = new HashSet<>();
|
this.history = new HashSet<>();
|
||||||
this.modules = new HashMap<>();
|
this.modules = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ImmutableMap<Mutation, Boolean> getMutations() {
|
public final ImmutableMap<Mutation, Boolean> mutations() {
|
||||||
return ImmutableMap.copyOf(mutations);
|
return ImmutableMap.copyOf(mutations);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ImmutableSet<Mutation> getActiveMutations() {
|
public final ImmutableSet<Mutation> mutationsActive() {
|
||||||
return ImmutableSet.copyOf(Collections2.filter(getMutations().keySet(), new Predicate<Mutation>() {
|
return ImmutableSet.copyOf(Collections2.filter(mutations().keySet(), mutations::get));
|
||||||
@Override
|
|
||||||
public boolean apply(Mutation mutation) {
|
|
||||||
return mutations.get(mutation);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ImmutableSet<Mutation> getHistoricalMutations() {
|
public final ImmutableSet<Mutation> mutationsHistorical() {
|
||||||
return ImmutableSet.copyOf(history);
|
return ImmutableSet.copyOf(history);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final ImmutableSet<MutationModule> getMutationModules() {
|
private Map<Mutation, Boolean> mutationsDefault() {
|
||||||
|
Map<Mutation, Boolean> defaults = new HashMap<>();
|
||||||
|
MapUtils.putAll(defaults, Sets.newHashSet(Mutation.values()), false);
|
||||||
|
return defaults;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final ImmutableSet<MutationModule> mutationModules() {
|
||||||
return ImmutableSet.copyOf(modules.values());
|
return ImmutableSet.copyOf(modules.values());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldLoad() {
|
|
||||||
return Config.Mutations.enabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void load() {
|
public void load() {
|
||||||
|
if(!Config.Mutations.enabled()) return;
|
||||||
Random random = match.getRandom();
|
Random random = match.getRandom();
|
||||||
// Check if the api has any queued mutations
|
// Check if the api has any queued mutations
|
||||||
Collection<Mutation> queuedMutations = mutationQueue.mutations();
|
Collection<Mutation> queuedMutations = mutationQueue.mutations();
|
||||||
|
@ -113,7 +110,7 @@ public class MutationMatchModule extends MatchModule {
|
||||||
mutationQueue.clear();
|
mutationQueue.clear();
|
||||||
}
|
}
|
||||||
// Load the mutation modules for this match
|
// Load the mutation modules for this match
|
||||||
for(Mutation mutation : getActiveMutations()) {
|
for(Mutation mutation : mutationsActive()) {
|
||||||
try {
|
try {
|
||||||
mutate(mutation);
|
mutate(mutation);
|
||||||
} catch (Throwable throwable) {
|
} catch (Throwable throwable) {
|
||||||
|
@ -124,43 +121,53 @@ public class MutationMatchModule extends MatchModule {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enable() {
|
public void enable() {
|
||||||
for(MutationModule module : modules.values()) {
|
modules.values().forEach(MutationModule::enable);
|
||||||
module.enable(match.isRunning());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disable() {
|
public void disable() {
|
||||||
for(MutationModule module : modules.values()) {
|
modules.values().forEach(MutationModule::disable);
|
||||||
module.disable(match.isRunning());
|
}
|
||||||
}
|
|
||||||
|
public void register(Mutation mutation, boolean load) {
|
||||||
|
mutations.put(mutation, load);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void mutate(Mutation mutation) throws Throwable {
|
public void mutate(Mutation mutation) throws Throwable {
|
||||||
Class<? extends MutationModule> clazz = mutation.getModuleClass();
|
Class<? extends MutationModule> loader = mutation.loader();
|
||||||
if(clazz == null || (match.isRunning() && !mutation.isChangeable())) return;
|
if(loader == null) return;
|
||||||
MutationModule module = modules.containsKey(clazz) ? modules.get(clazz) : mutation.getModuleClass().getDeclaredConstructor(Match.class).newInstance(match);
|
MutationModule module = modules.containsKey(loader) ? modules.get(loader) : loader.getDeclaredConstructor(Match.class).newInstance(match);
|
||||||
if(mutations.get(mutation)) {
|
if(mutations.get(mutation)) {
|
||||||
module.enable(match.isRunning());
|
module.enable();
|
||||||
modules.put(clazz, module);
|
modules.put(loader, module);
|
||||||
mutations.put(mutation, true);
|
mutations.put(mutation, true);
|
||||||
history.add(mutation);
|
history.add(mutation);
|
||||||
} else {
|
} else {
|
||||||
module.disable(match.isRunning());
|
module.disable();
|
||||||
modules.remove(clazz);
|
modules.remove(loader);
|
||||||
mutations.put(mutation, false);
|
mutations.put(mutation, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Mutation, Boolean> getDefaultMutations() {
|
public boolean enabled() {
|
||||||
Map<Mutation, Boolean> defaults = new HashMap<>();
|
return !mutationsActive().isEmpty();
|
||||||
MapUtils.putAll(defaults, Sets.newHashSet(Mutation.values()), false);
|
|
||||||
return defaults;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean check(Match match, Mutation mutation) {
|
public boolean enabled(Mutation... mutations) {
|
||||||
return Config.Mutations.enabled() &&
|
return mutationsActive().stream().anyMatch(m1 -> Stream.of(mutations).anyMatch(m2 -> m2.equals(m1)));
|
||||||
match.hasMatchModule(MutationMatchModule.class) &&
|
|
||||||
match.getMatchModule(MutationMatchModule.class).getActiveMutations().contains(mutation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean allowMob(CreatureSpawnEvent.SpawnReason reason) {
|
||||||
|
switch(reason) {
|
||||||
|
case NATURAL:
|
||||||
|
case DEFAULT:
|
||||||
|
case CHUNK_GEN:
|
||||||
|
case JOCKEY:
|
||||||
|
case MOUNT:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@ import java.util.HashSet;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import com.google.common.collect.Collections2;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import tc.oc.api.docs.Server;
|
import tc.oc.api.docs.Server;
|
||||||
import tc.oc.api.docs.virtual.ServerDoc;
|
import tc.oc.api.docs.virtual.ServerDoc;
|
||||||
|
@ -28,12 +27,12 @@ public class MutationQueue {
|
||||||
.getLocalServer()
|
.getLocalServer()
|
||||||
.queued_mutations()
|
.queued_mutations()
|
||||||
.stream()
|
.stream()
|
||||||
.map(mutation -> Mutation.values()[mutation.ordinal()])
|
.flatMap(Mutation::fromString)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Server> clear() {
|
public ListenableFuture<Server> clear() {
|
||||||
return force(Collections.<Mutation>emptyList());
|
return force(Collections.emptyList());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListenableFuture<Server> removeAll(final Collection<Mutation> mutations) {
|
public ListenableFuture<Server> removeAll(final Collection<Mutation> mutations) {
|
||||||
|
@ -53,6 +52,7 @@ public class MutationQueue {
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListenableFuture<Server> force(final Collection<Mutation> mutations) {
|
private ListenableFuture<Server> force(final Collection<Mutation> mutations) {
|
||||||
return minecraftService.updateLocalServer((ServerDoc.Mutation) () -> mutations.stream().map(Mutation::toApi).collect(Collectors.toSet()));
|
return minecraftService.updateLocalServer((ServerDoc.Mutation) () -> mutations.stream().map(Mutation::name).collect(Collectors.toSet()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,20 +2,21 @@ package tc.oc.pgm.mutation.command;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.logging.Level;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.collect.Sets;
|
import com.google.common.collect.Sets;
|
||||||
import com.sk89q.minecraft.util.commands.Command;
|
import com.sk89q.minecraft.util.commands.*;
|
||||||
import com.sk89q.minecraft.util.commands.CommandContext;
|
|
||||||
import com.sk89q.minecraft.util.commands.CommandException;
|
|
||||||
import com.sk89q.minecraft.util.commands.CommandPermissions;
|
|
||||||
import com.sk89q.minecraft.util.commands.NestedCommand;
|
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
import net.md_5.bungee.api.chat.BaseComponent;
|
||||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||||
import org.bukkit.command.CommandSender;
|
import org.bukkit.command.CommandSender;
|
||||||
|
import tc.oc.commons.bukkit.chat.PlayerComponent;
|
||||||
|
import tc.oc.commons.bukkit.chat.WarningComponent;
|
||||||
|
import tc.oc.commons.bukkit.nick.IdentityProvider;
|
||||||
|
import tc.oc.commons.core.chat.Audience;
|
||||||
import tc.oc.minecraft.scheduler.SyncExecutor;
|
import tc.oc.minecraft.scheduler.SyncExecutor;
|
||||||
import tc.oc.commons.bukkit.chat.Audiences;
|
import tc.oc.commons.bukkit.chat.Audiences;
|
||||||
import tc.oc.commons.bukkit.chat.ListComponent;
|
import tc.oc.commons.bukkit.chat.ListComponent;
|
||||||
|
@ -56,39 +57,42 @@ public class MutationCommands implements NestedCommands {
|
||||||
private final SyncExecutor syncExecutor;
|
private final SyncExecutor syncExecutor;
|
||||||
private final Audiences audiences;
|
private final Audiences audiences;
|
||||||
private final MutationQueue mutationQueue;
|
private final MutationQueue mutationQueue;
|
||||||
|
private final IdentityProvider identityProvider;
|
||||||
|
|
||||||
@Inject
|
@Inject MutationCommands(SyncExecutor syncExecutor, Audiences audiences, MutationQueue mutationQueue, IdentityProvider identityProvider) {
|
||||||
MutationCommands(SyncExecutor syncExecutor, Audiences audiences, MutationQueue mutationQueue) {
|
|
||||||
this.syncExecutor = syncExecutor;
|
this.syncExecutor = syncExecutor;
|
||||||
this.audiences = audiences;
|
this.audiences = audiences;
|
||||||
this.mutationQueue = mutationQueue;
|
this.mutationQueue = mutationQueue;
|
||||||
|
this.identityProvider = identityProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
aliases = {"enable", "add"},
|
aliases = {"enable", "e"},
|
||||||
desc = "Adds a mutation to the upcoming match." +
|
desc = "Adds a mutation to the upcoming match." +
|
||||||
"You can use '?' as a wildcard or " +
|
"You can use '?' as a wildcard or " +
|
||||||
"'*' to use all.",
|
"'*' to use all.",
|
||||||
usage = "<mutation|?|*>",
|
usage = "<mutation|?|*>",
|
||||||
|
flags = "q",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 1
|
max = 1
|
||||||
)
|
)
|
||||||
@CommandPermissions(PERMISSION_SET)
|
@CommandPermissions(PERMISSION_SET)
|
||||||
public void enable(CommandContext args, CommandSender sender) throws CommandException {
|
public void enable(CommandContext args, CommandSender sender) throws CommandException, SuggestException {
|
||||||
set(args, sender, true);
|
set(args, sender, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
aliases = {"disable", "remove"},
|
aliases = {"disable", "d"},
|
||||||
desc = "Remove a mutation to the upcoming match." +
|
desc = "Remove a mutation to the upcoming match." +
|
||||||
"You can use '?' as a wildcard or " +
|
"You can use '?' as a wildcard or " +
|
||||||
"'*' to use all.",
|
"'*' to use all.",
|
||||||
usage = "<mutation|?|*>",
|
usage = "<mutation|?|*>",
|
||||||
|
flags = "q",
|
||||||
min = 1,
|
min = 1,
|
||||||
max = 1
|
max = 1
|
||||||
)
|
)
|
||||||
@CommandPermissions(PERMISSION_SET)
|
@CommandPermissions(PERMISSION_SET)
|
||||||
public void disable(CommandContext args, CommandSender sender) throws CommandException {
|
public void disable(CommandContext args, CommandSender sender) throws CommandException, SuggestException {
|
||||||
set(args, sender, false);
|
set(args, sender, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,8 +109,8 @@ public class MutationCommands implements NestedCommands {
|
||||||
public void list(final CommandContext args, CommandSender sender) throws CommandException {
|
public void list(final CommandContext args, CommandSender sender) throws CommandException {
|
||||||
MutationMatchModule module = verify(sender);
|
MutationMatchModule module = verify(sender);
|
||||||
final boolean queued = args.hasFlag('q');
|
final boolean queued = args.hasFlag('q');
|
||||||
final Collection<Mutation> active = queued ? mutationQueue.mutations() : module.getActiveMutations();
|
final Collection<Mutation> active = queued ? mutationQueue.mutations() : module.mutationsActive();
|
||||||
new Paginator<Mutation>() {
|
new Paginator<Mutation>(Mutation.values().length / 2) {
|
||||||
@Override
|
@Override
|
||||||
protected BaseComponent title() {
|
protected BaseComponent title() {
|
||||||
return new TranslatableComponent(queued ? "command.mutation.list.queued" : "command.mutation.list.current");
|
return new TranslatableComponent(queued ? "command.mutation.list.queued" : "command.mutation.list.current");
|
||||||
|
@ -124,20 +128,26 @@ public class MutationCommands implements NestedCommands {
|
||||||
return CommandUtils.getMatchModule(MutationMatchModule.class, sender);
|
return CommandUtils.getMatchModule(MutationMatchModule.class, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void set(CommandContext args, final CommandSender sender, final boolean value) throws CommandException {
|
public void set(CommandContext args, final CommandSender sender, final boolean value) throws CommandException, SuggestException {
|
||||||
final MutationMatchModule module = verify(sender);
|
final MutationMatchModule module = verify(sender);
|
||||||
final Match match = module.getMatch();
|
final Match match = module.getMatch();
|
||||||
String action = args.getString(0);
|
String action = args.getString(0);
|
||||||
|
boolean queued = args.hasFlag('q') || match.isFinished();
|
||||||
// Mutations that *will* be added or removed
|
// Mutations that *will* be added or removed
|
||||||
final Collection<Mutation> mutations = new HashSet<>();
|
final Collection<Mutation> mutations = new HashSet<>();
|
||||||
// Mutations that *are allowed* to be added or removed
|
// Mutations that *are allowed* to be added or removed
|
||||||
final Collection<Mutation> availableMutations = Sets.newHashSet(Mutation.values());
|
final Collection<Mutation> availableMutations = Sets.newHashSet(Mutation.values());
|
||||||
|
|
||||||
final Collection<Mutation> queue = mutationQueue.mutations();
|
final Collection<Mutation> queue = queued ? mutationQueue.mutations() : module.mutationsActive();
|
||||||
if(value) availableMutations.removeAll(queue); else availableMutations.retainAll(queue);
|
if(value) availableMutations.removeAll(queue); else availableMutations.retainAll(queue);
|
||||||
// Check if all mutations have been enabled/disabled
|
// Check if all mutations have been enabled/disabled
|
||||||
if((queue.size() == Mutation.values().length && value) || (queue.isEmpty() && !value)) {
|
if((queue.size() == Mutation.values().length && value) || (queue.isEmpty() && !value)) {
|
||||||
throw newCommandException(sender, new TranslatableComponent(value ? "command.mutation.error.enabled" : "command.mutation.error.disabled"));
|
throw newCommandException(sender, new TranslatableComponent(value ? "command.mutation.error.enabled.all" : "command.mutation.error.disabled.all"));
|
||||||
|
}
|
||||||
|
// Suggest mutations for the user to choose
|
||||||
|
final SuggestionContext context = args.getSuggestionContext();
|
||||||
|
if(context != null) {
|
||||||
|
context.suggestArgument(0, StringUtils.complete(context.getPrefix(), availableMutations.stream().map(mutation -> mutation.name().toLowerCase())));
|
||||||
}
|
}
|
||||||
// Get which action the user wants to preform
|
// Get which action the user wants to preform
|
||||||
switch (action) {
|
switch (action) {
|
||||||
|
@ -145,27 +155,50 @@ public class MutationCommands implements NestedCommands {
|
||||||
case "?": mutations.add(Iterables.get(availableMutations, RandomUtils.safeNextInt(match.getRandom(), availableMutations.size()))); break;
|
case "?": mutations.add(Iterables.get(availableMutations, RandomUtils.safeNextInt(match.getRandom(), availableMutations.size()))); break;
|
||||||
default:
|
default:
|
||||||
Mutation query = StringUtils.bestFuzzyMatch(action, Sets.newHashSet(Mutation.values()), 0.9);
|
Mutation query = StringUtils.bestFuzzyMatch(action, Sets.newHashSet(Mutation.values()), 0.9);
|
||||||
if (query == null) {
|
if(query == null) {
|
||||||
throw newCommandException(sender, new TranslatableComponent("command.mutation.error.find", action));
|
throw newCommandException(sender, new TranslatableComponent("command.mutation.error.find", action));
|
||||||
|
} else if(value == queue.contains(query)) {
|
||||||
|
throw newCommandException(sender, new TranslatableComponent(value ? "command.mutation.error.enabled" : "command.mutation.error.disabled", query.getComponent(ChatColor.RED)));
|
||||||
} else {
|
} else {
|
||||||
mutations.add(query);
|
mutations.add(query);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Audience origin = audiences.get(sender);
|
||||||
// Send the queued changes off to the api
|
Audience all = audiences.localServer();
|
||||||
syncExecutor.callback(
|
String message = message(!queued, value, mutations.size() == 1);
|
||||||
value ? mutationQueue.mergeAll(mutations)
|
ListComponent changed = new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA)));
|
||||||
: mutationQueue.removeAll(mutations),
|
if(queued) {
|
||||||
result -> {
|
// Send the queued changes off to the api
|
||||||
audiences.get(sender).sendMessage(new Component(new TranslatableComponent(
|
syncExecutor.callback(
|
||||||
message(false, value, mutations.size() == 1),
|
value ? mutationQueue.mergeAll(mutations)
|
||||||
new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA)))
|
: mutationQueue.removeAll(mutations),
|
||||||
), ChatColor.WHITE));
|
result -> {
|
||||||
|
origin.sendMessage(new Component(new TranslatableComponent(message, changed), ChatColor.WHITE));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
// Make the changes immediately
|
||||||
|
for(Mutation mutation : mutations) {
|
||||||
|
try {
|
||||||
|
module.register(mutation, value);
|
||||||
|
module.mutate(mutation);
|
||||||
|
} catch(Throwable t) {
|
||||||
|
module.register(mutation, !value);
|
||||||
|
origin.sendMessage(
|
||||||
|
new WarningComponent(
|
||||||
|
"command.mutation.error.mutate",
|
||||||
|
mutation.getComponent(ChatColor.RED)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
module.getLogger().log(Level.SEVERE, "Unable to enable/disable mutation", t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
);
|
PlayerComponent player = new PlayerComponent(identityProvider.currentIdentity(sender));
|
||||||
|
all.sendMessage(new Component(new TranslatableComponent(message, player, changed)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: force enabling mutations
|
|
||||||
public String message(boolean now, boolean enable, boolean singular) {
|
public String message(boolean now, boolean enable, boolean singular) {
|
||||||
if(now) {
|
if(now) {
|
||||||
if(enable) {
|
if(enable) {
|
||||||
|
|
|
@ -1,28 +0,0 @@
|
||||||
package tc.oc.pgm.mutation.submodule;
|
|
||||||
|
|
||||||
import tc.oc.pgm.kits.Kit;
|
|
||||||
import tc.oc.pgm.match.Match;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mutation modules that injects special kits on participant spawn.
|
|
||||||
*/
|
|
||||||
public class KitMutationModule extends MutationModule {
|
|
||||||
|
|
||||||
protected final Kit[] kits;
|
|
||||||
protected final boolean force;
|
|
||||||
|
|
||||||
public KitMutationModule(Match match, boolean force, Kit... kits) {
|
|
||||||
super(match);
|
|
||||||
this.kits = kits;
|
|
||||||
this.force = force;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Kit[] getKits() {
|
|
||||||
return enabled ? kits : new Kit[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isForceful() {
|
|
||||||
return force;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,68 +0,0 @@
|
||||||
package tc.oc.pgm.mutation.submodule;
|
|
||||||
|
|
||||||
import org.bukkit.event.HandlerList;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import tc.oc.pgm.events.ListenerScope;
|
|
||||||
import tc.oc.pgm.match.Match;
|
|
||||||
import tc.oc.pgm.match.MatchScope;
|
|
||||||
import tc.oc.pgm.mutation.Mutation;
|
|
||||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bits of immutable code that manage a {@link Mutation}.
|
|
||||||
*
|
|
||||||
* This should be able to load at any time during the match
|
|
||||||
* and not cause any problems. This will allow mutations
|
|
||||||
* to be forcefully loaded on the fly without any worries
|
|
||||||
* of breaking the match state.
|
|
||||||
*
|
|
||||||
* TODO: Force loading is not been enabled yet, but all mutation
|
|
||||||
* modules should be ready for the switch.
|
|
||||||
*/
|
|
||||||
@ListenerScope(MatchScope.RUNNING)
|
|
||||||
public abstract class MutationModule implements Listener {
|
|
||||||
|
|
||||||
protected final Match match;
|
|
||||||
protected boolean enabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructed when {@link MutationMatchModule#load()}
|
|
||||||
* has been called. This will only be constructed if its
|
|
||||||
* subsequent {@link Mutation} is enabled for the match.
|
|
||||||
*
|
|
||||||
* @param match the match for this module.
|
|
||||||
*/
|
|
||||||
public MutationModule(Match match) {
|
|
||||||
this.match = match;
|
|
||||||
this.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the match starts.
|
|
||||||
*
|
|
||||||
* However, this should be able to be called at any
|
|
||||||
* point before the match ends and still work as expected.
|
|
||||||
* @param late called after the match starts.
|
|
||||||
*
|
|
||||||
* {@link MutationMatchModule#enable()}
|
|
||||||
*/
|
|
||||||
public void enable(boolean late) {
|
|
||||||
match.registerEvents(this);
|
|
||||||
enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the match ends.
|
|
||||||
*
|
|
||||||
* However, this should be able to be called at any
|
|
||||||
* point during a match and still work as expected.
|
|
||||||
* @param premature called before the match ends.
|
|
||||||
*
|
|
||||||
* {@link MutationMatchModule#disable()}
|
|
||||||
*/
|
|
||||||
public void disable(boolean premature) {
|
|
||||||
match.unregisterEvents(this);
|
|
||||||
enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,110 +0,0 @@
|
||||||
package tc.oc.pgm.mutation.submodule;
|
|
||||||
|
|
||||||
import org.bukkit.Material;
|
|
||||||
import org.bukkit.entity.Entity;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
import org.bukkit.entity.Projectile;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.EventPriority;
|
|
||||||
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
||||||
import org.bukkit.event.entity.ExplosionPrimeEvent;
|
|
||||||
import org.bukkit.inventory.ItemStack;
|
|
||||||
import org.bukkit.potion.PotionEffect;
|
|
||||||
import org.bukkit.potion.PotionEffectType;
|
|
||||||
import org.bukkit.util.Vector;
|
|
||||||
import java.time.Duration;
|
|
||||||
import tc.oc.commons.bukkit.inventory.ArmorType;
|
|
||||||
import tc.oc.commons.bukkit.inventory.Slot;
|
|
||||||
import tc.oc.commons.bukkit.item.ItemUtils;
|
|
||||||
import tc.oc.pgm.doublejump.DoubleJumpKit;
|
|
||||||
import tc.oc.pgm.kits.FreeItemKit;
|
|
||||||
import tc.oc.pgm.kits.Kit;
|
|
||||||
import tc.oc.pgm.kits.KitNode;
|
|
||||||
import tc.oc.pgm.kits.PotionKit;
|
|
||||||
import tc.oc.pgm.kits.SlotItemKit;
|
|
||||||
import tc.oc.pgm.match.Match;
|
|
||||||
import tc.oc.pgm.match.ParticipantState;
|
|
||||||
|
|
||||||
public class MutationModules {
|
|
||||||
|
|
||||||
public static class Explosives extends KitMutationModule {
|
|
||||||
public static final Float MULTIPLIER = 1.75f;
|
|
||||||
private static final Kit KIT = KitNode.of(new FreeItemKit(new ItemStack(Material.FLINT_AND_STEEL)),
|
|
||||||
new FreeItemKit(new ItemStack(Material.TNT, 16)));
|
|
||||||
|
|
||||||
public Explosives(Match match) {
|
|
||||||
super(match, false, KIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
|
||||||
public void onExplosionPrime(ExplosionPrimeEvent event) {
|
|
||||||
event.setRadius(event.getRadius() * MULTIPLIER);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Strength extends KitMutationModule {
|
|
||||||
public static final PotionEffect EFFECT = new PotionEffect(PotionEffectType.INCREASE_DAMAGE, Integer.MAX_VALUE, 0, false, false);
|
|
||||||
public static final PotionKit KIT = new PotionKit(EFFECT);
|
|
||||||
|
|
||||||
public Strength(Match match) {
|
|
||||||
super(match, true, KIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DoubleJump extends KitMutationModule {
|
|
||||||
public static final DoubleJumpKit KIT = new DoubleJumpKit(true, 3f, Duration.ofSeconds(5), true);
|
|
||||||
|
|
||||||
public DoubleJump(Match match) {
|
|
||||||
super(match, false, KIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Invisibility extends KitMutationModule {
|
|
||||||
public static final PotionEffect EFFECT = new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0, false, false);
|
|
||||||
public static final PotionKit KIT = new PotionKit(EFFECT);
|
|
||||||
|
|
||||||
public Invisibility(Match match) {
|
|
||||||
super(match, true, KIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Lightning extends TargetableMutationModule {
|
|
||||||
public static final Duration FREQUENCY = Duration.ofSeconds(30);
|
|
||||||
|
|
||||||
public Lightning(Match match) {
|
|
||||||
super(match, FREQUENCY, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute(ParticipantState player) {
|
|
||||||
match.getWorld().strikeLightning(player.getLocation().clone().add(Vector.getRandom()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Rage extends MutationModule {
|
|
||||||
public static final Integer DAMAGE = Integer.MAX_VALUE;
|
|
||||||
|
|
||||||
public Rage(Match match) {
|
|
||||||
super(match);
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
|
||||||
public void onPlayerDamage(EntityDamageByEntityEvent event) {
|
|
||||||
Entity damager = event.getDamager();
|
|
||||||
if ((damager instanceof Player && ItemUtils.isWeapon(((Player) damager).getItemInHand())) ||
|
|
||||||
(damager instanceof Projectile && ((Projectile) damager).getShooter() instanceof Player)) {
|
|
||||||
event.setDamage(DAMAGE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Elytra extends KitMutationModule {
|
|
||||||
public static final Kit KIT = KitNode.of(new SlotItemKit(new ItemStack(Material.ELYTRA), Slot.Armor.forType(ArmorType.CHESTPLATE)),
|
|
||||||
new DoubleJumpKit(true, 6f, Duration.ofSeconds(30), true));
|
|
||||||
|
|
||||||
public Elytra(Match match) {
|
|
||||||
super(match, true, KIT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package tc.oc.pgm.mutation.submodule;
|
|
||||||
|
|
||||||
import com.google.common.collect.Range;
|
|
||||||
import java.time.Duration;
|
|
||||||
import tc.oc.commons.core.random.Entropy;
|
|
||||||
import tc.oc.commons.core.scheduler.Task;
|
|
||||||
import tc.oc.commons.core.stream.Collectors;
|
|
||||||
import tc.oc.pgm.match.Match;
|
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
|
||||||
import tc.oc.pgm.match.MatchScope;
|
|
||||||
import tc.oc.pgm.match.ParticipantState;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A mutation module that executes a task on a random {@link ParticipantState}s.
|
|
||||||
*/
|
|
||||||
public abstract class TargetableMutationModule extends MutationModule {
|
|
||||||
|
|
||||||
protected final Duration frequency;
|
|
||||||
protected final int targets;
|
|
||||||
protected Task task;
|
|
||||||
|
|
||||||
public TargetableMutationModule(final Match match, Duration frequency, int targets) {
|
|
||||||
super(match);
|
|
||||||
this.frequency = checkNotNull(frequency, "frequency cannot be null");
|
|
||||||
this.targets = targets; checkArgument(targets >= 1, "amount of targets cannot be less than 1");
|
|
||||||
this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> {
|
|
||||||
final Entropy entropy = match.entropyForTick();
|
|
||||||
match.participants()
|
|
||||||
.filter(MatchPlayer::isSpawned)
|
|
||||||
.collect(Collectors.toRandomSubList(entropy, entropy.randomInt(Range.closed(1, targets))))
|
|
||||||
.forEach(player -> execute(player.getParticipantState()));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void disable(boolean premature) {
|
|
||||||
task.cancel();
|
|
||||||
super.disable(premature);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void execute(ParticipantState player);
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package tc.oc.pgm.mutation.types;
|
||||||
|
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import tc.oc.commons.bukkit.inventory.Slot;
|
||||||
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
|
import tc.oc.pgm.killreward.KillRewardMatchModule;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.kits.KitPlayerFacet;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutation module that injects special kits on spawn and/or kill.
|
||||||
|
*/
|
||||||
|
public class KitMutation extends MutationModule {
|
||||||
|
|
||||||
|
protected final List<Kit> kits;
|
||||||
|
protected final Map<MatchPlayer, List<Kit>> playerKits;
|
||||||
|
protected final Map<MatchPlayer, Map<Slot, ItemStack>> savedSlots;
|
||||||
|
protected final List<KillReward> rewards;
|
||||||
|
protected final boolean force;
|
||||||
|
|
||||||
|
public KitMutation(Match match, boolean force) {
|
||||||
|
super(match);
|
||||||
|
this.kits = new ArrayList<>();
|
||||||
|
this.playerKits = new WeakHashMap<>();
|
||||||
|
this.savedSlots = new WeakHashMap<>();
|
||||||
|
this.rewards = new ArrayList<>();
|
||||||
|
this.force = force;
|
||||||
|
}
|
||||||
|
|
||||||
|
public KitMutation(Match match, boolean force, Kit... kits) {
|
||||||
|
this(match, force);
|
||||||
|
Stream.of(kits).forEachOrdered(this.kits::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a list of kits to apply for the player.
|
||||||
|
* Called inside {@link #apply(MatchPlayer)}.
|
||||||
|
* @param player the player that will receive the kits
|
||||||
|
* @param kits a mutable list of kits.
|
||||||
|
*/
|
||||||
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
|
kits.addAll(this.kits);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the kits to the player.
|
||||||
|
* @param player the player.
|
||||||
|
*/
|
||||||
|
public void apply(MatchPlayer player) {
|
||||||
|
List<Kit> kits = new ArrayList<>();
|
||||||
|
kits(player, kits);
|
||||||
|
playerKits.put(player, kits);
|
||||||
|
saved().forEach(slot -> {
|
||||||
|
slot.item(player.getInventory()).ifPresent(item -> {
|
||||||
|
Map<Slot, ItemStack> slots = savedSlots.getOrDefault(player, new HashMap<>());
|
||||||
|
slots.put(slot, (ItemStack) item);
|
||||||
|
savedSlots.put(player, slots);
|
||||||
|
slot.putItem(player.getInventory(), null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
kits.forEach(kit -> player.facet(KitPlayerFacet.class).applyKit(kit, force));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forcefuly remove kits from the player.
|
||||||
|
* @param player the player.
|
||||||
|
*/
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
playerKits.getOrDefault(player, new ArrayList<>()).stream().filter(Kit::isRemovable).forEach(kit -> kit.remove(player));
|
||||||
|
savedSlots.getOrDefault(player, new HashMap<>()).forEach((Slot slot, ItemStack item) -> slot.putItem(player.getInventory(), item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Any slots in the player's inventory that should be saved
|
||||||
|
* before {@link #apply(MatchPlayer)} and restored after {@link #remove(MatchPlayer)}.
|
||||||
|
* @return the saved slots.
|
||||||
|
*/
|
||||||
|
public Stream<? extends Slot> saved() {
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
super.enable();
|
||||||
|
match.module(KillRewardMatchModule.class).get().rewards().addAll(rewards);
|
||||||
|
if(match.hasStarted()) {
|
||||||
|
match.participants().forEach(this::apply);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
match.module(KillRewardMatchModule.class).get().rewards().removeAll(rewards);
|
||||||
|
match.participants().forEach(this::remove);
|
||||||
|
kits.clear();
|
||||||
|
playerKits.clear();
|
||||||
|
savedSlots.clear();
|
||||||
|
rewards.clear();
|
||||||
|
super.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package tc.oc.pgm.mutation.types;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import tc.oc.commons.bukkit.item.ItemBuilder;
|
||||||
|
import tc.oc.commons.core.random.AdvancingEntropy;
|
||||||
|
import tc.oc.commons.core.random.Entropy;
|
||||||
|
import tc.oc.pgm.events.ListenerScope;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchScope;
|
||||||
|
import tc.oc.pgm.mutation.Mutation;
|
||||||
|
import tc.oc.pgm.mutation.MutationMatchModule;
|
||||||
|
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bits of immutable code that manage a {@link Mutation}.
|
||||||
|
*
|
||||||
|
* This should be able to load at any time during the match
|
||||||
|
* and not cause any problems. This will allow mutations
|
||||||
|
* to be forcefully loaded on the fly without any worries
|
||||||
|
* of breaking the match state.
|
||||||
|
*/
|
||||||
|
@ListenerScope(MatchScope.RUNNING)
|
||||||
|
public abstract class MutationModule implements Listener {
|
||||||
|
|
||||||
|
protected final Match match;
|
||||||
|
protected final World world;
|
||||||
|
protected final Entropy entropy;
|
||||||
|
protected final Random random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructed when {@link MutationMatchModule#load()}
|
||||||
|
* has been called. This will only be constructed if its
|
||||||
|
* subsequent {@link Mutation} is enabled for the match.
|
||||||
|
*
|
||||||
|
* @param match the match for this module.
|
||||||
|
*/
|
||||||
|
public MutationModule(Match match) {
|
||||||
|
this.match = match;
|
||||||
|
this.world = match.getWorld();
|
||||||
|
this.entropy = new AdvancingEntropy();
|
||||||
|
this.random = new Random();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the match starts.
|
||||||
|
*
|
||||||
|
* However, this should be able to be called at any
|
||||||
|
* point before the match ends and still work as expected.
|
||||||
|
*/
|
||||||
|
public void enable() {
|
||||||
|
match.registerEventsAndRepeatables(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the match ends.
|
||||||
|
*
|
||||||
|
* However, this should be able to be called at any
|
||||||
|
* point during a match and still work as expected.
|
||||||
|
*/
|
||||||
|
public void disable() {
|
||||||
|
match.unregisterEvents(this);
|
||||||
|
match.unregisterRepeatable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected <E extends Entity> E spawn(Location location, Class<E> entityClass) {
|
||||||
|
E entity = world.spawn(location, entityClass);
|
||||||
|
if(entity instanceof LivingEntity) {
|
||||||
|
LivingEntity living = (LivingEntity) entity;
|
||||||
|
living.setCanPickupItems(false);
|
||||||
|
living.setRemoveWhenFarAway(true);
|
||||||
|
EntityEquipment equipment = living.getEquipment();
|
||||||
|
equipment.setHelmetDropChance(0);
|
||||||
|
equipment.setChestplateDropChance(0);
|
||||||
|
equipment.setLeggingsDropChance(0);
|
||||||
|
equipment.setBootsDropChance(0);
|
||||||
|
}
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package tc.oc.pgm.mutation.types;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import tc.oc.commons.core.scheduler.Task;
|
||||||
|
import tc.oc.commons.core.stream.Collectors;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.match.MatchScope;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A mutation module that executes a task on random {@link MatchPlayer}s.
|
||||||
|
*/
|
||||||
|
public abstract class TargetMutation extends MutationModule {
|
||||||
|
|
||||||
|
private final Duration frequency;
|
||||||
|
private Task task;
|
||||||
|
|
||||||
|
public TargetMutation(final Match match, Duration frequency) {
|
||||||
|
super(match);
|
||||||
|
this.frequency = frequency;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a task on the given randomly selected players.
|
||||||
|
* @param players a list of players, which size is determined by {@link #targets()}.
|
||||||
|
*/
|
||||||
|
public abstract void execute(List<MatchPlayer> players);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine the number of random players to target.
|
||||||
|
* If there are no enough players on the server, it is possible
|
||||||
|
* that the number of targets is less than expected.
|
||||||
|
* @return number of targets.
|
||||||
|
*/
|
||||||
|
public abstract int targets();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a list of random players.
|
||||||
|
* @return the random players.
|
||||||
|
*/
|
||||||
|
public List<MatchPlayer> search() {
|
||||||
|
return match.participants()
|
||||||
|
.filter(MatchPlayer::isSpawned)
|
||||||
|
.collect(Collectors.toRandomSubList(entropy, targets()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
super.enable();
|
||||||
|
this.task = match.getScheduler(MatchScope.RUNNING).createRepeatingTask(frequency, () -> execute(search()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
if(task != null) {
|
||||||
|
task.cancel();
|
||||||
|
task = null;
|
||||||
|
}
|
||||||
|
super.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import tc.oc.commons.bukkit.inventory.ArmorType;
|
||||||
|
import tc.oc.commons.bukkit.inventory.Slot;
|
||||||
|
import tc.oc.commons.bukkit.item.ItemUtils;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKitApplicator;
|
||||||
|
import tc.oc.pgm.kits.SlotItemKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ArmorMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static FreeItemKit SWORD = new FreeItemKit(item(Material.DIAMOND_SWORD));
|
||||||
|
|
||||||
|
final static SlotItemKit[] ARMOR = new SlotItemKit[] {
|
||||||
|
new SlotItemKit(item(Material.DIAMOND_HELMET), Slot.Armor.forType(ArmorType.HELMET)),
|
||||||
|
new SlotItemKit(item(Material.DIAMOND_CHESTPLATE), Slot.Armor.forType(ArmorType.CHESTPLATE)),
|
||||||
|
new SlotItemKit(item(Material.DIAMOND_LEGGINGS), Slot.Armor.forType(ArmorType.LEGGINGS)),
|
||||||
|
new SlotItemKit(item(Material.DIAMOND_BOOTS), Slot.Armor.forType(ArmorType.BOOTS)),
|
||||||
|
};
|
||||||
|
|
||||||
|
final WeakHashMap<MatchPlayer, ItemStack> weapons;
|
||||||
|
|
||||||
|
public ArmorMutation(Match match) {
|
||||||
|
super(match, true, ARMOR);
|
||||||
|
this.kits.add(SWORD);
|
||||||
|
weapons = new WeakHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MatchPlayer player) {
|
||||||
|
// Find the player's first weapon and store it for later
|
||||||
|
List<ItemStack> hotbar = Slot.Hotbar.hotbar()
|
||||||
|
.map(slot -> slot.getItem(player.getInventory()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
for(ItemStack item : hotbar) {
|
||||||
|
if(item != null && ItemUtils.isWeapon(item)) {
|
||||||
|
weapons.put(player, item);
|
||||||
|
player.getInventory().remove(item);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
super.apply(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
super.remove(player);
|
||||||
|
// Restore the player's old weapon
|
||||||
|
ItemStack weapon = weapons.remove(player);
|
||||||
|
if(weapon != null) {
|
||||||
|
ItemKitApplicator applicator = new ItemKitApplicator();
|
||||||
|
applicator.add(weapon);
|
||||||
|
applicator.apply(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<? extends Slot> saved() {
|
||||||
|
return Slot.Armor.armor();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
super.disable();
|
||||||
|
weapons.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import tc.oc.commons.bukkit.inventory.ArmorType;
|
||||||
|
import tc.oc.commons.bukkit.inventory.Slot;
|
||||||
|
import tc.oc.pgm.doublejump.DoubleJumpKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKitApplicator;
|
||||||
|
import tc.oc.pgm.kits.SlotItemKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class ElytraMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static ItemKit ELYTRA = new SlotItemKit(item(Material.ELYTRA), Slot.Armor.forType(ArmorType.CHESTPLATE));
|
||||||
|
final static DoubleJumpKit JUMP = new DoubleJumpKit(true, 6f, Duration.ofSeconds(30), true);
|
||||||
|
|
||||||
|
public ElytraMutation(Match match) {
|
||||||
|
super(match, true, ELYTRA, JUMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<? extends Slot> saved() {
|
||||||
|
return Stream.of(Slot.Armor.forType(ArmorType.CHESTPLATE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
// If the player is mid-air, give them a totem so they don't fall and die
|
||||||
|
if(player.getBukkit().isGliding()) {
|
||||||
|
ItemKitApplicator applicator = new ItemKitApplicator();
|
||||||
|
applicator.put(Slot.OffHand.offHand(), item(Material.TOTEM), false);
|
||||||
|
applicator.apply(player);
|
||||||
|
}
|
||||||
|
super.remove(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,159 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import tc.oc.commons.bukkit.item.ItemUtils;
|
||||||
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
public class EnchantmentMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static ImmutableMap<Integer, Integer> LEVELS_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||||
|
.put(1, 10)
|
||||||
|
.put(2, 3)
|
||||||
|
.put(3, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Enchantment, Integer> ARMOR_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
|
.put(Enchantment.PROTECTION_ENVIRONMENTAL, 15)
|
||||||
|
.put(Enchantment.PROTECTION_PROJECTILE, 10)
|
||||||
|
.put(Enchantment.DURABILITY, 10)
|
||||||
|
.put(Enchantment.PROTECTION_EXPLOSIONS, 5)
|
||||||
|
.put(Enchantment.PROTECTION_FALL, 5)
|
||||||
|
.put(Enchantment.PROTECTION_FIRE, 5)
|
||||||
|
.put(Enchantment.THORNS, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Enchantment, Integer> BOOTS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
|
.putAll(ARMOR_MAP)
|
||||||
|
.put(Enchantment.WATER_WORKER, 5)
|
||||||
|
.put(Enchantment.DEPTH_STRIDER, 3)
|
||||||
|
.put(Enchantment.FROST_WALKER, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Enchantment, Integer> WEAPONS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
|
.put(Enchantment.DAMAGE_ALL, 15)
|
||||||
|
.put(Enchantment.DURABILITY, 10)
|
||||||
|
.put(Enchantment.KNOCKBACK, 10)
|
||||||
|
.put(Enchantment.MENDING, 5)
|
||||||
|
.put(Enchantment.SWEEPING_EDGE, 5)
|
||||||
|
.put(Enchantment.FIRE_ASPECT, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Enchantment, Integer> TOOLS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
|
.put(Enchantment.DURABILITY, 10)
|
||||||
|
.put(Enchantment.DIG_SPEED, 10)
|
||||||
|
.put(Enchantment.SILK_TOUCH, 5)
|
||||||
|
.put(Enchantment.LOOT_BONUS_BLOCKS, 5)
|
||||||
|
.put(Enchantment.LOOT_BONUS_MOBS, 5)
|
||||||
|
.put(Enchantment.LUCK, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Enchantment, Integer> BOWS_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
|
.put(Enchantment.ARROW_DAMAGE, 10)
|
||||||
|
.put(Enchantment.ARROW_KNOCKBACK, 5)
|
||||||
|
.put(Enchantment.ARROW_FIRE, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static Map<Enchantment, Integer> FISHING_MAP = new ImmutableMap.Builder<Enchantment, Integer>()
|
||||||
|
.put(Enchantment.KNOCKBACK, 3)
|
||||||
|
.put(Enchantment.LURE, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<Integer, Integer> LEVELS = new ImmutableWeightedRandomChooser<>(LEVELS_MAP);
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP);
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> BOOTS = new ImmutableWeightedRandomChooser<>(BOOTS_MAP);
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> WEAPONS = new ImmutableWeightedRandomChooser<>(WEAPONS_MAP);
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> TOOLS = new ImmutableWeightedRandomChooser<>(TOOLS_MAP);
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> BOWS = new ImmutableWeightedRandomChooser<>(BOWS_MAP);
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> FISHING = new ImmutableWeightedRandomChooser<>(FISHING_MAP);
|
||||||
|
|
||||||
|
Map<Entity, Map<ItemStack, Map<Enchantment, Integer>>> savedEnchantments;
|
||||||
|
|
||||||
|
public EnchantmentMutation(Match match) {
|
||||||
|
super(match, true);
|
||||||
|
this.savedEnchantments = new WeakHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void apply(ItemStack item, EntityEquipment equipment) {
|
||||||
|
// Pick the enchantment chooser depending on the item's material
|
||||||
|
WeightedRandomChooser<Enchantment, Integer> chooser;
|
||||||
|
if(item == null || ItemUtils.isNothing(item)) {
|
||||||
|
return;
|
||||||
|
} else if(ItemUtils.isWeapon(item)) {
|
||||||
|
chooser = WEAPONS;
|
||||||
|
} else if(ItemUtils.isArmor(item)) {
|
||||||
|
if(equipment.getBoots().equals(item)) {
|
||||||
|
chooser = BOOTS;
|
||||||
|
} else {
|
||||||
|
chooser = ARMOR;
|
||||||
|
}
|
||||||
|
} else if(ItemUtils.isTool(item)) {
|
||||||
|
chooser = TOOLS;
|
||||||
|
} else if(Material.FISHING_ROD.equals(item.getType())) {
|
||||||
|
chooser = FISHING;
|
||||||
|
} else if(Material.BOW.equals(item.getType())) {
|
||||||
|
chooser = BOWS;
|
||||||
|
} else {
|
||||||
|
chooser = null;
|
||||||
|
}
|
||||||
|
if(chooser != null) {
|
||||||
|
// Save the item's enchantments if they need to be restored
|
||||||
|
Entity entity = equipment.getHolder();
|
||||||
|
Map<ItemStack, Map<Enchantment, Integer>> byEntity = savedEnchantments.getOrDefault(entity, new WeakHashMap<>());
|
||||||
|
byEntity.put(item, ImmutableMap.copyOf(item.getEnchantments()));
|
||||||
|
savedEnchantments.put(entity, byEntity);
|
||||||
|
// Apply the new enchantments
|
||||||
|
int amountOfEnchants = LEVELS.choose(entropy);
|
||||||
|
for(int i = 0; i < amountOfEnchants; i++) {
|
||||||
|
item.addUnsafeEnchantment(chooser.choose(entropy), LEVELS.choose(entropy));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MatchPlayer player) {
|
||||||
|
super.apply(player);
|
||||||
|
player.getInventory().forEach(item -> {
|
||||||
|
// Random number of enchantments on each item
|
||||||
|
int numberOfEnchants = LEVELS.choose(entropy);
|
||||||
|
for(int i = 0; i < numberOfEnchants; i++) {
|
||||||
|
apply(item, player.getBukkit().getEquipment());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
super.remove(player);
|
||||||
|
remove(player.getBukkit());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Entity entity) {
|
||||||
|
savedEnchantments.getOrDefault(entity, new HashMap<>()).forEach((ItemStack item, Map<Enchantment, Integer> old) -> {
|
||||||
|
item.getEnchantments().keySet().forEach(item::removeEnchantment); // Clear the current enchantments
|
||||||
|
item.addUnsafeEnchantments(old); // Add the old enchantments back
|
||||||
|
});
|
||||||
|
savedEnchantments.remove(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
ImmutableSet.copyOf(savedEnchantments.keySet()).forEach(this::remove);
|
||||||
|
savedEnchantments.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,150 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.AbstractHorse;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.Horse;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.player.PlayerSpawnEntityEvent;
|
||||||
|
import org.bukkit.inventory.HorseInventory;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.SpawnEggMeta;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
|
import tc.oc.pgm.events.MatchPlayerDeathEvent;
|
||||||
|
import tc.oc.pgm.events.PlayerChangePartyEvent;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
public class EquestrianMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> TYPE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.put(EntityType.HORSE, 100)
|
||||||
|
// FIXME: Saddle do not work on these horses
|
||||||
|
//.put(EntityType.SKELETON_HORSE, 5)
|
||||||
|
//.put(EntityType.ZOMBIE_HORSE, 5)
|
||||||
|
//.put(EntityType.LLAMA, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Material, Integer> ARMOR_MAP = new ImmutableMap.Builder<Material, Integer>()
|
||||||
|
.put(Material.SADDLE, 25)
|
||||||
|
.put(Material.GOLD_BARDING, 10)
|
||||||
|
.put(Material.IRON_BARDING, 5)
|
||||||
|
.put(Material.DIAMOND_BARDING, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP);
|
||||||
|
final static WeightedRandomChooser<Material, Integer> ARMOR = new ImmutableWeightedRandomChooser<>(ARMOR_MAP);
|
||||||
|
|
||||||
|
final Map<MatchPlayer, AbstractHorse> horses;
|
||||||
|
|
||||||
|
public EquestrianMutation(Match match) {
|
||||||
|
super(match, false);
|
||||||
|
this.horses = new WeakHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
super.disable();
|
||||||
|
match.participants().forEach(this::remove);
|
||||||
|
horses.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
|
super.kits(player, kits);
|
||||||
|
Location location = player.getLocation();
|
||||||
|
// If there is not enough room to spawn a horse, give the player
|
||||||
|
// an egg so they can spawn it later
|
||||||
|
if(!spawnable(location)) {
|
||||||
|
ItemStack item = item(Material.MONSTER_EGG);
|
||||||
|
SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta();
|
||||||
|
egg.setSpawnedType(TYPES.choose(entropy));
|
||||||
|
item.setItemMeta(egg);
|
||||||
|
kits.add(new FreeItemKit(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MatchPlayer player) {
|
||||||
|
super.apply(player);
|
||||||
|
Location location = player.getLocation();
|
||||||
|
if(spawnable(location)) {
|
||||||
|
setup(player, spawn(location, (Class<? extends AbstractHorse>) TYPES.choose(match.entropyForTick()).getEntityClass()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void remove(MatchPlayer player) {
|
||||||
|
super.remove(player);
|
||||||
|
AbstractHorse horse = horses.remove(player);
|
||||||
|
if(horse != null) {
|
||||||
|
horse.ejectAll();
|
||||||
|
horse.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setup(MatchPlayer player, AbstractHorse horse) {
|
||||||
|
horses.put(player, horse);
|
||||||
|
horse.setAdult();
|
||||||
|
horse.setJumpStrength(2 * entropy.randomDouble());
|
||||||
|
horse.setDomestication(1);
|
||||||
|
horse.setMaxDomestication(1);
|
||||||
|
horse.setTamed(true);
|
||||||
|
horse.addPotionEffect(new PotionEffect(PotionEffectType.REGENERATION, Integer.MAX_VALUE, 0));
|
||||||
|
horse.setOwner(player.getBukkit());
|
||||||
|
horse.setPassenger(player.getBukkit());
|
||||||
|
if(horse instanceof Horse) {
|
||||||
|
Horse horsey = (Horse) horse;
|
||||||
|
horsey.setStyle(entropy.randomElement(Horse.Style.values()));
|
||||||
|
horsey.setColor(entropy.randomElement(Horse.Color.values()));
|
||||||
|
HorseInventory inventory = horsey.getInventory();
|
||||||
|
inventory.setSaddle(item(Material.SADDLE));
|
||||||
|
inventory.setArmor(item(ARMOR.choose(entropy)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean spawnable(Location location) {
|
||||||
|
// Allow at least 4 blocks of air from the feet of the player
|
||||||
|
// to allow a horse to be spawned
|
||||||
|
for(int i = 0; i <= 4; i++) {
|
||||||
|
if(!location.clone().add(0, i, 0).getBlock().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
|
public void onEntitySpawn(PlayerSpawnEntityEvent event) {
|
||||||
|
Entity entity = event.getEntity();
|
||||||
|
if(TYPE_MAP.containsKey(entity.getType())) {
|
||||||
|
match.participant(event.getPlayer())
|
||||||
|
.ifPresent(player -> setup(player, (AbstractHorse) event.getEntity()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.entity.TNTPrimed;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.entity.ExplosionPrimeEvent;
|
||||||
|
import org.bukkit.inventory.PlayerInventory;
|
||||||
|
import tc.oc.commons.bukkit.item.ItemBuilder;
|
||||||
|
import tc.oc.commons.core.collection.WeakHashSet;
|
||||||
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ExplosiveMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static ItemKit TNT = new FreeItemKit(item(Material.TNT, 8));
|
||||||
|
final static ItemKit LIGHTER = new FreeItemKit(item(Material.FLINT_AND_STEEL));
|
||||||
|
|
||||||
|
final static ItemKit FIRE_BOW = new FreeItemKit(new ItemBuilder(item(Material.BOW)).enchant(Enchantment.ARROW_FIRE, 1).get());
|
||||||
|
final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 8));
|
||||||
|
|
||||||
|
final static Range<Integer> RADIUS = Range.openClosed(0, 4);
|
||||||
|
|
||||||
|
final WeakHashSet<TNTPrimed> tracked;
|
||||||
|
|
||||||
|
public ExplosiveMutation(Match match) {
|
||||||
|
super(match, false);
|
||||||
|
this.tracked = new WeakHashSet<>();
|
||||||
|
this.rewards.add(new KillReward(TNT));
|
||||||
|
this.rewards.add(new KillReward(ARROWS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
super.disable();
|
||||||
|
tracked.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
|
super.kits(player, kits);
|
||||||
|
PlayerInventory inv = player.getInventory();
|
||||||
|
if(random.nextBoolean()) { // tnt and lighter kit
|
||||||
|
if(!inv.contains(Material.TNT)) kits.add(TNT);
|
||||||
|
if(!inv.contains(Material.FLINT_AND_STEEL)) kits.add(LIGHTER);
|
||||||
|
} else { // fire bow and arrows kit
|
||||||
|
if(!inv.contains(Material.ARROW)) kits.add(ARROWS);
|
||||||
|
if(!inv.contains(Material.BOW)) {
|
||||||
|
kits.add(FIRE_BOW);
|
||||||
|
} else {
|
||||||
|
inv.all(Material.BOW).values().forEach(bow -> bow.addUnsafeEnchantments(FIRE_BOW.item().getEnchantments()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
|
public void onExplosionPrime(ExplosionPrimeEvent event) {
|
||||||
|
event.setRadius(event.getRadius() + entropy.randomInt(RADIUS));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import tc.oc.pgm.kits.PotionKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
public class GlowMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static PotionKit GLOW = new PotionKit(new PotionEffect(PotionEffectType.GLOWING, Integer.MAX_VALUE, 0));
|
||||||
|
|
||||||
|
public GlowMutation(Match match) {
|
||||||
|
super(match, true, GLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import tc.oc.pgm.gamerules.GameRulesMatchModule;
|
||||||
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
import tc.oc.pgm.shield.ShieldKit;
|
||||||
|
import tc.oc.pgm.shield.ShieldParameters;
|
||||||
|
|
||||||
|
public class HardcoreMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static String KEY = "naturalRegeneration";
|
||||||
|
final static ShieldKit SHIELD = new ShieldKit(new ShieldParameters());
|
||||||
|
final static ItemKit APPLE = new FreeItemKit(item(Material.GOLDEN_APPLE));
|
||||||
|
|
||||||
|
String previous; // Stores the previous game rule setting
|
||||||
|
|
||||||
|
public HardcoreMutation(Match match) {
|
||||||
|
super(match, false);
|
||||||
|
this.kits.add(SHIELD);
|
||||||
|
this.rewards.add(new KillReward(APPLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameRulesMatchModule rules() {
|
||||||
|
return match.module(GameRulesMatchModule.class).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
super.enable();
|
||||||
|
previous = world.getGameRuleValue(KEY);
|
||||||
|
rules().gameRules().put(KEY, "false");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
rules().gameRules().remove(KEY);
|
||||||
|
if(previous != null) {
|
||||||
|
world.setGameRuleValue(KEY, previous);
|
||||||
|
}
|
||||||
|
super.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.HealthKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
|
import tc.oc.pgm.kits.MaxHealthKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
public class HealthMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static HealthKit HEALTH = new HealthKit(40);
|
||||||
|
final static MaxHealthKit MAX_HEALTH = new MaxHealthKit(40);
|
||||||
|
final static ItemKit APPLE = new FreeItemKit(item(Material.GOLDEN_APPLE, 3));
|
||||||
|
|
||||||
|
public HealthMutation(Match match) {
|
||||||
|
super(match, false, MAX_HEALTH, HEALTH, APPLE);
|
||||||
|
this.rewards.add(new KillReward(APPLE));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import tc.oc.pgm.doublejump.DoubleJumpKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
|
||||||
|
public class JumpMutation extends NoFallMutation {
|
||||||
|
|
||||||
|
final static DoubleJumpKit JUMP = new DoubleJumpKit();
|
||||||
|
|
||||||
|
public JumpMutation(Match match) {
|
||||||
|
super(match, false, JUMP);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.SpawnEggMeta;
|
||||||
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class MobsMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> TYPE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.put(EntityType.ZOMBIE, 50)
|
||||||
|
.put(EntityType.SKELETON, 40)
|
||||||
|
.put(EntityType.SPIDER, 40)
|
||||||
|
.put(EntityType.CREEPER, 30)
|
||||||
|
.put(EntityType.BLAZE, 20)
|
||||||
|
.put(EntityType.GHAST, 20)
|
||||||
|
.put(EntityType.SHULKER, 20)
|
||||||
|
.put(EntityType.WITCH, 10)
|
||||||
|
.put(EntityType.ENDERMAN, 10)
|
||||||
|
.put(EntityType.PIG_ZOMBIE, 5)
|
||||||
|
.put(EntityType.WITHER_SKELETON, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> TYPES = new ImmutableWeightedRandomChooser<>(TYPE_MAP);
|
||||||
|
|
||||||
|
final static Range<Integer> AMOUNT = Range.closed(1, 3);
|
||||||
|
|
||||||
|
public MobsMutation(Match match) {
|
||||||
|
super(match, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
|
super.kits(player, kits);
|
||||||
|
int eggs = entropy.randomInt(AMOUNT);
|
||||||
|
for(int i = 0; i < eggs; i++) {
|
||||||
|
ItemStack item = item(Material.MONSTER_EGG, entropy.randomInt(AMOUNT));
|
||||||
|
SpawnEggMeta egg = (SpawnEggMeta) item.getItemMeta();
|
||||||
|
egg.setSpawnedType(TYPES.choose(entropy));
|
||||||
|
item.setItemMeta(egg);
|
||||||
|
kits.add(new FreeItemKit(item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.event.entity.EntityDamageEvent;
|
||||||
|
import tc.oc.pgm.damage.DisableDamageMatchModule;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.PlayerRelation;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public abstract class NoFallMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static EntityDamageEvent.DamageCause FALL = EntityDamageEvent.DamageCause.FALL;
|
||||||
|
final static Iterable<PlayerRelation> RELATIONS = Stream.of(PlayerRelation.values()).collect(Collectors.toList());
|
||||||
|
|
||||||
|
Iterable<PlayerRelation> previous;
|
||||||
|
|
||||||
|
public NoFallMutation(Match match, boolean force, Kit... kits) {
|
||||||
|
super(match, force, kits);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DisableDamageMatchModule damage() {
|
||||||
|
return match.module(DisableDamageMatchModule.class).get();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
super.enable();
|
||||||
|
previous = damage().causes().get(FALL);
|
||||||
|
damage().causes().putAll(FALL, RELATIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
damage().causes().removeAll(FALL);
|
||||||
|
if(previous != null) {
|
||||||
|
damage().causes().putAll(FALL, previous);
|
||||||
|
}
|
||||||
|
super.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,100 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.inventory.meta.PotionMeta;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class PotionMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static ImmutableMap<PotionEffectType, Integer> BAD_MAP = new ImmutableMap.Builder<PotionEffectType, Integer>()
|
||||||
|
.put(PotionEffectType.WEAKNESS, 15)
|
||||||
|
.put(PotionEffectType.SLOW, 10)
|
||||||
|
.put(PotionEffectType.POISON, 10)
|
||||||
|
.put(PotionEffectType.BLINDNESS, 3)
|
||||||
|
.put(PotionEffectType.LEVITATION, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<PotionEffectType, Integer> GOOD_MAP = new ImmutableMap.Builder<PotionEffectType, Integer>()
|
||||||
|
.put(PotionEffectType.SPEED, 15)
|
||||||
|
.put(PotionEffectType.INCREASE_DAMAGE, 15)
|
||||||
|
.put(PotionEffectType.DAMAGE_RESISTANCE, 10)
|
||||||
|
.put(PotionEffectType.REGENERATION, 10)
|
||||||
|
.put(PotionEffectType.FIRE_RESISTANCE, 10)
|
||||||
|
.put(PotionEffectType.HEALTH_BOOST, 5)
|
||||||
|
.put(PotionEffectType.JUMP, 5)
|
||||||
|
.put(PotionEffectType.INVISIBILITY, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Material, Integer> BOTTLE_BAD_MAP = new ImmutableMap.Builder<Material, Integer>()
|
||||||
|
.put(Material.SPLASH_POTION, 5)
|
||||||
|
.put(Material.LINGERING_POTION, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Material, Integer> BOTTLE_GOOD_MAP = new ImmutableMap.Builder<Material, Integer>()
|
||||||
|
.putAll(BOTTLE_BAD_MAP)
|
||||||
|
.put(Material.POTION, 10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<PotionEffectType, Integer> BAD = new ImmutableWeightedRandomChooser<>(BAD_MAP);
|
||||||
|
final static WeightedRandomChooser<PotionEffectType, Integer> GOOD = new ImmutableWeightedRandomChooser<>(GOOD_MAP);
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<Material, Integer> BAD_BOTTLE = new ImmutableWeightedRandomChooser<>(BOTTLE_BAD_MAP);
|
||||||
|
final static WeightedRandomChooser<Material, Integer> GOOD_BOTTLE = new ImmutableWeightedRandomChooser<>(BOTTLE_GOOD_MAP);
|
||||||
|
|
||||||
|
final static Range<Integer> BAD_DURATION_RANGE = Range.closed(3, 10);
|
||||||
|
final static Range<Integer> GOOD_DURATION_RANGE = Range.closed(10, 45);
|
||||||
|
|
||||||
|
final static Range<Integer> AMOUNT_RANGE = Range.closed(1, 3);
|
||||||
|
final static Range<Integer> AMPLIFIER_RANGE = Range.closed(0, 2);
|
||||||
|
|
||||||
|
public PotionMutation(Match match) {
|
||||||
|
super(match, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
|
super.kits(player, kits);
|
||||||
|
int numberOfPotions = entropy.randomInt(AMOUNT_RANGE);
|
||||||
|
for(int i = 0; i < numberOfPotions; i++) {
|
||||||
|
WeightedRandomChooser<PotionEffectType, Integer> type;
|
||||||
|
WeightedRandomChooser<Material, Integer> material;
|
||||||
|
Range<Integer> range;
|
||||||
|
// Determine whether the potion will be "good" or "bad"
|
||||||
|
if(random.nextBoolean()) {
|
||||||
|
type = BAD;
|
||||||
|
material = BAD_BOTTLE;
|
||||||
|
range = BAD_DURATION_RANGE;
|
||||||
|
} else {
|
||||||
|
type = GOOD;
|
||||||
|
material = GOOD_BOTTLE;
|
||||||
|
range = GOOD_DURATION_RANGE;
|
||||||
|
}
|
||||||
|
// Choose all the random attributes
|
||||||
|
PotionEffectType effect = type.choose(entropy);
|
||||||
|
Material bottle = material.choose(entropy);
|
||||||
|
int duration = 20 * entropy.randomInt(range);
|
||||||
|
int amplifier = entropy.randomInt(AMPLIFIER_RANGE);
|
||||||
|
// Apply the attributes to the item stack
|
||||||
|
ItemStack potion = item(bottle);
|
||||||
|
PotionMeta meta = (PotionMeta) potion.getItemMeta();
|
||||||
|
meta.addCustomEffect(new PotionEffect(effect, duration, amplifier), true);
|
||||||
|
potion.setItemMeta(meta);
|
||||||
|
kits.add(new FreeItemKit(potion));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.enchantments.Enchantment;
|
||||||
|
import org.bukkit.entity.Arrow;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.TippedArrow;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.entity.EntityShootBowEvent;
|
||||||
|
import org.bukkit.event.entity.ProjectileLaunchEvent;
|
||||||
|
import org.bukkit.inventory.Inventory;
|
||||||
|
import org.bukkit.metadata.FixedMetadataValue;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
|
import tc.oc.pgm.PGM;
|
||||||
|
import tc.oc.pgm.killreward.KillReward;
|
||||||
|
import tc.oc.pgm.kits.FreeItemKit;
|
||||||
|
import tc.oc.pgm.kits.ItemKit;
|
||||||
|
import tc.oc.pgm.kits.Kit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ProjectileMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<Enchantment, Integer> ENCHANTMENTS = new ImmutableWeightedRandomChooser<>(EnchantmentMutation.BOWS_MAP);
|
||||||
|
final static WeightedRandomChooser<PotionEffectType, Integer> POTIONS = new ImmutableWeightedRandomChooser<>(PotionMutation.BAD_MAP);
|
||||||
|
|
||||||
|
final static Range<Integer> ENCHANT_RANGE = Range.closed(1, 3);
|
||||||
|
final static Range<Integer> AMPLIFIER_RANGE = Range.closed(0, 3);
|
||||||
|
final static Range<Integer> DURATION_RANGE = Range.closed(3, 10);
|
||||||
|
|
||||||
|
final static ItemKit BOW = new FreeItemKit(item(Material.BOW));
|
||||||
|
final static ItemKit ARROWS = new FreeItemKit(item(Material.ARROW, 16));
|
||||||
|
|
||||||
|
final static String KEY = "is_modified_arrow";
|
||||||
|
|
||||||
|
public ProjectileMutation(Match match) {
|
||||||
|
super(match, false);
|
||||||
|
this.rewards.add(new KillReward(ARROWS));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MatchPlayer player) {
|
||||||
|
super.apply(player);
|
||||||
|
Inventory inventory = player.getInventory();
|
||||||
|
inventory.all(Material.BOW).values().forEach(arrow -> arrow.addUnsafeEnchantment(ENCHANTMENTS.choose(entropy), entropy.randomInt(ENCHANT_RANGE)));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void kits(MatchPlayer player, List<Kit> kits) {
|
||||||
|
super.kits(player, kits);
|
||||||
|
Inventory inventory = player.getInventory();
|
||||||
|
if(!inventory.contains(Material.BOW)) kits.add(BOW);
|
||||||
|
if(!inventory.contains(Material.ARROW)) kits.add(ARROWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(ignoreCancelled = true, priority = EventPriority.HIGHEST)
|
||||||
|
public void onBowShoot(EntityShootBowEvent event) {
|
||||||
|
Entity projectile = event.getProjectile();
|
||||||
|
if(projectile instanceof Arrow && (!projectile.hasMetadata(KEY) || !projectile.getMetadata(KEY, PGM.get()).asBoolean())) {
|
||||||
|
Arrow arrow = (Arrow) projectile;
|
||||||
|
TippedArrow tipped = world.spawn(projectile.getLocation(), TippedArrow.class);
|
||||||
|
tipped.setMetadata(KEY, new FixedMetadataValue(PGM.get(), true));
|
||||||
|
tipped.setCritical(arrow.isCritical());
|
||||||
|
tipped.setKnockbackStrength(arrow.getKnockbackStrength());
|
||||||
|
tipped.setDamage(arrow.getDamage());
|
||||||
|
tipped.setShooter(arrow.getShooter());
|
||||||
|
tipped.setVelocity(projectile.getVelocity());
|
||||||
|
tipped.setPickupRule(Arrow.PickupRule.DISALLOWED);
|
||||||
|
tipped.addCustomEffect(new PotionEffect(POTIONS.choose(entropy), 20 * entropy.randomInt(DURATION_RANGE), entropy.randomInt(AMPLIFIER_RANGE)), true);
|
||||||
|
arrow.remove();
|
||||||
|
event.setCancelled(true);
|
||||||
|
match.callEvent(new ProjectileLaunchEvent(tipped));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package tc.oc.pgm.mutation.types.kit;
|
||||||
|
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import tc.oc.commons.bukkit.inventory.Slot;
|
||||||
|
import tc.oc.pgm.kits.PotionKit;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
|
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
public class StealthMutation extends KitMutation {
|
||||||
|
|
||||||
|
final static PotionKit INVISIBILITY = new PotionKit(new PotionEffect(PotionEffectType.INVISIBILITY, Integer.MAX_VALUE, 0));
|
||||||
|
|
||||||
|
public StealthMutation(Match match) {
|
||||||
|
super(match, true, INVISIBILITY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Stream<? extends Slot> saved() {
|
||||||
|
return Slot.Armor.armor();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package tc.oc.pgm.mutation.types.other;
|
||||||
|
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.mutation.types.MutationModule;
|
||||||
|
import tc.oc.pgm.rage.RageMatchModule;
|
||||||
|
|
||||||
|
public class RageMutation extends MutationModule {
|
||||||
|
|
||||||
|
RageMatchModule rage;
|
||||||
|
|
||||||
|
public RageMutation(Match match) {
|
||||||
|
super(match);
|
||||||
|
this.rage = match.module(RageMatchModule.class).orElse(new RageMatchModule(match));
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
||||||
|
public void onPlayerDamage(EntityDamageByEntityEvent event) {
|
||||||
|
rage.handlePlayerDamage(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
super.disable();
|
||||||
|
rage = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,315 @@
|
||||||
|
package tc.oc.pgm.mutation.types.targetable;
|
||||||
|
|
||||||
|
import com.google.common.collect.ImmutableMap;
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.apache.commons.lang.math.Fraction;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.entity.Creeper;
|
||||||
|
import org.bukkit.entity.Entity;
|
||||||
|
import org.bukkit.entity.EntityType;
|
||||||
|
import org.bukkit.entity.LivingEntity;
|
||||||
|
import org.bukkit.entity.PigZombie;
|
||||||
|
import org.bukkit.entity.Slime;
|
||||||
|
import org.bukkit.entity.TNTPrimed;
|
||||||
|
import org.bukkit.entity.Zombie;
|
||||||
|
import org.bukkit.inventory.EntityEquipment;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import tc.oc.commons.core.random.ImmutableWeightedRandomChooser;
|
||||||
|
import tc.oc.commons.core.random.WeightedRandomChooser;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.match.Repeatable;
|
||||||
|
import tc.oc.pgm.mutation.types.kit.EnchantmentMutation;
|
||||||
|
import tc.oc.pgm.mutation.types.TargetMutation;
|
||||||
|
import tc.oc.pgm.points.PointProviderAttributes;
|
||||||
|
import tc.oc.pgm.points.RandomPointProvider;
|
||||||
|
import tc.oc.pgm.points.RegionPointProvider;
|
||||||
|
import tc.oc.pgm.regions.CuboidRegion;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
import static tc.oc.commons.core.random.RandomUtils.nextBoolean;
|
||||||
|
|
||||||
|
public class ApocalypseMutation extends TargetMutation {
|
||||||
|
|
||||||
|
final static ImmutableMap<Integer, Integer> AMOUNT_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||||
|
.put(3, 25)
|
||||||
|
.put(5, 20)
|
||||||
|
.put(10, 15)
|
||||||
|
.put(20, 5)
|
||||||
|
.put(50, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<Integer, Integer> STACK_MAP = new ImmutableMap.Builder<Integer, Integer>()
|
||||||
|
.put(1, 100)
|
||||||
|
.put(2, 25)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> AERIAL_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.put(EntityType.VEX, 5)
|
||||||
|
.put(EntityType.BLAZE, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> GROUND_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.put(EntityType.SPIDER, 50)
|
||||||
|
.put(EntityType.ZOMBIE, 40)
|
||||||
|
.put(EntityType.CREEPER, 30)
|
||||||
|
.put(EntityType.HUSK, 20)
|
||||||
|
.put(EntityType.CAVE_SPIDER, 10)
|
||||||
|
.put(EntityType.PIG_ZOMBIE, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> RANGED_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.put(EntityType.SKELETON, 50)
|
||||||
|
.put(EntityType.STRAY, 20)
|
||||||
|
.put(EntityType.BLAZE, 20)
|
||||||
|
.put(EntityType.GHAST, 10)
|
||||||
|
.put(EntityType.SHULKER, 5)
|
||||||
|
.put(EntityType.WITCH, 5)
|
||||||
|
.put(EntityType.WITHER_SKELETON, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> FLYABLE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.putAll(AERIAL_MAP)
|
||||||
|
.put(EntityType.BAT, 10)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> PASSENGER_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.putAll(RANGED_MAP)
|
||||||
|
.put(EntityType.CREEPER, 40)
|
||||||
|
.put(EntityType.PRIMED_TNT, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static ImmutableMap<EntityType, Integer> CUBE_MAP = new ImmutableMap.Builder<EntityType, Integer>()
|
||||||
|
.put(EntityType.SLIME, 10)
|
||||||
|
.put(EntityType.MAGMA_CUBE, 1)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
final static WeightedRandomChooser<Integer, Integer> AMOUNT = new ImmutableWeightedRandomChooser<>(AMOUNT_MAP);
|
||||||
|
final static WeightedRandomChooser<Integer, Integer> STACK = new ImmutableWeightedRandomChooser<>(STACK_MAP);
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> AERIAL = new ImmutableWeightedRandomChooser<>(AERIAL_MAP);
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> GROUND = new ImmutableWeightedRandomChooser<>(GROUND_MAP);
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> RANGED = new ImmutableWeightedRandomChooser<>(RANGED_MAP);
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> FLYABLE = new ImmutableWeightedRandomChooser<>(FLYABLE_MAP);
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> PASSENGER = new ImmutableWeightedRandomChooser<>(PASSENGER_MAP);
|
||||||
|
final static WeightedRandomChooser<EntityType, Integer> CUBE = new ImmutableWeightedRandomChooser<>(CUBE_MAP);
|
||||||
|
|
||||||
|
final static int DISTANCE = 15; // Max distance entities spawn from players
|
||||||
|
final static int PARTICIPANT_ENTITIES = 25; // Max entities on the field per participant
|
||||||
|
final static int MAX_ENTITIES = 500; // Max total entities on the field
|
||||||
|
final static Range<Integer> AIR_OFFSET = Range.closed(DISTANCE / 4, DISTANCE); // Y-axis offset for spawning flying entities
|
||||||
|
final static Fraction SPECIAL_CHANCE = Fraction.ONE_FIFTH; // Chance of a special attribute occuring in an entity
|
||||||
|
final static int SPECIAL_MULTIPLIER = 3; // Multiplier for special attributes
|
||||||
|
|
||||||
|
final WeakHashMap<Entity, Instant> entities;
|
||||||
|
long time;
|
||||||
|
|
||||||
|
public ApocalypseMutation(Match match) {
|
||||||
|
super(match, Duration.ofSeconds(20));
|
||||||
|
this.entities = new WeakHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the maximum amount of entities that can be spawned.
|
||||||
|
*/
|
||||||
|
public int entitiesMax() {
|
||||||
|
return Math.min((int) match.participants().count() * PARTICIPANT_ENTITIES, MAX_ENTITIES);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the number of available slots are left for additional entities to spawn.
|
||||||
|
*/
|
||||||
|
public int entitiesLeft() {
|
||||||
|
return entitiesMax() - world.getLivingEntities().size() + (int) match.participants().count();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random spawn point given two locations.
|
||||||
|
*/
|
||||||
|
public Optional<Location> location(Location start, Location end) {
|
||||||
|
return Optional.ofNullable(new RandomPointProvider(Collections.singleton(new RegionPointProvider(new CuboidRegion(start.position(), end.position()), new PointProviderAttributes()))).getPoint(match, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn a cohort of entities at the given location.
|
||||||
|
* @param location location to spawn the entity.
|
||||||
|
* @param ground whether the location is on the ground.
|
||||||
|
*/
|
||||||
|
public void spawn(Location location, boolean ground) {
|
||||||
|
int slots = entitiesLeft();
|
||||||
|
int queued = AMOUNT.choose(entropy);
|
||||||
|
// Remove any entities that may be over the max limit
|
||||||
|
despawn(queued - slots);
|
||||||
|
// Determine whether the entities should be airborn
|
||||||
|
int stack = STACK.choose(entropy);
|
||||||
|
boolean air = !ground || nextBoolean(random, SPECIAL_CHANCE);
|
||||||
|
if(air) {
|
||||||
|
stack += (stack == 1 && random.nextBoolean() ? 1 : 0);
|
||||||
|
location.add(0, entropy.randomInt(AIR_OFFSET), 0);
|
||||||
|
}
|
||||||
|
// Select the random entity chooser based on ground, air, and stacked
|
||||||
|
boolean stacked = stack > 1;
|
||||||
|
WeightedRandomChooser<EntityType, Integer> chooser;
|
||||||
|
if(air) {
|
||||||
|
if(stacked) {
|
||||||
|
if(ground) {
|
||||||
|
chooser = nextBoolean(random, SPECIAL_CHANCE) ? CUBE : FLYABLE;
|
||||||
|
} else {
|
||||||
|
chooser = FLYABLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
chooser = AERIAL;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(stacked) {
|
||||||
|
chooser = GROUND;
|
||||||
|
} else {
|
||||||
|
chooser = random.nextBoolean() ? GROUND : RANGED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Select the specific entity types for the spawn,
|
||||||
|
// all entities will have the same sequence of entity type
|
||||||
|
// but may have variations (like armor) between them.
|
||||||
|
List<EntityType> types = new ArrayList<>();
|
||||||
|
for(int i = 0; i < stack; i++) {
|
||||||
|
types.add((i == 0 ? chooser : PASSENGER).choose(entropy));
|
||||||
|
}
|
||||||
|
// Spawn the mobs and stack them if required
|
||||||
|
for(int i = 0; i < queued; i++) {
|
||||||
|
Entity last = null;
|
||||||
|
for(EntityType type : types) {
|
||||||
|
Entity entity = spawn(location, type);
|
||||||
|
if(last != null) {
|
||||||
|
last.setPassenger(entity);
|
||||||
|
}
|
||||||
|
last = entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn an individual entitiy at a location given an entity type.
|
||||||
|
* @param location the location to spawn at.
|
||||||
|
* @param type the type of entity.
|
||||||
|
* @return the constructed entity.
|
||||||
|
*/
|
||||||
|
public LivingEntity spawn(Location location, EntityType type) {
|
||||||
|
EnchantmentMutation enchant = new EnchantmentMutation(match);
|
||||||
|
LivingEntity entity = (LivingEntity) spawn(location, type.getEntityClass());
|
||||||
|
EntityEquipment equipment = entity.getEquipment();
|
||||||
|
entity.setVelocity(Vector.getRandom());
|
||||||
|
entity.setAbsorption(5);
|
||||||
|
ItemStack held = null;
|
||||||
|
switch(type) {
|
||||||
|
case SKELETON:
|
||||||
|
case WITHER_SKELETON:
|
||||||
|
case STRAY:
|
||||||
|
held = item(Material.BOW);
|
||||||
|
break;
|
||||||
|
case ZOMBIE:
|
||||||
|
case ZOMBIE_VILLAGER:
|
||||||
|
case HUSK:
|
||||||
|
Zombie zombie = (Zombie) entity;
|
||||||
|
zombie.setBaby(nextBoolean(random, SPECIAL_CHANCE));
|
||||||
|
break;
|
||||||
|
case PIG_ZOMBIE:
|
||||||
|
PigZombie pigZombie = (PigZombie) entity;
|
||||||
|
pigZombie.setAngry(true);
|
||||||
|
pigZombie.setAnger(Integer.MAX_VALUE);
|
||||||
|
held = item(Material.GOLD_SWORD);
|
||||||
|
break;
|
||||||
|
case CREEPER:
|
||||||
|
Creeper creeper = (Creeper) entity;
|
||||||
|
creeper.setPowered(nextBoolean(random, SPECIAL_CHANCE));
|
||||||
|
world.strikeLightningEffect(location);
|
||||||
|
break;
|
||||||
|
case PRIMED_TNT:
|
||||||
|
TNTPrimed tnt = (TNTPrimed) entity;
|
||||||
|
tnt.setFuseTicks(tnt.getFuseTicks() * SPECIAL_MULTIPLIER);
|
||||||
|
break;
|
||||||
|
case SLIME:
|
||||||
|
case MAGMA_CUBE:
|
||||||
|
Slime slime = (Slime) entity;
|
||||||
|
slime.setSize(slime.getSize() * SPECIAL_MULTIPLIER);
|
||||||
|
break;
|
||||||
|
case SKELETON_HORSE:
|
||||||
|
world.strikeLightning(location);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if(held != null && random.nextBoolean()) {
|
||||||
|
enchant.apply(held, equipment);
|
||||||
|
equipment.setItemInMainHand(held);
|
||||||
|
}
|
||||||
|
entities.put(entity, Instant.now());
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Select the entities that have lived the longest and remove them
|
||||||
|
* to make room for new entities.
|
||||||
|
* @param amount the amount of entities to despawn.
|
||||||
|
*/
|
||||||
|
public void despawn(int amount) {
|
||||||
|
entities.entrySet()
|
||||||
|
.stream()
|
||||||
|
.sorted(Map.Entry.comparingByValue(Comparator.comparing(Instant::toEpochMilli)))
|
||||||
|
.limit(Math.max(0, amount))
|
||||||
|
.map(Map.Entry::getKey)
|
||||||
|
.forEach(Entity::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(List<MatchPlayer> players) {
|
||||||
|
// At least one player is required to spawn mobs
|
||||||
|
if(players.size() >= 1) {
|
||||||
|
Location start, end;
|
||||||
|
start = players.get(0).getLocation(); // player 1 is the first location
|
||||||
|
if(players.size() >= 2) {
|
||||||
|
end = players.get(1).getLocation(); // if player 2 exists, they are the second location
|
||||||
|
} else { // if no player 2, generate a random location near player 1
|
||||||
|
end = start.clone().add(Vector.getRandom().multiply(DISTANCE));
|
||||||
|
}
|
||||||
|
Optional<Location> location = location(start, end);
|
||||||
|
if(location.isPresent()) { // if the location is safe (on ground)
|
||||||
|
spawn(location.get(), true);
|
||||||
|
} else { // if the location was not safe, generate a simple midpoint location
|
||||||
|
spawn(start.position().midpoint(end.position()).toLocation(world), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int targets() {
|
||||||
|
return 2; // Always require 2 targets to generate a spawn location between them
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable() {
|
||||||
|
super.enable();
|
||||||
|
time = world.getTime();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Repeatable
|
||||||
|
public void tick() {
|
||||||
|
world.setTime(16000); // Night time to prevent flaming entities
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disable() {
|
||||||
|
world.setTime(time);
|
||||||
|
despawn(entities.size());
|
||||||
|
super.disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
package tc.oc.pgm.mutation.types.targetable;
|
||||||
|
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.entity.TNTPrimed;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import tc.oc.commons.core.collection.WeakHashSet;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.match.Repeatable;
|
||||||
|
import tc.oc.pgm.mutation.types.TargetMutation;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class BomberMutation extends TargetMutation {
|
||||||
|
|
||||||
|
final static Duration FREQUENCY = Duration.ofSeconds(30);
|
||||||
|
final static Range<Integer> TARGETS = Range.closed(1, 5);
|
||||||
|
final static Range<Integer> HEIGHT = Range.closed(30, 60);
|
||||||
|
final static Range<Integer> TICKS = Range.closed(10, 30);
|
||||||
|
|
||||||
|
final WeakHashSet<TNTPrimed> falling;
|
||||||
|
|
||||||
|
public BomberMutation(Match match) {
|
||||||
|
super(match, FREQUENCY);
|
||||||
|
this.falling = new WeakHashSet<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(List<MatchPlayer> players) {
|
||||||
|
players.forEach(player -> {
|
||||||
|
int bombs = entropy.randomInt(TARGETS);
|
||||||
|
int height = entropy.randomInt(HEIGHT);
|
||||||
|
Location location = player.getLocation().clone().add(0, height, 0);
|
||||||
|
for(int i = 0; i < bombs; i++) {
|
||||||
|
TNTPrimed tnt = world.spawn(location, TNTPrimed.class);
|
||||||
|
tnt.setGlowing(true);
|
||||||
|
tnt.setIsIncendiary(false);
|
||||||
|
tnt.setFuseTicks(Integer.MAX_VALUE);
|
||||||
|
tnt.setVelocity(
|
||||||
|
new Vector(
|
||||||
|
(random.nextBoolean() ? .5 : -.5) * entropy.randomDouble(),
|
||||||
|
-entropy.randomDouble(),
|
||||||
|
(random.nextBoolean() ? .5 : -.5) * entropy.randomDouble()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
falling.add(tnt);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int targets() {
|
||||||
|
return match.entropyForTick().randomInt(TARGETS);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Repeatable
|
||||||
|
public void tick() {
|
||||||
|
falling.stream()
|
||||||
|
.filter(TNTPrimed::isOnGround)
|
||||||
|
.forEach(tnt -> tnt.setFuseTicks(entropy.randomInt(TICKS)));
|
||||||
|
falling.removeIf(TNTPrimed::isOnGround);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package tc.oc.pgm.mutation.types.targetable;
|
||||||
|
|
||||||
|
import com.google.common.collect.Range;
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.World;
|
||||||
|
import org.bukkit.util.Vector;
|
||||||
|
import tc.oc.pgm.match.Match;
|
||||||
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
|
import tc.oc.pgm.mutation.types.TargetMutation;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class LightningMutation extends TargetMutation {
|
||||||
|
|
||||||
|
final static Duration FREQUENCY = Duration.ofSeconds(30);
|
||||||
|
final static Range<Integer> TARGETS = Range.closed(1, 5);
|
||||||
|
final static Range<Integer> STRIKES = Range.closed(0, 3);
|
||||||
|
|
||||||
|
public LightningMutation(Match match) {
|
||||||
|
super(match, FREQUENCY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute(List<MatchPlayer> players) {
|
||||||
|
players.forEach(player -> {
|
||||||
|
World world = match.getWorld();
|
||||||
|
Location location = player.getLocation();
|
||||||
|
world.strikeLightning(location.clone().add(Vector.getRandom()));
|
||||||
|
int strikes = match.entropyForTick().randomInt(STRIKES);
|
||||||
|
for(int i = 0; i < strikes; i++) {
|
||||||
|
world.strikeLightningEffect(location.clone().add(Vector.getRandom().multiply(Math.pow(i + 1, 2))));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int targets() {
|
||||||
|
return match.entropyForTick().randomInt(TARGETS);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -14,4 +14,8 @@ public class ShieldParameters {
|
||||||
this.maxHealth = maxHealth;
|
this.maxHealth = maxHealth;
|
||||||
this.rechargeDelay = rechargeDelay;
|
this.rechargeDelay = rechargeDelay;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ShieldParameters() {
|
||||||
|
this(DEFAULT_HEALTH, DEFAULT_DELAY);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,7 @@ import tc.oc.pgm.match.Competitor;
|
||||||
import tc.oc.pgm.match.MatchPlayer;
|
import tc.oc.pgm.match.MatchPlayer;
|
||||||
import tc.oc.pgm.match.ParticipantState;
|
import tc.oc.pgm.match.ParticipantState;
|
||||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
import tc.oc.pgm.mutation.MutationMatchModule;
|
||||||
import tc.oc.pgm.mutation.submodule.KitMutationModule;
|
import tc.oc.pgm.mutation.types.KitMutation;
|
||||||
import tc.oc.pgm.mutation.submodule.MutationModule;
|
|
||||||
import tc.oc.pgm.spawns.Spawn;
|
import tc.oc.pgm.spawns.Spawn;
|
||||||
import tc.oc.pgm.spawns.events.ParticipantDespawnEvent;
|
import tc.oc.pgm.spawns.events.ParticipantDespawnEvent;
|
||||||
import tc.oc.pgm.spawns.events.ParticipantReleaseEvent;
|
import tc.oc.pgm.spawns.events.ParticipantReleaseEvent;
|
||||||
|
@ -86,16 +85,10 @@ public class Alive extends Participating {
|
||||||
match.module(KillRewardMatchModule.class).ifPresent(krmm -> krmm.giveDeadPlayerRewards(player));
|
match.module(KillRewardMatchModule.class).ifPresent(krmm -> krmm.giveDeadPlayerRewards(player));
|
||||||
|
|
||||||
// Apply kit injections from KitMutationModules
|
// Apply kit injections from KitMutationModules
|
||||||
match.module(MutationMatchModule.class).ifPresent(mmm -> {
|
match.module(MutationMatchModule.class)
|
||||||
for(MutationModule module : mmm.getMutationModules()) {
|
.ifPresent(mmm -> mmm.mutationModules().stream()
|
||||||
if(module instanceof KitMutationModule) {
|
.filter(mm -> mm instanceof KitMutation)
|
||||||
KitMutationModule kitModule = ((KitMutationModule) module);
|
.forEach(mm -> ((KitMutation) mm).apply(player)));
|
||||||
for(Kit kit : kitModule.getKits()) {
|
|
||||||
player.facet(KitPlayerFacet.class).applyKit(kit, kitModule.isForceful());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
player.getBukkit().updateInventory();
|
player.getBukkit().updateInventory();
|
||||||
|
|
||||||
|
|
|
@ -1,21 +0,0 @@
|
||||||
package tc.oc.pgm.mutation;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.junit.runners.JUnit4;
|
|
||||||
import tc.oc.api.docs.virtual.MatchDoc;
|
|
||||||
|
|
||||||
@RunWith(JUnit4.class)
|
|
||||||
public class MutationTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testMutationEnumsAreSynced() {
|
|
||||||
assertTrue("Mutation enums must be equal in size", Mutation.values().length == MatchDoc.Mutation.values().length);
|
|
||||||
for(int i = 0; i < Mutation.values().length; i++) {
|
|
||||||
assertTrue("Mutation enums of ordinal (" + i + ") do not match", Mutation.values()[i].name().equals(MatchDoc.Mutation.values()[i].name()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -123,7 +123,7 @@ public class ItemBuilder<S extends ItemBuilder<?>> {
|
||||||
*/
|
*/
|
||||||
public S lore(String lore) {
|
public S lore(String lore) {
|
||||||
meta().setLore(meta().hasLore() ? ListUtils.append(meta().getLore(), lore)
|
meta().setLore(meta().hasLore() ? ListUtils.append(meta().getLore(), lore)
|
||||||
: Collections.singletonList(lore));
|
: Collections.singletonList(lore));
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -172,4 +172,14 @@ public class ItemBuilder<S extends ItemBuilder<?>> {
|
||||||
meta(SkullMeta.class).setOwner(name, uuid, skin);
|
meta(SkullMeta.class).setOwner(name, uuid, skin);
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public S shareable(boolean yes) {
|
||||||
|
new BooleanItemTag("prevent-sharing", false).set(stack, !yes);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
|
|
||||||
|
public S locked(boolean yes) {
|
||||||
|
new BooleanItemTag("locked", false).set(stack, !yes);
|
||||||
|
return self();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Stream;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
import com.google.common.collect.ImmutableSet;
|
||||||
|
@ -100,6 +101,24 @@ public class ItemUtils {
|
||||||
return isNothing(stack) ? Optional.empty() : Optional.of(stack);
|
return isNothing(stack) ? Optional.empty() : Optional.of(stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String[] TOOLS = {"axe", "hoe", "spade"};
|
||||||
|
|
||||||
|
public static boolean isTool(ItemStack stack) {
|
||||||
|
return isTool(stack.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isTool(MaterialData item) {
|
||||||
|
return Stream.of(TOOLS).anyMatch(query -> item.getItemType().name().toLowerCase().contains(query));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isArmor(ItemStack stack) {
|
||||||
|
return isArmor(stack.getData());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isArmor(MaterialData item) {
|
||||||
|
return !Bukkit.getItemFactory().getAttributeModifiers(item, Attribute.GENERIC_ARMOR).isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isWeapon(ItemStack stack) {
|
public static boolean isWeapon(ItemStack stack) {
|
||||||
return isWeapon(stack.getData());
|
return isWeapon(stack.getData());
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,91 @@
|
||||||
|
package tc.oc.commons.core.collection;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class ForwardingSet<E> implements Set<E> {
|
||||||
|
|
||||||
|
protected final Map<E, ?> backend;
|
||||||
|
|
||||||
|
public ForwardingSet(Map<E, ?> backend) {
|
||||||
|
this.backend = backend;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return backend.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return backend.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean contains(Object o) {
|
||||||
|
return backend.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<E> iterator() {
|
||||||
|
return backend.keySet().iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] toArray() {
|
||||||
|
return backend.keySet().toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T[] toArray(T[] a) {
|
||||||
|
return backend.keySet().toArray(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean add(E e) {
|
||||||
|
if(backend.containsKey(e)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
backend.put(e, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean remove(Object o) {
|
||||||
|
if(!backend.containsKey(o)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
backend.remove(o);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsAll(Collection<?> c) {
|
||||||
|
return c.stream().allMatch(this::contains);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean addAll(Collection<? extends E> c) {
|
||||||
|
return c.stream().anyMatch(this::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean retainAll(Collection<?> c) {
|
||||||
|
return c.stream().filter(i -> !contains(i)).anyMatch(this::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean removeAll(Collection<?> c) {
|
||||||
|
return c.stream().anyMatch(this::remove);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void clear() {
|
||||||
|
backend.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package tc.oc.commons.core.collection;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.WeakHashMap;
|
||||||
|
|
||||||
|
public class WeakHashSet<E> extends ForwardingSet<E> {
|
||||||
|
|
||||||
|
public WeakHashSet() {
|
||||||
|
super(new WeakHashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
public WeakHashSet(Collection<? extends E> initial) {
|
||||||
|
this();
|
||||||
|
this.addAll(initial);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,6 +3,7 @@ package tc.oc.commons.core.random;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.collect.Range;
|
import com.google.common.collect.Range;
|
||||||
import tc.oc.commons.core.util.Ranges;
|
import tc.oc.commons.core.util.Ranges;
|
||||||
|
|
||||||
|
@ -26,6 +27,10 @@ public interface Entropy {
|
||||||
return min + (int) ((randomLong() & 0xffffffffL) * delta / 0x100000000L);
|
return min + (int) ((randomLong() & 0xffffffffL) * delta / 0x100000000L);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
default <T> T randomElement(T... array) {
|
||||||
|
return randomElement(Lists.newArrayList(array));
|
||||||
|
}
|
||||||
|
|
||||||
default <T> T randomElement(Iterable<T> iterable) {
|
default <T> T randomElement(Iterable<T> iterable) {
|
||||||
return Iterables.get(iterable, randomInt(Range.closedOpen(0, Iterables.size(iterable))));
|
return Iterables.get(iterable, randomInt(Range.closedOpen(0, Iterables.size(iterable))));
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package tc.oc.commons.core.random;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import org.apache.commons.lang.math.Fraction;
|
||||||
|
|
||||||
public class RandomUtils {
|
public class RandomUtils {
|
||||||
|
|
||||||
|
@ -13,4 +14,8 @@ public class RandomUtils {
|
||||||
public static <T> T element(Random random, Iterable<? extends T> collection) {
|
public static <T> T element(Random random, Iterable<? extends T> collection) {
|
||||||
return Iterables.get(collection, safeNextInt(random, Iterables.size(collection)));
|
return Iterables.get(collection, safeNextInt(random, Iterables.size(collection)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean nextBoolean(Random random, Fraction chance) {
|
||||||
|
return random.nextDouble() < chance.doubleValue();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue