package tc.oc.pgm.modes; import java.time.Duration; import java.util.Comparator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import javax.inject.Inject; 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.Sound; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import tc.oc.commons.bukkit.chat.BukkitSound; import tc.oc.commons.bukkit.chat.TemplateComponent; import tc.oc.commons.core.chat.Component; import tc.oc.commons.core.stream.Collectors; import tc.oc.commons.core.util.Comparables; import tc.oc.commons.core.util.Pair; import tc.oc.pgm.countdowns.CountdownContext; import tc.oc.pgm.countdowns.MatchCountdown; import tc.oc.pgm.countdowns.MultiCountdownContext; import tc.oc.pgm.events.ListenerScope; import tc.oc.pgm.events.MatchBeginEvent; import tc.oc.pgm.events.MatchEndEvent; import tc.oc.pgm.goals.ModeChangeGoal; import tc.oc.pgm.match.Match; import tc.oc.pgm.match.MatchScope; import tc.oc.pgm.timelimit.TimeLimitCountdown; @ListenerScope(MatchScope.LOADED) public class ObjectiveModeManager implements Listener { private static final BukkitSound SOUND = new BukkitSound(Sound.ENTITY_ZOMBIE_VILLAGER_CURE, 0.15f, 1.2f); private final Match match; private final MultiCountdownContext countdowns; private final List<ModeChangeGoal> goals; private final Map<ObjectiveMode, ModeChangeCountdown> modes; private final String message; private boolean started; @Inject ObjectiveModeManager(Match match, MultiCountdownContext countdowns, List<ObjectiveMode> modes, List<ModeChangeGoal> goals) { this.match = match; this.countdowns = countdowns; match.registerEvents(countdowns); this.goals = goals.stream() .filter(ModeChangeGoal::isAffectedByModeChanges) .collect(Collectors.toImmutableList()); this.modes = modes.stream() .sorted(Comparator.comparing(ObjectiveMode::after)) .collect(Collectors.mappingTo(ModeChangeCountdown::new)); String message = null; for(ModeChangeGoal goal : this.goals) { if(message == null) { message = goal.getModeChangeMessage(); } else if(!message.equals(goal.getModeChangeMessage())) { message = null; break; } } this.message = message != null ? message : "match.objectiveMode.name.generic"; } public BaseComponent name(ObjectiveMode mode) { return mode.name() .<BaseComponent>map(TemplateComponent::new) .orElseGet(() -> new TranslatableComponent(message, mode.materialName())); } public Optional<Pair<ObjectiveMode, Duration>> nextMode() { return started ? countdowns.countdowns(ModeChangeCountdown.class).findFirst() .map(countdown -> Pair.of(countdown.mode, countdowns.getTimeLeft(countdown))) : modes.keySet().stream().findFirst().map(mode -> Pair.of(mode, mode.after())); } public Duration timeUntilMode(ObjectiveMode mode) { return started ? Optional.ofNullable(countdowns.getTimeLeft(modes.get(mode))) .orElse(Duration.ZERO) : mode.after(); } public Map<ObjectiveMode, Duration> modes() { return modes.keySet().stream().collect(Collectors.mappingTo(this::timeUntilMode)); } @EventHandler private void start(MatchBeginEvent event) { modes.forEach((mode, countdown) -> countdowns.start(countdown, mode.after())); started = true; } @EventHandler private void stop(MatchEndEvent event) { countdowns.cancelAll(); } private class ModeChangeCountdown extends MatchCountdown { final ObjectiveMode mode; ModeChangeCountdown(ObjectiveMode mode) { super(ObjectiveModeManager.this.match); this.mode = mode; } @Override public void onEnd(Duration total) { super.onEnd(total); match.sendMessage(new Component(ChatColor.DARK_AQUA) .extra("> > > > ") .extra(new Component(name(mode), ChatColor.RED)) .extra(" < < < <")); match.playSound(SOUND); goals.forEach(goal -> goal.replaceBlocks(mode.material())); } @Override public BaseComponent barText(Player viewer) { return new Component(new TranslatableComponent("match.objectiveMode.countdown", new Component(name(mode), ChatColor.GOLD), secondsRemaining(ChatColor.AQUA)), ChatColor.DARK_AQUA); } @Override protected float barProgress(Duration remaining, Duration total) { return super.barProgress(remaining, this.mode.show_before()); } @Override public boolean isVisible(Player viewer) { if(!super.isVisible(viewer)) return false; CountdownContext countdowns = this.getMatch().countdowns(); Set<TimeLimitCountdown> timeLimitCountdowns = countdowns.getAll(TimeLimitCountdown.class); for (TimeLimitCountdown limit : timeLimitCountdowns) { // Don't show the countdown if it wont happen before the match ends if (Comparables.lessThan(countdowns.getTimeLeft(limit), remaining)) { return false; } } return remaining.getSeconds() < this.mode.show_before().getSeconds(); } } }