Add new mutations
This commit is contained in:
parent
b883ef5799
commit
ee06c979b4
|
@ -30,11 +30,7 @@ public interface MatchDoc extends Model {
|
|||
Collection<String> winning_team_ids();
|
||||
Collection<String> winning_user_ids();
|
||||
|
||||
enum Mutation {
|
||||
BLITZ, UHC, EXPLOSIVES, NO_FALL, MOBS, STRENGTH, DOUBLE_JUMP, INVISIBILITY, LIGHTNING, RAGE, ELYTRA;
|
||||
}
|
||||
|
||||
Set<Mutation> mutations();
|
||||
Set<String> mutations();
|
||||
|
||||
@Serialize
|
||||
interface Team extends MapDoc.Team, CompetitorDoc {
|
||||
|
|
|
@ -138,7 +138,7 @@ public interface ServerDoc {
|
|||
|
||||
@Serialize
|
||||
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>>(){})
|
||||
.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
|
||||
public Set<MatchDoc.Mutation> queued_mutations() {
|
||||
public Set<String> queued_mutations() {
|
||||
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.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.enabled = All mutations have already been enabled
|
||||
command.mutation.error.disabled = All mutations have already been disabled
|
||||
command.mutation.error.enabled = {0} mutation is already enabled
|
||||
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.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
|
||||
|
||||
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.uhc.desc = no natural regeneration
|
||||
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 = Rage
|
||||
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.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.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
|
||||
public Set<Mutation> mutations() {
|
||||
return mutations.map(mmm -> mmm.getHistoricalMutations()
|
||||
public Set<String> mutations() {
|
||||
return mutations.map(mmm -> mmm.mutationsHistorical()
|
||||
.stream()
|
||||
.map(tc.oc.pgm.mutation.Mutation::toApi)
|
||||
.map(tc.oc.pgm.mutation.Mutation::name)
|
||||
.collect(Collectors.toImmutableSet()))
|
||||
.orElse(ImmutableSet.of());
|
||||
}
|
||||
|
|
|
@ -65,7 +65,7 @@ public class BlitzMatchModule extends MatchModule implements Listener, JoinHandl
|
|||
|
||||
public BlitzMatchModule(Match match, BlitzConfig config) {
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package tc.oc.pgm.damage;
|
|||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.collect.ImmutableSetMultimap;
|
||||
import com.google.common.collect.SetMultimap;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.block.Block;
|
||||
|
@ -36,12 +37,12 @@ public class DisableDamageMatchModule extends MatchModule implements Listener {
|
|||
this.causes = causes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
if(MutationMatchModule.check(match, Mutation.NO_FALL)) {
|
||||
this.causes.putAll(DamageCause.FALL, Sets.newHashSet(PlayerRelation.values()));
|
||||
}
|
||||
public SetMultimap<DamageCause, PlayerRelation> causes() {
|
||||
return causes;
|
||||
}
|
||||
|
||||
public ImmutableSetMultimap<DamageCause, PlayerRelation> causesImmutable() {
|
||||
return ImmutableSetMultimap.copyOf(causes());
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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)
|
||||
|
|
|
@ -21,10 +21,23 @@ public class DoubleJumpKit extends Kit.Impl {
|
|||
this.rechargeInAir = rechargeInAir;
|
||||
}
|
||||
|
||||
public DoubleJumpKit() {
|
||||
this(true, DEFAULT_POWER, DEFAULT_RECHARGE, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||
DoubleJumpMatchModule djmm = player.getMatch().getMatchModule(DoubleJumpMatchModule.class);
|
||||
if(djmm != null) djmm.setKit(player.getBukkit(), this);
|
||||
player.getMatch().module(DoubleJumpMatchModule.class).ifPresent(jump -> jump.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() {
|
||||
|
|
|
@ -57,7 +57,7 @@ public class ProjectileTrailMatchModule extends MatchModule implements Listener
|
|||
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) {
|
||||
match.player(event.getActor()).ifPresent(shooter -> {
|
||||
final Projectile projectile = event.getEntity();
|
||||
|
|
|
@ -15,7 +15,7 @@ public class MatchMutationFilter extends TypedFilter.Impl<IMatchQuery> {
|
|||
@Override
|
||||
public boolean matches(IMatchQuery query) {
|
||||
return query.module(MutationMatchModule.class)
|
||||
.filter(mmm -> mmm.getActiveMutations().contains(mutation))
|
||||
.filter(mmm -> mmm.enabled(mutation))
|
||||
.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.MatchModule;
|
||||
import tc.oc.pgm.mutation.Mutation;
|
||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
||||
import tc.oc.pgm.match.Repeatable;
|
||||
import tc.oc.time.Time;
|
||||
|
||||
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);
|
||||
this.gameRules = Preconditions.checkNotNull(gameRules, "gamerules");
|
||||
if(MutationMatchModule.check(match, Mutation.UHC)) {
|
||||
this.gameRules.put(GameRule.NATURAL_REGENERATION, Boolean.FALSE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (Map.Entry<GameRule, Boolean> gameRule : this.gameRules.entrySet()) {
|
||||
this.match.getWorld().setGameRuleValue(gameRule.getKey().getValue(), gameRule.getValue().toString());
|
||||
}
|
||||
update();
|
||||
}
|
||||
|
||||
public ImmutableMap<GameRule, Boolean> getGameRules() {
|
||||
return ImmutableMap.copyOf(gameRules);
|
||||
@Repeatable(interval = @Time(seconds = 1))
|
||||
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)
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -33,32 +33,32 @@ public class GameRulesModule implements MapModule, MatchModuleFactory<GameRulesM
|
|||
// ---------------------
|
||||
|
||||
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 gameRuleElement : gameRulesElement.getChildren()) {
|
||||
GameRule gameRule = GameRule.forName(gameRuleElement.getName());
|
||||
String gameRule = gameRuleElement.getName();
|
||||
String value = gameRuleElement.getValue();
|
||||
|
||||
if (gameRule == null) {
|
||||
if(gameRule == null) {
|
||||
throw new InvalidXMLException(gameRuleElement.getName() + " is not a valid gamerule", gameRuleElement);
|
||||
}
|
||||
if (value == null) {
|
||||
throw new InvalidXMLException("Missing value for gamerule " + gameRule.getValue(), gameRuleElement);
|
||||
if(value == null) {
|
||||
throw new InvalidXMLException("Missing value for gamerule " + gameRule, gameRuleElement);
|
||||
} else if (!(value.equalsIgnoreCase("true") || value.equalsIgnoreCase("false"))) {
|
||||
throw new InvalidXMLException(gameRuleElement.getValue() + " is not a valid gamerule value", gameRuleElement);
|
||||
}
|
||||
if (gameRules.containsKey(gameRule)){
|
||||
throw new InvalidXMLException(gameRule.getValue() + " has already been specified", gameRuleElement);
|
||||
if(gameRules.containsKey(gameRule)){
|
||||
throw new InvalidXMLException(gameRule + " has already been specified", gameRuleElement);
|
||||
}
|
||||
|
||||
gameRules.put(gameRule, Boolean.valueOf(value));
|
||||
gameRules.put(gameRule, value);
|
||||
}
|
||||
}
|
||||
return new GameRulesModule(gameRules);
|
||||
}
|
||||
|
||||
public ImmutableMap<GameRule, Boolean> getGameRules() {
|
||||
public ImmutableMap<String, String> getGameRules() {
|
||||
return ImmutableMap.copyOf(this.gameRules);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@ package tc.oc.pgm.killreward;
|
|||
import com.google.common.collect.ImmutableList;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
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;
|
||||
|
||||
public class KillReward {
|
||||
|
@ -15,4 +17,8 @@ public class KillReward {
|
|||
this.filter = filter;
|
||||
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.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Multimap;
|
||||
import org.bukkit.event.Event;
|
||||
|
@ -27,34 +26,38 @@ import tc.oc.pgm.tracker.damage.DamageInfo;
|
|||
|
||||
@ListenerScope(MatchScope.RUNNING)
|
||||
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) {
|
||||
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);
|
||||
return Collections2.filter(killRewards, new Predicate<KillReward>() {
|
||||
@Override
|
||||
public boolean apply(KillReward killReward) {
|
||||
return killReward.filter.query(query).isAllowed();
|
||||
}
|
||||
});
|
||||
return rewardsImmutable().stream().filter(reward -> reward.filter.query(query).isAllowed()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private Collection<KillReward> getRewards(MatchPlayerDeathEvent event) {
|
||||
return getRewards(event, event.getVictim().getParticipantState(), event.getDamageInfo());
|
||||
public List<KillReward> rewards(MatchPlayerDeathEvent event) {
|
||||
return rewards(event, event.getVictim().getParticipantState(), event.getDamageInfo());
|
||||
}
|
||||
|
||||
private void giveRewards(MatchPlayer killer, Collection<KillReward> rewards) {
|
||||
for(KillReward reward : rewards) {
|
||||
public void giveRewards(MatchPlayer killer, Collection<KillReward> rewards) {
|
||||
rewards.forEach(reward -> {
|
||||
// Apply kit first so it can not override reward items
|
||||
reward.kit.apply(killer);
|
||||
reward.items.forEach(stack -> ItemKitApplicator.fireEventAndTransfer(killer, stack));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
|
||||
|
@ -63,7 +66,7 @@ public class KillRewardMatchModule extends MatchModule implements Listener {
|
|||
MatchPlayer killer = event.getOnlineKiller();
|
||||
if(killer == null) return;
|
||||
|
||||
Collection<KillReward> rewards = getRewards(event);
|
||||
List<KillReward> rewards = rewards(event);
|
||||
|
||||
if(killer.isDead()) {
|
||||
// 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) {
|
||||
deadPlayerRewards.removeAll(event.getPlayer());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package tc.oc.pgm.killreward;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.logging.Logger;
|
||||
|
@ -27,10 +28,10 @@ import tc.oc.pgm.xml.InvalidXMLException;
|
|||
|
||||
@ModuleDescription(name="Kill Reward")
|
||||
public class KillRewardModule implements MapModule, MatchModuleFactory<KillRewardMatchModule> {
|
||||
protected final ImmutableList<KillReward> rewards;
|
||||
protected final List<KillReward> rewards;
|
||||
|
||||
public KillRewardModule(List<KillReward> rewards) {
|
||||
this.rewards = ImmutableList.copyOf(rewards);
|
||||
this.rewards = rewards;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -43,7 +44,7 @@ public class KillRewardModule implements MapModule, MatchModuleFactory<KillRewar
|
|||
// ---------------------
|
||||
|
||||
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 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));
|
||||
}
|
||||
|
||||
ImmutableList<KillReward> list = rewards.build();
|
||||
if(list.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return new KillRewardModule(list);
|
||||
}
|
||||
return new KillRewardModule(rewards);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
package tc.oc.pgm.kits;
|
||||
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.PlayerInventory;
|
||||
import tc.oc.commons.bukkit.inventory.Slot;
|
||||
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 {
|
||||
|
||||
protected final ItemStack item;
|
||||
|
@ -20,4 +27,36 @@ public class FreeItemKit extends BaseItemKit {
|
|||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||
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;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import tc.oc.pgm.match.MatchPlayer;
|
||||
|
||||
public class HealthKit extends Kit.Impl {
|
||||
protected final 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,12 +11,27 @@ public class NaturalRegenerationKit extends Kit.Impl {
|
|||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(MatchPlayer player, boolean force, ItemKitApplicator items) {
|
||||
public void toggle(MatchPlayer player, boolean enabled) {
|
||||
if(fast) {
|
||||
player.getBukkit().setFastNaturalRegeneration(enabled);
|
||||
} else {
|
||||
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) {
|
||||
items.put(slot, item, force);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -164,11 +164,11 @@ public class MatchAnnouncer implements PluginFacet, Listener {
|
|||
}
|
||||
|
||||
final MutationMatchModule mmm = viewer.getMatch().getMatchModule(MutationMatchModule.class);
|
||||
if(mmm != null && mmm.getActiveMutations().size() > 0) {
|
||||
if(mmm != null && mmm.mutationsActive().size() > 0) {
|
||||
viewer.sendMessage(
|
||||
new Component(" ", ChatColor.DARK_GRAY).extra(
|
||||
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.MatchEndEvent;
|
||||
import tc.oc.pgm.events.MatchLoadEvent;
|
||||
import tc.oc.pgm.gamerules.GameRule;
|
||||
import tc.oc.pgm.gamerules.GameRulesModule;
|
||||
import tc.oc.pgm.match.MatchManager;
|
||||
import tc.oc.pgm.modules.TimeLockModule;
|
||||
|
@ -99,9 +98,11 @@ public class PGMListener implements PluginFacet, Listener {
|
|||
// Time Lock
|
||||
// lock time before, during (if time lock enabled), and after the match
|
||||
//
|
||||
static final String DO_DAYLIGHT_CYCLE = "doDaylightCycle";
|
||||
|
||||
@EventHandler
|
||||
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
|
||||
|
@ -113,16 +114,16 @@ public class PGMListener implements PluginFacet, Listener {
|
|||
|
||||
GameRulesModule gameRulesModule = event.getMatch().getModuleContext().getModule(GameRulesModule.class);
|
||||
|
||||
if (gameRulesModule != null && gameRulesModule.getGameRules().containsKey(GameRule.DO_DAYLIGHT_CYCLE)) {
|
||||
unlockTime = gameRulesModule.getGameRules().get(GameRule.DO_DAYLIGHT_CYCLE);
|
||||
if (gameRulesModule != null && gameRulesModule.getGameRules().containsKey(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
|
||||
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
|
||||
|
|
|
@ -4,22 +4,21 @@ import org.bukkit.event.EventHandler;
|
|||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import tc.oc.pgm.events.ListenerScope;
|
||||
import tc.oc.pgm.filters.matcher.StaticFilter;
|
||||
import tc.oc.pgm.match.Match;
|
||||
import tc.oc.pgm.filters.Filter;
|
||||
import tc.oc.pgm.filters.query.EntitySpawnQuery;
|
||||
import tc.oc.pgm.match.MatchModule;
|
||||
import tc.oc.pgm.match.MatchScope;
|
||||
import tc.oc.pgm.mutation.Mutation;
|
||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
||||
|
||||
@ListenerScope(MatchScope.LOADED)
|
||||
public class MobsMatchModule extends MatchModule implements Listener {
|
||||
|
||||
private final Filter mobsFilter;
|
||||
|
||||
public MobsMatchModule(Match match, Filter mobsFilter) {
|
||||
super(match);
|
||||
this.mobsFilter = MutationMatchModule.check(match, Mutation.MOBS) ? StaticFilter.ALLOW : mobsFilter;
|
||||
this.mobsFilter = mobsFilter;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -40,8 +39,10 @@ public class MobsMatchModule extends MatchModule implements Listener {
|
|||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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.HoverEvent;
|
||||
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.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 {
|
||||
|
||||
BLITZ (null, false),
|
||||
UHC (null, false),
|
||||
EXPLOSIVES (Explosives.class, true),
|
||||
NO_FALL (null, false),
|
||||
MOBS (null, false),
|
||||
STRENGTH (Strength.class, true),
|
||||
DOUBLE_JUMP (DoubleJump.class, true),
|
||||
INVISIBILITY(Invisibility.class, true),
|
||||
LIGHTNING (Lightning.class, true),
|
||||
RAGE (Rage.class, true),
|
||||
ELYTRA (Elytra.class, true);
|
||||
BLITZ (null),
|
||||
RAGE (RageMutation.class),
|
||||
HARDCORE (HardcoreMutation.class),
|
||||
JUMP (JumpMutation.class),
|
||||
EXPLOSIVE (ExplosiveMutation.class),
|
||||
ELYTRA (ElytraMutation.class),
|
||||
PROJECTILE (ProjectileMutation.class),
|
||||
ENCHANTMENT(EnchantmentMutation.class),
|
||||
POTION (PotionMutation.class),
|
||||
EQUESTRIAN (EquestrianMutation.class),
|
||||
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 DESCRIPTION_KEY = ".desc";
|
||||
|
||||
/**
|
||||
* The module class that handles this mutation.
|
||||
*/
|
||||
private final @Nullable Class<? extends MutationModule> clazz;
|
||||
private final @Nullable Class<? extends MutationModule> loader;
|
||||
|
||||
/**
|
||||
* Whether this mutation be changed during a match.
|
||||
*/
|
||||
private final boolean change;
|
||||
|
||||
Mutation(@Nullable Class<? extends MutationModule> clazz, boolean change) {
|
||||
this.clazz = clazz;
|
||||
this.change = change;
|
||||
Mutation(@Nullable Class<? extends MutationModule> loader) {
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
public static Mutation fromApi(MatchDoc.Mutation mutation) {
|
||||
return values()[mutation.ordinal()];
|
||||
}
|
||||
|
||||
public MatchDoc.Mutation toApi() {
|
||||
return MatchDoc.Mutation.values()[ordinal()];
|
||||
}
|
||||
|
||||
public Class<? extends MutationModule> getModuleClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public boolean isChangeable() {
|
||||
return change;
|
||||
public Class<? extends MutationModule> loader() {
|
||||
return loader;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
@ -76,4 +79,14 @@ public enum Mutation {
|
|||
public static Function<Mutation, BaseComponent> toComponent(final ChatColor 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;
|
||||
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.bukkit.event.entity.CreatureSpawnEvent;
|
||||
import tc.oc.commons.core.util.MapUtils;
|
||||
import tc.oc.commons.core.random.RandomUtils;
|
||||
import tc.oc.pgm.Config;
|
||||
import tc.oc.pgm.match.*;
|
||||
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 java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Stream;
|
||||
import javax.inject.Inject;
|
||||
|
||||
public class MutationMatchModule extends MatchModule {
|
||||
// TODO: send remote mutation alerts via an AMQP message
|
||||
|
||||
/**
|
||||
* Chance that a mutation event will occur.
|
||||
|
@ -63,39 +63,36 @@ public class MutationMatchModule extends MatchModule {
|
|||
this.chance = options.chance;
|
||||
this.amount = options.amount;
|
||||
this.weightedSelector = new ImmutableWeightedRandomChooser<>(options.weights);
|
||||
this.mutations = getDefaultMutations();
|
||||
this.mutations = mutationsDefault();
|
||||
this.history = new HashSet<>();
|
||||
this.modules = new HashMap<>();
|
||||
}
|
||||
|
||||
public final ImmutableMap<Mutation, Boolean> getMutations() {
|
||||
public final ImmutableMap<Mutation, Boolean> mutations() {
|
||||
return ImmutableMap.copyOf(mutations);
|
||||
}
|
||||
|
||||
public final ImmutableSet<Mutation> getActiveMutations() {
|
||||
return ImmutableSet.copyOf(Collections2.filter(getMutations().keySet(), new Predicate<Mutation>() {
|
||||
@Override
|
||||
public boolean apply(Mutation mutation) {
|
||||
return mutations.get(mutation);
|
||||
}
|
||||
}));
|
||||
public final ImmutableSet<Mutation> mutationsActive() {
|
||||
return ImmutableSet.copyOf(Collections2.filter(mutations().keySet(), mutations::get));
|
||||
}
|
||||
|
||||
public final ImmutableSet<Mutation> getHistoricalMutations() {
|
||||
public final ImmutableSet<Mutation> mutationsHistorical() {
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldLoad() {
|
||||
return Config.Mutations.enabled();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
if(!Config.Mutations.enabled()) return;
|
||||
Random random = match.getRandom();
|
||||
// Check if the api has any queued mutations
|
||||
Collection<Mutation> queuedMutations = mutationQueue.mutations();
|
||||
|
@ -113,7 +110,7 @@ public class MutationMatchModule extends MatchModule {
|
|||
mutationQueue.clear();
|
||||
}
|
||||
// Load the mutation modules for this match
|
||||
for(Mutation mutation : getActiveMutations()) {
|
||||
for(Mutation mutation : mutationsActive()) {
|
||||
try {
|
||||
mutate(mutation);
|
||||
} catch (Throwable throwable) {
|
||||
|
@ -124,43 +121,53 @@ public class MutationMatchModule extends MatchModule {
|
|||
|
||||
@Override
|
||||
public void enable() {
|
||||
for(MutationModule module : modules.values()) {
|
||||
module.enable(match.isRunning());
|
||||
}
|
||||
modules.values().forEach(MutationModule::enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disable() {
|
||||
for(MutationModule module : modules.values()) {
|
||||
module.disable(match.isRunning());
|
||||
}
|
||||
modules.values().forEach(MutationModule::disable);
|
||||
}
|
||||
|
||||
public void register(Mutation mutation, boolean load) {
|
||||
mutations.put(mutation, load);
|
||||
}
|
||||
|
||||
public void mutate(Mutation mutation) throws Throwable {
|
||||
Class<? extends MutationModule> clazz = mutation.getModuleClass();
|
||||
if(clazz == null || (match.isRunning() && !mutation.isChangeable())) return;
|
||||
MutationModule module = modules.containsKey(clazz) ? modules.get(clazz) : mutation.getModuleClass().getDeclaredConstructor(Match.class).newInstance(match);
|
||||
Class<? extends MutationModule> loader = mutation.loader();
|
||||
if(loader == null) return;
|
||||
MutationModule module = modules.containsKey(loader) ? modules.get(loader) : loader.getDeclaredConstructor(Match.class).newInstance(match);
|
||||
if(mutations.get(mutation)) {
|
||||
module.enable(match.isRunning());
|
||||
modules.put(clazz, module);
|
||||
module.enable();
|
||||
modules.put(loader, module);
|
||||
mutations.put(mutation, true);
|
||||
history.add(mutation);
|
||||
} else {
|
||||
module.disable(match.isRunning());
|
||||
modules.remove(clazz);
|
||||
module.disable();
|
||||
modules.remove(loader);
|
||||
mutations.put(mutation, false);
|
||||
}
|
||||
}
|
||||
|
||||
private Map<Mutation, Boolean> getDefaultMutations() {
|
||||
Map<Mutation, Boolean> defaults = new HashMap<>();
|
||||
MapUtils.putAll(defaults, Sets.newHashSet(Mutation.values()), false);
|
||||
return defaults;
|
||||
public boolean enabled() {
|
||||
return !mutationsActive().isEmpty();
|
||||
}
|
||||
|
||||
public static boolean check(Match match, Mutation mutation) {
|
||||
return Config.Mutations.enabled() &&
|
||||
match.hasMatchModule(MutationMatchModule.class) &&
|
||||
match.getMatchModule(MutationMatchModule.class).getActiveMutations().contains(mutation);
|
||||
public boolean enabled(Mutation... mutations) {
|
||||
return mutationsActive().stream().anyMatch(m1 -> Stream.of(mutations).anyMatch(m2 -> m2.equals(m1)));
|
||||
}
|
||||
|
||||
public boolean allowMob(CreatureSpawnEvent.SpawnReason reason) {
|
||||
switch(reason) {
|
||||
case NATURAL:
|
||||
case DEFAULT:
|
||||
case CHUNK_GEN:
|
||||
case JOCKEY:
|
||||
case MOUNT:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -6,7 +6,6 @@ import java.util.HashSet;
|
|||
import java.util.stream.Collectors;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import tc.oc.api.docs.Server;
|
||||
import tc.oc.api.docs.virtual.ServerDoc;
|
||||
|
@ -28,12 +27,12 @@ public class MutationQueue {
|
|||
.getLocalServer()
|
||||
.queued_mutations()
|
||||
.stream()
|
||||
.map(mutation -> Mutation.values()[mutation.ordinal()])
|
||||
.flatMap(Mutation::fromString)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public ListenableFuture<Server> clear() {
|
||||
return force(Collections.<Mutation>emptyList());
|
||||
return force(Collections.emptyList());
|
||||
}
|
||||
|
||||
public ListenableFuture<Server> removeAll(final Collection<Mutation> mutations) {
|
||||
|
@ -53,6 +52,7 @@ public class MutationQueue {
|
|||
}
|
||||
|
||||
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.HashSet;
|
||||
import java.util.logging.Level;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import com.google.common.collect.Collections2;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.sk89q.minecraft.util.commands.Command;
|
||||
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 com.sk89q.minecraft.util.commands.*;
|
||||
import net.md_5.bungee.api.ChatColor;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.chat.TranslatableComponent;
|
||||
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.commons.bukkit.chat.Audiences;
|
||||
import tc.oc.commons.bukkit.chat.ListComponent;
|
||||
|
@ -56,39 +57,42 @@ public class MutationCommands implements NestedCommands {
|
|||
private final SyncExecutor syncExecutor;
|
||||
private final Audiences audiences;
|
||||
private final MutationQueue mutationQueue;
|
||||
private final IdentityProvider identityProvider;
|
||||
|
||||
@Inject
|
||||
MutationCommands(SyncExecutor syncExecutor, Audiences audiences, MutationQueue mutationQueue) {
|
||||
@Inject MutationCommands(SyncExecutor syncExecutor, Audiences audiences, MutationQueue mutationQueue, IdentityProvider identityProvider) {
|
||||
this.syncExecutor = syncExecutor;
|
||||
this.audiences = audiences;
|
||||
this.mutationQueue = mutationQueue;
|
||||
this.identityProvider = identityProvider;
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = {"enable", "add"},
|
||||
aliases = {"enable", "e"},
|
||||
desc = "Adds a mutation to the upcoming match." +
|
||||
"You can use '?' as a wildcard or " +
|
||||
"'*' to use all.",
|
||||
usage = "<mutation|?|*>",
|
||||
flags = "q",
|
||||
min = 1,
|
||||
max = 1
|
||||
)
|
||||
@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);
|
||||
}
|
||||
|
||||
@Command(
|
||||
aliases = {"disable", "remove"},
|
||||
aliases = {"disable", "d"},
|
||||
desc = "Remove a mutation to the upcoming match." +
|
||||
"You can use '?' as a wildcard or " +
|
||||
"'*' to use all.",
|
||||
usage = "<mutation|?|*>",
|
||||
flags = "q",
|
||||
min = 1,
|
||||
max = 1
|
||||
)
|
||||
@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);
|
||||
}
|
||||
|
||||
|
@ -105,8 +109,8 @@ public class MutationCommands implements NestedCommands {
|
|||
public void list(final CommandContext args, CommandSender sender) throws CommandException {
|
||||
MutationMatchModule module = verify(sender);
|
||||
final boolean queued = args.hasFlag('q');
|
||||
final Collection<Mutation> active = queued ? mutationQueue.mutations() : module.getActiveMutations();
|
||||
new Paginator<Mutation>() {
|
||||
final Collection<Mutation> active = queued ? mutationQueue.mutations() : module.mutationsActive();
|
||||
new Paginator<Mutation>(Mutation.values().length / 2) {
|
||||
@Override
|
||||
protected BaseComponent title() {
|
||||
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);
|
||||
}
|
||||
|
||||
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 Match match = module.getMatch();
|
||||
String action = args.getString(0);
|
||||
boolean queued = args.hasFlag('q') || match.isFinished();
|
||||
// Mutations that *will* be added or removed
|
||||
final Collection<Mutation> mutations = new HashSet<>();
|
||||
// Mutations that *are allowed* to be added or removed
|
||||
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);
|
||||
// Check if all mutations have been enabled/disabled
|
||||
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
|
||||
switch (action) {
|
||||
|
@ -145,27 +155,50 @@ public class MutationCommands implements NestedCommands {
|
|||
case "?": mutations.add(Iterables.get(availableMutations, RandomUtils.safeNextInt(match.getRandom(), availableMutations.size()))); break;
|
||||
default:
|
||||
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));
|
||||
} 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 {
|
||||
mutations.add(query);
|
||||
}
|
||||
}
|
||||
|
||||
// Send the queued changes off to the api
|
||||
syncExecutor.callback(
|
||||
value ? mutationQueue.mergeAll(mutations)
|
||||
: mutationQueue.removeAll(mutations),
|
||||
result -> {
|
||||
audiences.get(sender).sendMessage(new Component(new TranslatableComponent(
|
||||
message(false, value, mutations.size() == 1),
|
||||
new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA)))
|
||||
), ChatColor.WHITE));
|
||||
Audience origin = audiences.get(sender);
|
||||
Audience all = audiences.localServer();
|
||||
String message = message(!queued, value, mutations.size() == 1);
|
||||
ListComponent changed = new ListComponent(Collections2.transform(mutations, Mutation.toComponent(ChatColor.AQUA)));
|
||||
if(queued) {
|
||||
// Send the queued changes off to the api
|
||||
syncExecutor.callback(
|
||||
value ? mutationQueue.mergeAll(mutations)
|
||||
: mutationQueue.removeAll(mutations),
|
||||
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) {
|
||||
if(now) {
|
||||
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.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.ParticipantState;
|
||||
import tc.oc.pgm.mutation.MutationMatchModule;
|
||||
import tc.oc.pgm.mutation.submodule.KitMutationModule;
|
||||
import tc.oc.pgm.mutation.submodule.MutationModule;
|
||||
import tc.oc.pgm.mutation.types.KitMutation;
|
||||
import tc.oc.pgm.spawns.Spawn;
|
||||
import tc.oc.pgm.spawns.events.ParticipantDespawnEvent;
|
||||
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));
|
||||
|
||||
// Apply kit injections from KitMutationModules
|
||||
match.module(MutationMatchModule.class).ifPresent(mmm -> {
|
||||
for(MutationModule module : mmm.getMutationModules()) {
|
||||
if(module instanceof KitMutationModule) {
|
||||
KitMutationModule kitModule = ((KitMutationModule) module);
|
||||
for(Kit kit : kitModule.getKits()) {
|
||||
player.facet(KitPlayerFacet.class).applyKit(kit, kitModule.isForceful());
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
match.module(MutationMatchModule.class)
|
||||
.ifPresent(mmm -> mmm.mutationModules().stream()
|
||||
.filter(mm -> mm instanceof KitMutation)
|
||||
.forEach(mm -> ((KitMutation) mm).apply(player)));
|
||||
|
||||
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) {
|
||||
meta().setLore(meta().hasLore() ? ListUtils.append(meta().getLore(), lore)
|
||||
: Collections.singletonList(lore));
|
||||
: Collections.singletonList(lore));
|
||||
return self();
|
||||
}
|
||||
|
||||
|
@ -172,4 +172,14 @@ public class ItemBuilder<S extends ItemBuilder<?>> {
|
|||
meta(SkullMeta.class).setOwner(name, uuid, skin);
|
||||
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.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
@ -100,6 +101,24 @@ public class ItemUtils {
|
|||
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) {
|
||||
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 com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Range;
|
||||
import tc.oc.commons.core.util.Ranges;
|
||||
|
||||
|
@ -26,6 +27,10 @@ public interface Entropy {
|
|||
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) {
|
||||
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 com.google.common.collect.Iterables;
|
||||
import org.apache.commons.lang.math.Fraction;
|
||||
|
||||
public class RandomUtils {
|
||||
|
||||
|
@ -13,4 +14,8 @@ public class RandomUtils {
|
|||
public static <T> T element(Random random, Iterable<? extends T> 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