ProjectAres/PGM/src/main/java/tc/oc/pgm/ghostsquadron/GhostSquadronMatchModule.java

409 lines
16 KiB
Java

package tc.oc.pgm.ghostsquadron;
import static com.google.common.base.Preconditions.checkNotNull;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
import org.bukkit.*;
import org.bukkit.block.BlockFace;
import org.bukkit.entity.*;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.*;
import org.bukkit.event.entity.EntityDamageEvent.DamageCause;
import org.bukkit.event.player.*;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitTask;
import tc.oc.api.docs.PlayerId;
import tc.oc.api.docs.UserId;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.match.Competitor;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.PGMTranslations;
import tc.oc.pgm.classes.ClassMatchModule;
import tc.oc.pgm.classes.PlayerClass;
import tc.oc.pgm.ghostsquadron.RevealTask.RevealEntry;
import tc.oc.pgm.match.MatchModule;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.utils.MatchPlayers;
@ListenerScope(MatchScope.RUNNING)
public class GhostSquadronMatchModule extends MatchModule implements Listener {
BukkitTask mainTask;
BukkitTask revealTask;
final ClassMatchModule classMatchModule;
public final Map<Location, PlayerId> landmines = Maps.newHashMap();
public final Map<Location, Competitor> landmineTeams = Maps.newHashMap();
public final Map<UserId, Date> spideySenses = Maps.newHashMap();
final Map<Player, Double> walkDistance = Maps.newHashMap();
public final Map<Player, RevealEntry> revealMap = Maps.newHashMap();
// classes
final Optional<PlayerClass> trackerClass;
final Optional<PlayerClass> spiderClass;
final Optional<PlayerClass> leprechaunClass;
final Optional<PlayerClass> demoClass;
// final PlayerClass ninjaClass;
public GhostSquadronMatchModule(Match match, ClassMatchModule classMatchModule) {
super(match);
this.classMatchModule = checkNotNull(classMatchModule, "class match module");
this.trackerClass = classMatchModule.findClass("Tracker");
this.spiderClass = classMatchModule.findClass("Spider");
this.leprechaunClass = classMatchModule.findClass("Leprechaun");
this.demoClass = classMatchModule.findClass("Demo");
// this.ninjaClass = checkNotNull(classMatchModule.getPlayerClass("Ninja"), "Ninja class not found");
}
@Override
public void enable() {
GhostSquadronTask task = new GhostSquadronTask(this.match, this, this.classMatchModule);
this.mainTask = Bukkit.getScheduler().runTaskTimer(this.match.getPlugin(), task, 0, 10);
this.revealTask = Bukkit.getScheduler().runTaskTimer(this.match.getPlugin(), new RevealTask(this), 0, 1);
}
@Override
public void disable() {
this.mainTask.cancel();
this.revealTask.cancel();
}
/*
* GENERAL
*/
@EventHandler
public void cancelDrop(final PlayerDropItemEvent event) {
Material hand = event.getPlayer().getItemInHand().getType();
if(!GhostSquadron.ALLOWED_DROPS.contains(hand)) {
MatchPlayer player = this.match.getPlayer(event.getPlayer());
if(MatchPlayers.canInteract(player)) {
event.setCancelled(true);
}
}
}
@EventHandler
public void cancelItemSpawn(final ItemSpawnEvent event) {
Material hand = event.getEntity().getItemStack().getType();
if(!GhostSquadron.ALLOWED_DROPS.contains(hand)) {
event.setCancelled(true);
}
}
@EventHandler
public void cancelPickup(final PlayerPickupItemEvent event) {
event.setCancelled(true);
}
@EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true)
public void noExplosionBlockDamage(EntityExplodeEvent event) {
event.blockList().clear();
}
@EventHandler(priority = EventPriority.MONITOR)
public void enforceFireTickLimit(EntityDamageEvent event) {
event.getEntity().setFireTicks(Math.min(event.getEntity().getFireTicks(), GhostSquadron.MAX_FIRE_TICKS));
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void resetRevealTicks(PlayerDeathEvent event) {
this.revealMap.remove(event.getEntity());
}
private void reveal(Player player) {
this.reveal(player, GhostSquadron.REVEAL_STANDARD_DURATION);
}
private void reveal(Player player, int ticks) {
RevealEntry entry = this.revealMap.get(player);
if(entry == null) entry = new RevealEntry();
entry.revealTicks = ticks;
for(PotionEffect e : player.getActivePotionEffects()) {
if(e.getType().equals(PotionEffectType.INVISIBILITY)) {
entry.potionTicks = e.getDuration();
}
}
player.removePotionEffect(PotionEffectType.INVISIBILITY);
this.revealMap.put(player, entry);
}
/*
* ARCHER
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void revealOnArrow(final EntityDamageByEntityEvent event) {
if(event.getCause() == DamageCause.PROJECTILE && event.getDamager() instanceof Arrow && event.getEntity() instanceof Player) {
this.reveal((Player) event.getEntity(), GhostSquadron.ARROW_REVEAL_DURATION);
}
}
/*
* TRACKER
*/
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void trackerMove(final PlayerMoveEvent event) {
MatchPlayer enemy = this.getMatch().getPlayer(event.getPlayer());
if(!MatchPlayers.canInteract(enemy)) return;
ImmutableList.builder();
double distance = event.getFrom().distance(event.getTo());
final Double walkedRaw = this.walkDistance.get(event.getPlayer());
final double walkedStart = walkedRaw != null ? walkedRaw.doubleValue() : 0;
final int stepStart = (int) Math.floor(walkedStart / GhostSquadron.TRACKER_FOOTSTEP_SPACING);
final double walkedEnd = walkedStart + distance;
final int stepEnd = (int) Math.floor(walkedEnd / GhostSquadron.TRACKER_FOOTSTEP_SPACING);
this.walkDistance.put(event.getPlayer(), walkedEnd);
Location normal = event.getTo().clone().subtract(event.getFrom());
normal.multiply(1.0 / normal.length());
for(int step = stepStart; step < stepEnd; step++) {
double distanceFromStart = (step + 1) * GhostSquadron.TRACKER_FOOTSTEP_SPACING - walkedStart;
Location stepLoc = normal.clone().multiply(distanceFromStart).add(event.getFrom()).add(0, GhostSquadron.TRACKER_FOOTSTEP_DY, 0);
for(UserId userId : this.classMatchModule.getClassMembers(this.trackerClass)) {
MatchPlayer tracker = this.match.getPlayer(userId);
if(MatchPlayers.canInteract(tracker) && tracker.getParty() != enemy.getParty()) {
tracker.getBukkit().playEffect(stepLoc, Effect.FOOTSTEP, 0, 0, 0f, 0f, 0f, 0f, 1, 50);
}
}
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void clearWalkDistanceOnDeath(PlayerDeathEvent event) {
// players are killed when leaving team or quitting, so this covers every instance
this.walkDistance.remove(event.getEntity());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void trackerMelee(final EntityDamageByEntityEvent event) {
if(event.getDamager() instanceof Player && event.getEntity() instanceof Player) {
MatchPlayer damager = this.getMatch().getPlayer((Player) event.getDamager());
MatchPlayer damaged = this.getMatch().getPlayer((Player) event.getEntity());
if(damager != null && damaged != null && this.isClass(damager, this.trackerClass) && damager.getParty() != damaged.getParty()) {
ItemStack hand = damager.getBukkit().getItemInHand();
if(hand != null && hand.getType() == Material.COMPASS) {
this.reveal(damaged.getBukkit(), GhostSquadron.TRACKER_REVEAL_DURATION);
}
}
}
}
/*
* LEPRECHAUN
*/
@EventHandler
public void dontPickupExp(final PlayerPickupExperienceEvent event) {
event.setCancelled(true);
}
@EventHandler
public void fastLiquids(final PlayerMoveEvent event) {
final MatchPlayer player = this.getMatch().getParticipant(event.getPlayer());
if(player != null && this.isClass(player, this.leprechaunClass)) {
if(event.getTo().getBlock().isLiquid()) {
event.getPlayer().setAllowFlight(true);
event.getPlayer().setFlying(true);
} else {
event.getPlayer().setFlying(false);
}
}
}
/*
* DEMO
*/
@EventHandler
public void landminePlace(final PlayerInteractEvent event) {
Player player = event.getPlayer();
MatchPlayer mPlayer = match.getPlayer(player);
if(!MatchPlayers.canInteract(mPlayer) || !this.isClass(mPlayer, this.demoClass)) return;
final ItemStack item = event.getPlayer().getItemInHand();
if(event.getAction() == Action.RIGHT_CLICK_BLOCK && item != null && item.getType() == Material.TNT) {
if(event.getClickedBlock().getRelative(BlockFace.UP).getType() != Material.AIR) {
mPlayer.sendWarning(ChatColor.RED + PGMTranslations.t("match.ghostSquadron.landmine.invalidLocation", mPlayer), true);
return;
}
if(this.landmines.containsKey(event.getClickedBlock().getLocation())) {
mPlayer.sendWarning(ChatColor.RED + PGMTranslations.t("match.ghostSquadron.landmine.alreadyExists", mPlayer), true);
return;
}
Location place = event.getClickedBlock().getLocation().add(.5, 0, .5);
for(Location loc : this.landmines.keySet()) {
boolean xClose = Math.abs(place.getX() - loc.getX()) <= GhostSquadron.LANDMINE_SPACING;
boolean zClose = Math.abs(place.getZ() - loc.getZ()) <= GhostSquadron.LANDMINE_SPACING;
if(xClose && zClose) {
mPlayer.sendWarning(ChatColor.RED + PGMTranslations.t("match.ghostSquadron.landmine.tooClose", mPlayer), true);
return;
}
}
event.setCancelled(true);
this.landmines.put(place, mPlayer.getPlayerId());
this.landmineTeams.put(place, mPlayer.getCompetitor());
if(item.getAmount() > 1) {
item.setAmount(item.getAmount() - 1);
} else {
event.getPlayer().setItemInHand(null);
}
player.sendMessage(ChatColor.GREEN + PGMTranslations.t("ghostSquadron.landminePlanted", mPlayer));
}
}
@EventHandler
public void landmineExplode(final PlayerMoveEvent event) {
MatchPlayer player = this.getMatch().getPlayer(event.getPlayer());
if(!MatchPlayers.canInteract(player)) return;
Location to = event.getTo();
Iterator<Entry<Location, PlayerId>> iterator = this.landmines.entrySet().iterator();
while(iterator.hasNext()) {
Map.Entry<Location, PlayerId> entry = iterator.next();
Location landmine = entry.getKey();
MatchPlayer placer = this.getMatch().getPlayer(entry.getValue());
if(placer == null || !placer.isParticipating()) {
iterator.remove();
continue;
}
Competitor placerTeam = this.landmineTeams.get(landmine);
if(placerTeam == player.getParty()) continue;
if(to.distanceSquared(landmine) < GhostSquadron.LANDMINE_ACTIVATION_DISTANCE_SQ) {
TNTPrimed tnt = (TNTPrimed) landmine.getWorld().spawnEntity(landmine.clone().add(0, 1, 0), EntityType.PRIMED_TNT);
tnt.setFuseTicks(0);
tnt.setYield(1);
this.reveal(player.getBukkit());
getMatch().callEvent(new ExplosionPrimeByEntityEvent(tnt, placer.getBukkit()));
iterator.remove();
this.landmineTeams.remove(landmine);
}
}
}
/*
* SPIDER
*/
public void spideySense(final MatchPlayer player) {
UserId userId = player.getPlayerId();
Date when = this.spideySenses.get(userId);
Date now = new Date();
if(when == null || now.getTime() > when.getTime() + GhostSquadron.SPIDEY_SENSE_COOLDOWN) {
this.spideySenses.put(userId, now);
player.getBukkit().addPotionEffect(new PotionEffect(PotionEffectType.NIGHT_VISION, 4 * 20, 0), true);
player.getBukkit().addPotionEffect(new PotionEffect(PotionEffectType.SPEED, 4 * 20, 1), true);
player.getBukkit().playSound(player.getBukkit().getLocation(), Sound.ENTITY_SPIDER_AMBIENT, 5, 0);
player.getBukkit().playSound(player.getBukkit().getLocation(), Sound.ENTITY_SPIDER_AMBIENT, 5, 0.25f);
player.getBukkit().playSound(player.getBukkit().getLocation(), Sound.ENTITY_SPIDER_AMBIENT, 5, 0.5f);
}
}
@EventHandler
public void webBow(final EntityShootBowEvent event) {
if(!(event.getEntity() instanceof Player)) return;
Player player = (Player) event.getEntity();
if(!this.isClass(this.getMatch().getPlayer(player), spiderClass)) return;
FallingBlock web = event.getEntity().getWorld().spawnFallingBlock(event.getProjectile().getLocation(), Material.WEB, (byte) 0);
web.setDropItem(false);
web.setVelocity(event.getProjectile().getVelocity());
event.setProjectile(web);
}
@EventHandler
public void webLand(final EntityChangeBlockEvent event) {
if(!(event.getEntity() instanceof FallingBlock)) return;
FallingBlock block = (FallingBlock) event.getEntity();
if(block.getMaterial() != Material.WEB) return;
event.getEntity().getLocation().getBlock().setType(Material.WEB);
}
/*
* NINJA - Temporarily disabled
*/
/*
@EventHandler
public void hookPlayer(final PlayerFishEvent event) {
if(event.getState() == PlayerFishEvent.State.FISHING || event.getState() == PlayerFishEvent.State.FAILED_ATTEMPT) return;
MatchPlayer caster = this.match.getPlayer(event.getPlayer());
if (!this.isClass(caster, this.ninjaClass)) return;
Location center = event.getHook().getLocation();
Vector pullTowards = event.getPlayer().getLocation().toVector();
for(Player player : event.getPlayer().getWorld().getPlayers()) {
if(player == event.getPlayer()) continue;
if(player.getLocation().distance(center) > 5) continue;
MatchPlayer matchPlayer = this.match.getPlayer(player);
if(!matchPlayer.canInteract() || matchPlayer.getTeam() == caster.getTeam()) continue;
Vector velocity = pullTowards.subtract(player.getLocation().toVector()).divide(new Vector(3, 3, 3));
double MIN = -2;
double MAX = 2;
velocity.setX(clamp(MIN, MAX, velocity.getX()));
velocity.setY(clamp(MIN, MAX, velocity.getY()));
velocity.setZ(clamp(MIN, MAX, velocity.getZ()));
player.setVelocity(velocity);
this.reveal(player);
}
event.getHook().remove();
}
*/
private static double clamp(double min, double max, double def) {
if(def < min) return min;
if(def > max) return max;
return def;
}
private boolean isClass(MatchPlayer player, Optional<PlayerClass> playerClass) {
return playerClass.isPresent() && isClass(player, playerClass.get());
}
private boolean isClass(MatchPlayer player, PlayerClass playerClass) {
return classMatchModule.playingClass(player)
.filter(playerClass::equals)
.isPresent();
}
}