package in.twizmwaz.cardinal.module.modules.proximity;
import com.google.common.base.Optional;
import in.twizmwaz.cardinal.event.CardinalDeathEvent;
import in.twizmwaz.cardinal.event.CycleCompleteEvent;
import in.twizmwaz.cardinal.event.MatchEndEvent;
import in.twizmwaz.cardinal.event.MatchStartEvent;
import in.twizmwaz.cardinal.event.flag.FlagCaptureEvent;
import in.twizmwaz.cardinal.event.flag.FlagPickupEvent;
import in.twizmwaz.cardinal.event.flag.FlagRespawnEvent;
import in.twizmwaz.cardinal.event.objective.ObjectiveCompleteEvent;
import in.twizmwaz.cardinal.event.objective.ObjectiveProximityEvent;
import in.twizmwaz.cardinal.event.objective.ObjectiveTouchEvent;
import in.twizmwaz.cardinal.module.GameObjective;
import in.twizmwaz.cardinal.module.Module;
import in.twizmwaz.cardinal.module.modules.ctf.FlagObjective;
import in.twizmwaz.cardinal.module.modules.ctf.net.Net;
import in.twizmwaz.cardinal.module.modules.team.TeamModule;
import in.twizmwaz.cardinal.module.modules.wools.WoolObjective;
import in.twizmwaz.cardinal.util.Flags;
import in.twizmwaz.cardinal.util.Teams;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.BlockPlaceEvent;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Wool;
import org.bukkit.util.Vector;
import java.util.HashSet;
import java.util.Set;
public class GameObjectiveProximityHandler implements Module {
private GameObjective objective = null;
private TeamModule team;
private ProximityInfo info;
private Boolean active;
private Double proximity;
public GameObjectiveProximityHandler(TeamModule team, ProximityInfo info) {
this.team = team;
this.info = info;
this.proximity = Double.POSITIVE_INFINITY;
active = false;
}
public void setObjective(GameObjective objective) {
this.objective = objective;
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
}
public GameObjective getObjective() {
return objective;
}
public Double getProximity() {
return proximity;
}
public String getProximityAsString() {
return (proximity.equals(Double.POSITIVE_INFINITY) ? "Infinity" : (Math.round(proximity * 100.0) / 100.0) + "");
}
public String getProximityName() {
return info.needsTouch ? info.metric.getTouchedName() : info.metric.getName();
}
private void setProximity(Location loc, Player player) {
if (info.locations == null) return;
if (info.horizontal) {
loc = loc.clone();
loc.setY(0);
}
double newProximity = proximity;
for (Vector proxLoc : info.locations) {
double prox = proxLoc.distance(loc.position());
if (prox < newProximity) {
newProximity = prox;
}
}
if (newProximity < proximity) {
Double old = proximity;
proximity = newProximity;
Bukkit.getServer().getPluginManager().callEvent(new ObjectiveProximityEvent(objective, player, old, proximity));
}
}
private void tryUpdate(Player player, Block block) {
if (!teamAllowsUpdate(Teams.getTeamByPlayer(player))) return;
boolean update = true;
if (objective instanceof WoolObjective) {
update = !info.needsTouch;
if (info.needsTouch) {
if (info.metric.equals(ProximityMetric.CLOSEST_BLOCK)) {
if (block.getType().equals(Material.WOOL) && ((Wool) block.getState().getData()).getColor().equals(((WoolObjective) objective).getColor()))
update = true;
} else {
ItemStack item = new ItemStack(Material.WOOL, 1, ((WoolObjective) objective).getColor().getWoolData());
if (player.getInventory().containsAtLeast(item, 1)) update = true;
}
}
} else if (objective instanceof FlagObjective) {
if (info.needsTouch) {
update = Flags.getFlag(player) == objective;
}
}
if (update) setProximity(player.getLocation(), player);
}
public boolean teamAllowsUpdate(Optional<TeamModule> team) {
return !(team.isPresent() && team.get().isObserver()) && team.orNull() == this.team;
}
public void setActive(boolean active) {
this.active = active;
}
public void reset() {
this.proximity = Double.POSITIVE_INFINITY;
setActive(false);
}
public void setLocation(Location location) {
Set<Vector> locations = new HashSet<>();
locations.add(location.position());
this.info.setLocations(locations);
}
public void setLocations(Set<Vector> locations) {
this.info.setLocations(locations);
}
@EventHandler
public void onCycleComplete(CycleCompleteEvent event) {
if (objective instanceof FlagObjective && info.needsTouch) {
Set<Net> nets = Flags.getNetsByFlag((FlagObjective) objective);
Set<Vector> locations = new HashSet<>();
for (Net net : nets) locations.add(net.getLocation());
setLocations(locations);
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onMove(PlayerMoveEvent event) {
if (!active || !info.metric.equals(ProximityMetric.CLOSEST_PLAYER) && event.getFrom().getBlock() != event.getTo().getBlock()) return;
tryUpdate(event.getPlayer(), null);
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onPlaceBlock(BlockPlaceEvent event) {
if (!active || !info.metric.equals(ProximityMetric.CLOSEST_BLOCK)) return;
tryUpdate(event.getPlayer(), event.getBlockPlaced());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onKill(CardinalDeathEvent event) {
if (!active || !info.metric.equals(ProximityMetric.CLOSEST_KILL) || event.getKiller() == null
|| Teams.getTeamByPlayer(event.getKiller()).orNull() == Teams.getTeamByPlayer(event.getPlayer()).orNull()) return;
tryUpdate(event.getKiller(), null);
}
@EventHandler
public void onMatchStart(MatchStartEvent event) {
this.active = !info.needsTouch;
}
@EventHandler
public void onTouchEvent(ObjectiveTouchEvent event) {
if (event.getObjective().equals(objective)) this.active = info.needsTouch;
}
@EventHandler
public void onObjectiveComplete(ObjectiveCompleteEvent event) {
if (event.getObjective().equals(objective)) this.active = false;
}
@EventHandler(priority = EventPriority.MONITOR)
public void onFlagRespawn(FlagRespawnEvent event) {
if (event.getFlag().equals(this.objective)) {
this.active = !info.needsTouch;
if (this.active) setLocation(event.getPost().getCurrentBlock().getLocation());
}
}
@EventHandler
public void onFlagPickUp(FlagPickupEvent event) {
if (event.getFlag().equals(this.objective)) {
this.active = info.needsTouch;
}
}
@EventHandler
public void onFlagCapture(FlagCaptureEvent event) {
if (event.getFlag().equals(this.objective)) {
this.reset();
}
}
@EventHandler
public void onMatchEnd(MatchEndEvent event) {
this.active = false;
}
public enum ProximityMetric {
CLOSEST_PLAYER("closest player", "closest player"),
CLOSEST_BLOCK("closest block", "closest safety"),
CLOSEST_KILL("closest kill", "closest kill"),
NULL_PROXIMITY(null, null);
private final String name;
private final String touchedName;
ProximityMetric(String name, String touchedName) {
this.name = name;
this.touchedName = touchedName;
}
public String getName() {
return this.name;
}
public String getTouchedName() {
return this.touchedName;
}
public static ProximityMetric getByName(String name) {
for(ProximityMetric prox : ProximityMetric.values()) {
if(prox.name.equals(name)) return prox;
}
return NULL_PROXIMITY;
}
}
}