ProjectAres/PGM/src/main/java/tc/oc/pgm/flag/state/Carried.java

399 lines
14 KiB
Java

package tc.oc.pgm.flag.state;
import java.util.ArrayDeque;
import java.util.Deque;
import javax.annotation.Nullable;
import net.md_5.bungee.api.ChatColor;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import org.bukkit.Location;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.event.player.PlayerDropItemEvent;
import org.bukkit.inventory.ItemStack;
import tc.oc.commons.bukkit.event.CoarsePlayerMoveEvent;
import tc.oc.commons.bukkit.inventory.ArmorType;
import tc.oc.commons.core.IterableUtils;
import tc.oc.commons.core.chat.Component;
import tc.oc.commons.core.chat.Components;
import tc.oc.minecraft.protocol.MinecraftVersion;
import tc.oc.pgm.events.PlayerLeavePartyEvent;
import tc.oc.pgm.filters.query.PlayerQueryWithLocation;
import tc.oc.pgm.flag.Flag;
import tc.oc.pgm.flag.FlagDefinition;
import tc.oc.pgm.flag.Net;
import tc.oc.pgm.flag.Post;
import tc.oc.pgm.flag.event.FlagCaptureEvent;
import tc.oc.pgm.flag.event.FlagStateChangeEvent;
import tc.oc.pgm.goals.events.GoalEvent;
import tc.oc.pgm.kits.Kit;
import tc.oc.pgm.kits.KitPlayerFacet;
import tc.oc.pgm.match.Competitor;
import tc.oc.pgm.match.MatchPlayer;
import tc.oc.pgm.match.MatchScope;
import tc.oc.pgm.match.ParticipantState;
import tc.oc.pgm.match.Party;
import tc.oc.pgm.score.ScoreMatchModule;
import tc.oc.pgm.scoreboard.SidebarMatchModule;
import tc.oc.pgm.spawns.events.ParticipantDespawnEvent;
import tc.oc.pgm.teams.TeamFactory;
import tc.oc.pgm.teams.TeamMatchModule;
/**
* State of a flag when a player has picked it up and is wearing the banner on their head.
*/
public class Carried extends Spawned implements Missing {
protected final MatchPlayer carrier;
protected ItemStack helmetItem;
protected @Nullable Net deniedByNet;
protected @Nullable Flag deniedByFlag;
protected @Nullable BaseComponent lastMessage;
private static final int DROP_QUEUE_SIZE = 100;
private Deque<Location> dropLocations = new ArrayDeque<>(DROP_QUEUE_SIZE);
public Carried(Flag flag, Post post, MatchPlayer carrier, Location dropLocation) {
super(flag, post);
this.carrier = carrier;
this.dropLocations.add(dropLocation); // Need an initial dropLocation in case the carrier never generates ones
}
public MatchPlayer getCarrier() {
return carrier;
}
@Override
public boolean isRecoverable() {
return true;
}
@Override
public Location getLocation() {
return this.carrier.getBukkit().getLocation();
}
@Override
public Iterable<Location> getProximityLocations(ParticipantState player) {
if(isCarrying(player)) {
return IterableUtils.transfilter(flag.getNets(), (net) -> {
if(net.getCaptureFilter().query(player).isAllowed()) {
return net.getProximityLocation().toLocation(flag.getMatch().getWorld());
}
else {
return null;
}
});
} else {
return super.getProximityLocations(player);
}
}
@Override
public void enterState() {
super.enterState();
Kit kit = this.flag.getDefinition().getPickupKit();
if(kit != null) carrier.facet(KitPlayerFacet.class).applyKit(kit, false);
kit = this.flag.getDefinition().getCarryKit();
if(kit != null) carrier.facet(KitPlayerFacet.class).applyKit(kit, false);
this.helmetItem = this.carrier.getBukkit().getInventory().getHelmet();
this.carrier.getBukkit().getInventory().setHelmet(this.flag.getBannerItem().clone());
SidebarMatchModule smm = this.flag.getMatch().getMatchModule(SidebarMatchModule.class);
if(smm != null) smm.blinkGoal(this.flag, 2, null);
}
@Override
public void leaveState() {
SidebarMatchModule smm = this.flag.getMatch().getMatchModule(SidebarMatchModule.class);
if(smm != null) smm.stopBlinkingGoal(this.flag);
this.carrier.getBukkit().sendMessage(ChatMessageType.ACTION_BAR, Components.blank());
this.carrier.getInventory().remove(this.flag.getBannerItem());
this.carrier.getInventory().setHelmet(this.helmetItem);
Kit kit = this.flag.getDefinition().getDropKit();
if(kit != null) this.carrier.facet(KitPlayerFacet.class).applyKit(kit, false);
kit = this.flag.getDefinition().getCarryKit();
if(kit != null) kit.remove(this.carrier);
super.leaveState();
}
protected Competitor getBeneficiary(TeamFactory owner) {
if(owner != null) {
return this.flag.getMatch().needMatchModule(TeamMatchModule.class).team(owner);
} else {
return this.carrier.getCompetitor();
}
}
protected BaseComponent getMessage() {
BaseComponent message;
if(this.deniedByNet == null) {
if(this.flag.getDefinition().getCarryMessage() != null) {
message = this.flag.getDefinition().getCarryMessage();
} else {
message = new TranslatableComponent("match.flag.carrying.you", this.flag.getComponentName());
}
message.setColor(ChatColor.AQUA);
message.setBold(true);
return message;
} else {
if(this.deniedByNet.getDenyMessage() != null) {
message = this.deniedByNet.getDenyMessage();
} else if(this.deniedByFlag != null){
message = new TranslatableComponent("match.flag.captureDenied.byFlag",
this.flag.getComponentName(),
this.deniedByFlag.getComponentName());
} else {
message = new TranslatableComponent("match.flag.captureDenied",
this.flag.getComponentName());
}
message.setColor(ChatColor.RED);
message.setBold(true);
return message;
}
}
@Override
public void tickRunning() {
if (flag.getMatch().isFinished()) {
this.flag.transition(new Returned(this.flag, this.post, this.carrier.getEntityLocation()));
return;
}
super.tickRunning();
BaseComponent message = this.getMessage();
if (MinecraftVersion.atLeast(MinecraftVersion.MINECRAFT_1_8, this.carrier.getBukkit().getProtocolVersion())) {
this.carrier.sendHotbarMessage(message);
}
if(!Components.equals(message, this.lastMessage)) {
this.lastMessage = message;
this.carrier.showTitle(new Component(), message, 0, 5, 35);
}
ScoreMatchModule smm = this.flag.getMatch().getMatchModule(ScoreMatchModule.class);
if(smm != null && this.flag.getDefinition().getPointsPerSecond() > 0) {
smm.incrementScore(this.getBeneficiary(this.flag.getDefinition().getOwner()),
this.flag.getDefinition().getPointsPerSecond() / 20D);
}
}
@Override
public boolean isCarrying(MatchPlayer player) {
return this.carrier == player;
}
@Override
public boolean isCarrying(Party party) {
return this.carrier.getParty() == party;
}
@Override
protected boolean canSeeParticles(Player player) {
return super.canSeeParticles(player) && player != this.carrier.getBukkit();
}
protected void dropFlag() {
for(Location dropLocation : this.dropLocations) {
if(this.flag.canDrop(new PlayerQueryWithLocation(carrier, dropLocation))) {
this.flag.transition(new Dropped(this.flag, this.post, dropLocation, this.carrier));
return;
}
}
// Could not find a usable drop location, just recover the flag
forceRecover();
}
protected void captureFlag(Net net) {
this.carrier.sendMessage(new TranslatableComponent("match.flag.capture.you",
this.flag.getComponentName()));
this.flag.getMatch().sendMessageExcept(new TranslatableComponent("match.flag.capture",
this.flag.getComponentName(),
this.carrier.getComponentName()),
this.carrier);
this.flag.resetTouches(this.carrier.getCompetitor());
this.flag.resetProximity(this.carrier.getCompetitor());
ScoreMatchModule smm = this.flag.getMatch().getMatchModule(ScoreMatchModule.class);
if(smm != null) {
if(net.getPointsPerCapture() != 0) {
smm.incrementScore(this.getBeneficiary(net.getOwner()),
net.getPointsPerCapture());
}
if(this.flag.getDefinition().getPointsPerCapture() != 0) {
smm.incrementScore(this.getBeneficiary(this.flag.getDefinition().getOwner()),
this.flag.getDefinition().getPointsPerCapture());
}
}
Post post = net.getReturnPost() != null ? net.getReturnPost() : this.post;
if(post.isPermanent()) {
this.flag.transition(new Completed(this.flag, post));
} else {
this.flag.transition(new Captured(this.flag, post, net, this.getLocation()));
}
FlagCaptureEvent event = new FlagCaptureEvent(this.flag, this.carrier, net);
this.flag.getMatch().callEvent(event);
}
protected boolean isCarrier(MatchPlayer player) {
return carrier.equals(player);
}
protected boolean isCarrier(Entity player) {
return carrier.getBukkit().equals(player);
}
protected boolean isFlag(ItemStack stack) {
return stack.isSimilar(this.flag.getBannerItem());
}
@Override
public void onEvent(PlayerDropItemEvent event) {
super.onEvent(event);
if(this.isCarrier(event.getPlayer()) && this.isFlag(event.getItemDrop().getItemStack())) {
event.getItemDrop().remove();
dropFlag();
}
}
@Override
public void onEvent(ParticipantDespawnEvent event) {
super.onEvent(event);
// Don't drop the flag when the match ends and everyone despawns
if(isCarrier(event.getPlayer()) && flag.getMatch().isRunning()) {
dropFlag();
}
}
@Override
public void onEvent(PlayerLeavePartyEvent event) {
super.onEvent(event);
// Handle flag carrier disconnecting after match ends.
// Disconnect during the match is handled by the despawn event instead.
if(isCarrier(event.getPlayer()) && flag.getMatch().isFinished()) {
dropFlag();
}
}
@Override
public void onEvent(InventoryClickEvent event) {
super.onEvent(event);
if(this.isCarrier(event.getWhoClicked()) && event.getSlot() == ArmorType.HELMET.inventorySlot()) {
event.setCancelled(true);
event.getView().setCursor(null);
event.setCurrentItem(null);
this.flag.getMatch().getScheduler(MatchScope.RUNNING).createTask(() -> {
if(isCurrent()) {
dropFlag();
}
});
}
}
@Override
public void onEvent(CoarsePlayerMoveEvent event) {
super.onEvent(event);
if(flag.getMatch().isFinished()) {
dropFlag();
return;
}
if(this.isCarrier(event.getPlayer())) {
final Location playerLoc = event.getBlockTo();
if((dropLocations.isEmpty() || !dropLocations.peekLast().equals(playerLoc)) &&
flag.canDrop(new PlayerQueryWithLocation(carrier, playerLoc))) {
if(this.dropLocations.size() >= DROP_QUEUE_SIZE) this.dropLocations.removeLast();
this.dropLocations.addFirst(playerLoc);
}
this.checkCapture(playerLoc);
}
}
@Override
public void onEvent(GoalEvent event) {
super.onEvent(event);
this.checkCapture(null);
}
@Override
public void onEvent(FlagStateChangeEvent event) {
super.onEvent(event);
this.checkCapture(null);
}
protected void checkCapture(Location to) {
if(to == null) to = this.carrier.getBukkit().getLocation();
this.deniedByFlag = null;
if(this.deniedByNet != null && !this.deniedByNet.isSticky()) {
this.deniedByNet = null;
}
for(Net net : this.flag.getNets()) {
if(net.getRegion().contains(to)) {
if(tryCapture(net)) {
return;
} else {
this.deniedByNet = net;
}
}
}
if(this.deniedByNet != null) {
tryCapture(this.deniedByNet);
}
}
protected boolean tryCapture(Net net) {
for(FlagDefinition returnableDef : net.getRecoverableFlags()) {
Flag returnable = returnableDef.getGoal(this.flag.getMatch());
if(returnable.isCurrent(Carried.class)) {
this.deniedByFlag = returnable;
return false;
}
}
if(this.flag.canCapture(this.carrier, net)) {
this.captureFlag(net);
return true;
} else {
return false;
}
}
@Override
public ChatColor getStatusColor(Party viewer) {
if(this.flag.getDefinition().hasMultipleCarriers()) {
return this.carrier.getParty().getColor();
} else {
return super.getStatusColor(viewer);
}
}
@Override
public String getStatusSymbol(Party viewer) {
return Flag.CARRIED_SYMBOL;
}
}