package tc.oc.pgm.controlpoint;
import org.bukkit.block.Block;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.util.BlockVector;
import org.bukkit.util.Vector;
import tc.oc.commons.bukkit.util.BlockUtils;
import tc.oc.commons.bukkit.util.BukkitUtils;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.filters.operator.AllFilter;
import tc.oc.pgm.filters.operator.InverseFilter;
import tc.oc.pgm.match.Competitor;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.filters.query.BlockQuery;
import tc.oc.pgm.controlpoint.events.CapturingTimeChangeEvent;
import tc.oc.pgm.controlpoint.events.ControllerChangeEvent;
import tc.oc.pgm.filters.Filter;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.regions.FiniteBlockRegion;
import tc.oc.pgm.regions.Region;
import tc.oc.pgm.regions.SectorRegion;
import tc.oc.pgm.renewable.BlockImage;
import java.util.Objects;
/**
* Displays the status of a ControlPoint by coloring blocks in specified regions
*/
@ListenerScope(MatchScope.LOADED)
public class ControlPointBlockDisplay implements Listener {
protected final Match match;
protected final ControlPoint controlPoint;
protected final FiniteBlockRegion progressDisplayRegion;
protected final BlockImage progressDisplayImage;
protected final FiniteBlockRegion controllerDisplayRegion;
protected final BlockImage controllerDisplayImage;
protected Competitor controllingTeam;
public ControlPointBlockDisplay(Match match, ControlPoint controlPoint) {
this.match = match;
this.controlPoint = controlPoint;
Filter visualMaterials = controlPoint.getDefinition().getVisualMaterials();
Region progressDisplayRegion = controlPoint.getDefinition().getProgressDisplayRegion();
Region controllerDisplayRegion = controlPoint.getDefinition().getControllerDisplayRegion();
final FiniteBlockRegion.Factory regionFactory = new FiniteBlockRegion.Factory(match.getMapInfo().proto);
if(progressDisplayRegion == null) {
this.progressDisplayRegion = null;
this.progressDisplayImage = null;
} else {
this.progressDisplayRegion = regionFactory.fromWorld(progressDisplayRegion,
match.getWorld(),
visualMaterials);
this.progressDisplayImage = new BlockImage(match.getWorld(), this.progressDisplayRegion.getBounds());
this.progressDisplayImage.save();
}
if(controllerDisplayRegion == null) {
this.controllerDisplayRegion = null;
this.controllerDisplayImage = null;
} else {
// Ensure the controller and progress display regions do not overlap. The progress display has priority.
this.controllerDisplayRegion = regionFactory.fromWorld(
controllerDisplayRegion,
match.getWorld(),
this.progressDisplayRegion == null ? visualMaterials
: AllFilter.of(visualMaterials, new InverseFilter(progressDisplayRegion))
);
this.controllerDisplayImage = new BlockImage(match.getWorld(), this.controllerDisplayRegion.getBounds());
this.controllerDisplayImage.save();
}
}
/**
* Change the controller display to the given team's color, or reset the display if team is null
*/
@SuppressWarnings("deprecation")
public void setController(Competitor controllingTeam) {
if(!Objects.equals(this.controllingTeam, controllingTeam) && this.controllerDisplayRegion != null) {
if(controllingTeam == null) {
for(BlockVector block : this.controllerDisplayRegion.getBlockVectors()) {
this.controllerDisplayImage.restore(block);
}
} else {
byte blockData = BukkitUtils.chatColorToDyeColor(controllingTeam.getColor()).getWoolData();
for(BlockVector pos : this.controllerDisplayRegion.getBlockVectors()) {
BlockUtils.blockAt(match.getWorld(), pos).setData(blockData);
}
}
this.controllingTeam = controllingTeam;
}
}
private void setBlock(BlockVector pos, Competitor team) {
final Block block = BlockUtils.blockAt(match.getWorld(), pos);
if(this.controlPoint.getDefinition().getVisualMaterials().query(new BlockQuery(block)).isAllowed()) {
if(team != null) {
block.setData(BukkitUtils.chatColorToDyeColor(team.getColor()).getWoolData());
} else {
this.progressDisplayImage.restore(pos);
}
}
}
protected void setProgress(Competitor controllingTeam, Competitor capturingTeam, double capturingProgress) {
if(this.progressDisplayRegion != null) {
Vector center = this.progressDisplayRegion.getBounds().center();
// capturingProgress can be zero, but it can never be one, so invert it to avoid
// a zero-area SectorRegion that can cause glitchy rendering
SectorRegion sectorRegion = new SectorRegion(center.getX(), center.getZ(), 0, (1 - capturingProgress) * 2 * Math.PI);
for(BlockVector pos : this.progressDisplayRegion.getBlockVectors()) {
if(sectorRegion.contains(pos)) {
this.setBlock(pos, controllingTeam);
} else {
this.setBlock(pos, capturingTeam);
}
}
}
}
public void render() {
this.setController(this.controlPoint.getOwner());
this.setProgress(this.controlPoint.getOwner(),
this.controlPoint.getCapturer(),
this.controlPoint.getCompletion());
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onTimeChange(CapturingTimeChangeEvent event) {
if(this.controlPoint == event.getControlPoint()) {
this.setProgress(event.getControlPoint().getOwner(),
event.getControlPoint().getCapturer(),
event.getControlPoint().getCompletion());
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void onControllerChange(ControllerChangeEvent event) {
if(this.controlPoint == event.getControlPoint()) {
this.setController(event.getNewController());
}
}
}