ProjectAres/PGM/src/main/java/tc/oc/pgm/raindrops/RaindropListener.java

313 lines
13 KiB
Java
Executable File

package tc.oc.pgm.raindrops;
import java.time.Duration;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import com.google.common.collect.HashMultimap;
import com.google.common.primitives.Ints;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.ChatColor;
import org.bukkit.DyeColor;
import org.bukkit.Material;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Item;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.Listener;
import org.bukkit.event.entity.EntityDespawnInVoidEvent;
import org.bukkit.event.inventory.CraftItemEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.material.Wool;
import tc.oc.api.docs.PlayerId;
import tc.oc.commons.bukkit.raindrops.RaindropConstants;
import tc.oc.commons.bukkit.raindrops.RaindropUtil;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.util.Comparables;
import tc.oc.pgm.PGM;
import tc.oc.pgm.destroyable.DestroyableContribution;
import tc.oc.pgm.destroyable.DestroyableDestroyedEvent;
import tc.oc.pgm.events.ListenerScope;
import tc.oc.pgm.events.MatchEndEvent;
import tc.oc.pgm.events.MatchPlayerDeathEvent;
import tc.oc.pgm.goals.Goal;
import tc.oc.pgm.goals.GoalMatchModule;
import tc.oc.pgm.goals.TouchableGoal;
import tc.oc.pgm.goals.events.GoalCompleteEvent;
import tc.oc.pgm.goals.events.GoalTouchEvent;
import tc.oc.pgm.match.Competitor;
import tc.oc.pgm.match.Match;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.match.MatchPlayerState;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.match.ParticipantState;
import tc.oc.pgm.teams.Team;
import tc.oc.pgm.teams.TeamMatchModule;
import tc.oc.pgm.victory.VictoryMatchModule;
import tc.oc.pgm.wool.MonumentWool;
import tc.oc.pgm.wool.MonumentWoolFactory;
import tc.oc.pgm.wool.PlayerWoolPlaceEvent;
@ListenerScope(MatchScope.LOADED)
public class RaindropListener implements Listener {
private final HashMultimap<MonumentWool, PlayerId> touchedWools = HashMultimap.create();
private final Map<Item, PlayerId> droppedWools = new WeakHashMap<>();
private final HashMultimap<DyeColor, PlayerId> destroyedWools = HashMultimap.create();
private final HashMultimap<TouchableGoal, ParticipantState> deferredTouches = HashMultimap.create();
private double tiedRewardPercent(Match match) {
return 1.0d / match.needMatchModule(TeamMatchModule.class).getTeams().size();
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void handleItemDrop(final PlayerDropItemEvent event) {
ParticipantState player = PGM.getMatchManager().getParticipantState(event.getPlayer());
if(player == null) return;
Competitor team = player.getParty();
Item itemDrop = event.getItemDrop();
ItemStack item = itemDrop.getItemStack();
if (this.isDestroyableWool(item, team)) {
this.droppedWools.put(itemDrop, player.getPlayerId());
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleItemDespawn(final EntityDespawnInVoidEvent event) {
Entity entity = event.getEntity();
if (!(entity instanceof Item)) return;
ItemStack stack = ((Item) entity).getItemStack();
PlayerId playerId = this.droppedWools.remove(entity);
if (playerId == null) return;
ParticipantState player = PGM.getMatchManager().getParticipantState(playerId);
if (player == null) return;
if(isDestroyableWool(stack, player.getParty())) {
giveWoolDestroyRaindrops(player, ((Wool) stack.getData()).getColor());
}
}
@EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
public void handleCraft(final CraftItemEvent event) {
ParticipantState player = PGM.getMatchManager().getParticipantState(event.getWhoClicked());
if (player == null) return;
for (ItemStack ingredient : event.getInventory().getMatrix()) {
if(this.isDestroyableWool(ingredient, player.getParty())) {
giveWoolDestroyRaindrops(player, ((Wool) ingredient.getData()).getColor());
}
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleMatchEndEvent(final MatchEndEvent event) {
final Set<Competitor> winners = event.getMatch().needMatchModule(VictoryMatchModule.class).winners();
Match match = event.getMatch();
boolean applyCutoff = Comparables.greaterThan(match.getLength(), RaindropConstants.TEAM_REWARD_CUTOFF);
for(MatchPlayer player : match.getParticipatingPlayers()) {
if(player.getParty() instanceof Team) {
Team team = (Team) player.getParty();
Duration teamTime = team.getCumulativeParticipation(player.getPlayerId());
if(!(applyCutoff && Comparables.lessThan(teamTime, RaindropConstants.TEAM_REWARD_CUTOFF))) {
Component message = new Component(net.md_5.bungee.api.ChatColor.GRAY);
double percent = 1.0;
Competitor playerTeam = player.getCompetitor();
assert playerTeam != null;
if(winners.contains(playerTeam)) {
if(winners.size() == 1) {
message.extra(new TranslatableComponent("matchend.team.won", playerTeam.getComponentName()));
} else {
message.extra(new TranslatableComponent("matchend.team.tied", playerTeam.getComponentName()));
percent = tiedRewardPercent(match);
}
} else {
message.extra(new TranslatableComponent("matchend.team.loyalty",
playerTeam.getComponentName(),
new Component(String.valueOf(teamTime.toMinutes()))));
percent = RaindropConstants.LOSING_TEAM_REWARD_PERCENT;
}
givePercentageRaindrops(player.getParticipantState(), RaindropConstants.TEAM_REWARD, message, true, percent);
}
}
}
this.touchedWools.clear();
this.droppedWools.clear();
this.destroyedWools.clear();
this.deferredTouches.clear();
}
private void giveGoalTouchRaindrops(ParticipantState player, TouchableGoal goal) {
this.giveRaindrops(player,
RaindropConstants.TOUCH_GOAL_REWARD,
goal.getTouchMessage(player, true));
}
@EventHandler(priority = EventPriority.MONITOR)
public void handleGoalTouch(final GoalTouchEvent event) {
Goal goal = event.getGoal();
if (!goal.isVisible() || goal.isCompleted() || event.getPlayer() == null || !event.isFirstForPlayerLife()) {
return;
}
if (goal instanceof MonumentWool) {
PlayerId playerId = event.getPlayer().getPlayerId();
if (this.touchedWools.containsEntry(goal, playerId)) {
return;
} else {
this.touchedWools.put((MonumentWool) goal, playerId);
}
}
if(event.getGoal().getDeferTouches()) {
this.deferredTouches.put(event.getGoal(), event.getPlayer());
} else {
this.giveGoalTouchRaindrops(event.getPlayer(), event.getGoal());
event.setCancelToucherMessage(true);
}
}
@EventHandler
public void handleGoalComplete(GoalCompleteEvent event) {
if(event.getGoal() instanceof TouchableGoal) {
TouchableGoal goal = (TouchableGoal) event.getGoal();
for(ParticipantState player : this.deferredTouches.get(goal)) {
this.giveGoalTouchRaindrops(player, goal);
}
this.deferredTouches.removeAll(goal);
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void woolPlace(final PlayerWoolPlaceEvent event) {
if (event.getWool().isVisible()) {
giveRaindrops(event.getPlayer().getPlayerId(), RaindropConstants.WOOL_PLACE_REWARD, new TranslatableComponent("match.wool.place", event.getWool().getComponentName()));
}
}
@EventHandler(priority = EventPriority.MONITOR)
public void destroyableDestroy(final DestroyableDestroyedEvent event) {
if (event.getDestroyable().isVisible()) {
for (DestroyableContribution info : event.getDestroyable().getContributions()) {
String percentage = ChatColor.GREEN.toString() + ((int) (info.getPercentage() * 100)) + ChatColor.GRAY;
BaseComponent reason = new TranslatableComponent("match.destroyable.destroy", percentage, new Component(event.getDestroyable().getName(), net.md_5.bungee.api.ChatColor.GREEN));
giveRaindrops(info.getPlayerState().getPlayerId(), (int) (RaindropConstants.DESTROYABLE_DESTROY_PERCENT_REWARD * info.getPercentage()), reason);
}
}
}
@EventHandler(priority = EventPriority.NORMAL)
public void kill(final MatchPlayerDeathEvent event) {
if(!event.isChallengeKill()) return;
// Death raindrops are given by the backend, to reduce API usage
final int raindrops = calculateRaindrops(
event.getKiller(),
RaindropConstants.KILL_REWARD,
false,
1
);
event.setRaindrops(raindrops);
final MatchPlayer killer = event.getOnlineKiller();
if(killer != null) {
RaindropUtil.showRaindrops(
killer.getBukkit(),
raindrops,
RaindropUtil.calculateMultiplier(event.getKiller().getPlayerId()),
new TranslatableComponent("match.kill.killed", event.getVictim().getComponentName())
);
}
}
private void giveWoolDestroyRaindrops(final ParticipantState player, final DyeColor wool) {
MatchPlayer online = player.getMatchPlayer();
if(online != null) giveWoolDestroyRaindrops(online, wool);
}
private void giveWoolDestroyRaindrops(final MatchPlayer player, final DyeColor wool) {
if(!this.destroyedWools.containsEntry(wool, player.getPlayerId())) {
this.destroyedWools.put(wool, player.getPlayerId());
giveRaindrops(player.getPlayerId(), RaindropConstants.WOOL_DESTROY_REWARD, new TranslatableComponent("match.wool.destroy", MonumentWoolFactory.makeComponentName(wool)));
}
}
private int calculateRaindrops(MatchPlayerState player, int count, boolean scaled, double percent) {
if(scaled) {
final Match match = player.getMatch();
count += (int) ((double) match.getParticipatingPlayers().size() / match.getMaxPlayers() * RaindropConstants.MATCH_FULLNESS_BONUS);
if(player.getParty() instanceof Team) {
count += Ints.min((int) (Math.sqrt(((Team) player.getParty()).getCumulativeParticipation(player.getPlayerId()).getSeconds()) / RaindropConstants.PLAY_TIME_BONUS), RaindropConstants.PLAY_TIME_BONUS_CUTOFF);
}
}
return RaindropUtil.calculateRaindrops(player.getPlayerId(), (int) (count * percent), true);
}
private void giveRaindrops(final MatchPlayerState player, int count, BaseComponent reason) {
giveRaindrops(player, count, reason, true);
}
private void giveRaindrops(final MatchPlayerState player, int count, final BaseComponent reason, boolean scaled) {
givePercentageRaindrops(player, count, reason, scaled, 1.0);
}
private void giveRaindrops(final PlayerId playerId, int count, BaseComponent reason) {
RaindropUtil.giveRaindrops(playerId, count, null, reason);
}
private void givePercentageRaindrops(final MatchPlayerState player, int count, final BaseComponent reason, boolean scaled, double percent) {
RaindropUtil.giveRaindrops(
player.getPlayerId(),
calculateRaindrops(player, count, scaled, percent),
RaindropUtil.calculateMultiplier(player.getPlayerId()),
null,
reason,
true
);
}
/**
* Test if the given ItemStack is strictly an enemy wool i.e. not also
* a wool that the given team can capture.
*/
private boolean isDestroyableWool(ItemStack stack, Competitor team) {
if(stack == null || stack.getType() != Material.WOOL) {
return false;
}
DyeColor color = ((Wool) stack.getData()).getColor();
boolean enemyOwned = false;
for(Goal goal : team.getMatch().needMatchModule(GoalMatchModule.class).getGoals()) {
if(goal instanceof MonumentWool) {
MonumentWool wool = (MonumentWool) goal;
if(wool.isVisible() && !wool.isPlaced() && wool.getDyeColor() == color) {
if(wool.getOwner() == team) {
return false;
} else {
enemyOwned = true;
}
}
}
}
return enemyOwned;
}
}