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()); } } }