package in.twizmwaz.cardinal.module.modules.titleRespawn;
import com.google.common.base.Optional;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import in.twizmwaz.cardinal.Cardinal;
import in.twizmwaz.cardinal.GameHandler;
import in.twizmwaz.cardinal.chat.ChatConstant;
import in.twizmwaz.cardinal.chat.LocalizedChatMessage;
import in.twizmwaz.cardinal.event.CardinalDeathEvent;
import in.twizmwaz.cardinal.event.CardinalSpawnEvent;
import in.twizmwaz.cardinal.event.CycleCompleteEvent;
import in.twizmwaz.cardinal.event.MatchEndEvent;
import in.twizmwaz.cardinal.event.MatchStartEvent;
import in.twizmwaz.cardinal.event.PlayerChangeTeamEvent;
import in.twizmwaz.cardinal.event.PlayerNameUpdateEvent;
import in.twizmwaz.cardinal.module.TaskedModule;
import in.twizmwaz.cardinal.module.modules.filter.FilterModule;
import in.twizmwaz.cardinal.module.modules.filter.FilterState;
import in.twizmwaz.cardinal.module.modules.spawn.SpawnModule;
import in.twizmwaz.cardinal.module.modules.team.TeamModule;
import in.twizmwaz.cardinal.module.modules.teamPicker.TeamPicker;
import in.twizmwaz.cardinal.module.modules.visibility.Visibility;
import in.twizmwaz.cardinal.util.PacketUtils;
import in.twizmwaz.cardinal.util.Players;
import in.twizmwaz.cardinal.util.Teams;
import in.twizmwaz.cardinal.util.Watchers;
import net.md_5.bungee.api.ChatMessageType;
import net.md_5.bungee.api.chat.BaseComponent;
import net.md_5.bungee.api.chat.TextComponent;
import net.md_5.bungee.api.chat.TranslatableComponent;
import net.md_5.bungee.chat.ComponentSerializer;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.EnumItemSlot;
import net.minecraft.server.Packet;
import net.minecraft.server.PacketPlayOutEntityDestroy;
import net.minecraft.server.PacketPlayOutEntityEquipment;
import net.minecraft.server.PacketPlayOutEntityStatus;
import net.minecraft.server.PacketPlayOutMount;
import net.minecraft.server.PacketPlayOutSpawnEntityLiving;
import net.minecraft.server.PacketPlayOutWorldBorder;
import net.minecraft.server.WorldBorder;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.craftbukkit.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.bukkit.entity.Projectile;
import org.bukkit.event.EventHandler;
import org.bukkit.event.EventPriority;
import org.bukkit.event.HandlerList;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.player.PlayerBucketEmptyEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerLocaleChangeEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import java.lang.reflect.Field;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
public class TitleRespawn implements TaskedModule {
private final int delay;
private final boolean auto;
private final boolean blackout;
private final boolean spectate;
private final boolean bed;
private final String message;
private DecimalFormat formatter = new DecimalFormat("0.0");
private Set<UUID> hasLeftClicked = new HashSet<>();
private Map<UUID, Long> deadPlayers = new HashMap<>();
public TitleRespawn(int delay, boolean auto, boolean blackout, boolean spectate, boolean bed, String message) {
this.delay = delay;
this.auto = auto;
this.blackout = blackout;
this.spectate = spectate;
this.bed = bed;
this.message = message;
}
@Override
public void unload() {
HandlerList.unregisterAll(this);
}
@Override
public void run() {
getSubtitleLogic();
}
private boolean isAllowedToRespawn(UUID id) {
double timeLeftToRespawn = this.delay - ((System.currentTimeMillis() - deadPlayers.get(id)) / 1000.0);
return this.isDeadUUID(id) && timeLeftToRespawn <= 0;
}
public boolean isDeadUUID(UUID id) {
return this.deadPlayers.containsKey(id);
}
private boolean hasLeftClicked(UUID id) {
return hasLeftClicked.contains(id);
}
private void getSubtitleLogic() {
List<UUID> deadClone = new ArrayList<>();
for (UUID uuid : this.deadPlayers.keySet()) deadClone.add(uuid);
for (UUID id : deadClone) {
double timeLeftToRespawn = this.delay - ((System.currentTimeMillis() - deadPlayers.get(id)) / 1000.0);
Player player = Bukkit.getPlayer(id);
if (timeLeftToRespawn > 0) {
if (hasLeftClicked(id)) {
player.setSubtitle(TextComponent.fromLegacyText(ChatColor.GREEN + new LocalizedChatMessage(ChatConstant.UI_DEATH_RESPAWN_CONFIRMED_TIME, ChatColor.AQUA + formatter.format(timeLeftToRespawn).replace(",", ".") + ChatColor.GREEN).getMessage(player.getLocale())));
} else {
player.setSubtitle(TextComponent.fromLegacyText(ChatColor.GREEN + new LocalizedChatMessage(ChatConstant.UI_DEATH_RESPAWN_UNCONFIRMED_TIME, ChatColor.AQUA + formatter.format(timeLeftToRespawn).replace(",",".") + ChatColor.GREEN).getMessage(player.getLocale())));
}
} else if (isAllowedToRespawn(id)) {
if (hasLeftClicked(id) && playerCanRespawn(player)) {
respawnPlayer(player, true);
} else {
if (hasLeftClicked(id)) {
if (message == null) {
player.setSubtitle(TextComponent.fromLegacyText(ChatColor.GREEN + new LocalizedChatMessage(ChatConstant.UI_DEATH_RESPAWN_CONFIRMED_WAITING).getMessage(player.getLocale())));
} else {
player.setSubtitle(getSubtitleMessage(player.getLocale()));
}
} else {
player.setSubtitle(TextComponent.fromLegacyText(ChatColor.GREEN + new LocalizedChatMessage(ChatConstant.UI_DEATH_RESPAWN_UNCONFIRMED).getMessage(player.getLocale())));
}
if (player.getInventory().getItem(2) == null || !player.getInventory().getItem(2).getType().equals(Material.LEATHER_HELMET)) {
ItemStack picker = TeamPicker.getTeamPicker(player.getLocale());
player.getInventory().setItem(2, picker);
}
}
}
}
}
private boolean playerCanRespawn(Player player) {
TeamModule team = Teams.getTeamByPlayer(player).get();
for (SpawnModule spawnModule : Teams.getSpawns(team)) {
FilterModule filter = spawnModule.getFilter();
if (filter == null || filter.evaluate(player).equals(FilterState.ALLOW)) return true;
}
return false;
}
private BaseComponent[] getSubtitleMessage(String locale) {
JsonParser parser = new JsonParser();
JsonObject object = translateJson(parser.parse(message).getAsJsonObject(), locale);
BaseComponent[] components = ComponentSerializer.parse(object.toString());
components[0].setColor(net.md_5.bungee.api.ChatColor.GREEN);
return components;
}
private JsonObject translateJson(JsonObject object, String locale) {
if (object.get("translate") != null) {
ChatConstant chatConstant = ChatConstant.fromPath(object.get("translate").getAsString());
if (chatConstant != null) {
object.addProperty("text", new LocalizedChatMessage(ChatConstant.fromPath(object.get("translate").getAsString())).getMessage(locale));
object.remove("translate");
}
}
if (object.get("extra") != null) {
for (JsonElement extra : object.getAsJsonArray("extra")) {
translateJson(extra.getAsJsonObject(), locale);
}
}
return object;
}
private void dropInventory(Player player) {
dropInventory(player, false);
}
private void dropInventory(Player player, boolean force) {
Optional<TeamModule> team = Teams.getTeamByPlayer(player);
if (GameHandler.getGameHandler().getMatch().isRunning() && (force || (team.isPresent() && !team.get().isObserver() && !isDeadUUID(player.getUniqueId())))) {
for (ItemStack stack : player.getInventory().getContents()) {
if (stack != null && stack.getType() != Material.AIR) {
player.getWorld().dropItemNaturally(player.getLocation().add(0, 0.5, 0), stack);
}
}
player.getInventory().clear();
player.getInventory().setArmorContents(null);
player.getInventory().setExtraContents(null);
}
}
private void setBlood(Player player, Boolean active) {
sendWorldBorderPacket(player, active ? Integer.MAX_VALUE : GameHandler.getGameHandler().getMatchWorld().getWorldBorder().getWarningDistance());
}
private void sendTitle(Player player) {
TranslatableComponent title = new TranslatableComponent("deathScreen.title");
title.setColor(net.md_5.bungee.api.ChatColor.RED);
player.showTitle(title, new TextComponent(""), 0, Integer.MAX_VALUE, 0);
}
private void sendWorldBorderPacket(Player player, int warningBlocks) {
EntityPlayer nmsPlayer = ((CraftPlayer) player).getHandle();
WorldBorder playerWorldBorder = nmsPlayer.world.getWorldBorder();
PacketPlayOutWorldBorder worldBorder = new PacketPlayOutWorldBorder(playerWorldBorder, PacketPlayOutWorldBorder.EnumWorldBorderAction.SET_WARNING_BLOCKS);
try {
Field field = worldBorder.getClass().getDeclaredField("i");
field.setAccessible(true);
field.setInt(worldBorder, warningBlocks);
field.setAccessible(!field.isAccessible());
} catch (Exception e) {
e.printStackTrace();
}
PacketUtils.sendPacket(player, worldBorder);
}
private void playDeathAnimation(final Player player) {
Bukkit.getScheduler().scheduleSyncDelayedTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
EntityPlayer nmsPlayer = ((CraftPlayer) player).getHandle();
List<Packet> packets = new ArrayList<>();
for (EnumItemSlot slot : EnumItemSlot.values()) {
packets.add(new PacketPlayOutEntityEquipment(nmsPlayer.getId(), slot,
net.minecraft.server.ItemStack.a)); // Removes armor, otherwise, a client-side glitch makes items
}
packets.add(PacketUtils.createMetadataPacket(nmsPlayer.getId(), Watchers.getHealth(0)));
packets.add(new PacketPlayOutEntityStatus(nmsPlayer, (byte) 3));
for (Player online : Bukkit.getOnlinePlayers()) {
if (!online.equals(player)){
for (Packet packet : packets) {
PacketUtils.sendPacket(online, packet);
}
}
}
}
}, 1L);
}
public void sendArmorStandPacket(Player player) {
EntityPlayer nmsPlayer = ((CraftPlayer) player).getHandle();
Location loc = player.getLocation();
PacketPlayOutSpawnEntityLiving spawnPacket = new PacketPlayOutSpawnEntityLiving(
Integer.MAX_VALUE, UUID.randomUUID(), // Entity id and Entity UUID
30, // Entity type id (ArmorStand)
loc.getX(), loc.getY() - 1.1D, loc.getZ(),// X, Y and Z Position
0, 0, 0, // X, Y and Z Motion
(byte)2, (byte)0, (byte)2, // Yaw, Pitch and Head Pitch
Watchers.toList(Watchers.INVISIBLE) // Metadata
);
PacketUtils.sendPacket(player, spawnPacket);
// Create a packet to send 0 max health attribute, so that health doesn't display
PacketUtils.sendPacket(player, PacketUtils.createHealthAttribute(Integer.MAX_VALUE));
PacketUtils.sendPacket(player, new PacketPlayOutMount(Integer.MAX_VALUE, nmsPlayer.getId()));
player.sendMessage(ChatMessageType.ACTION_BAR, new TextComponent(""));
}
public void destroyArmorStandPacket(Player player) {
PacketUtils.sendPacket(player, new PacketPlayOutEntityDestroy(Integer.MAX_VALUE));
}
private void respawnPlayer(final Player player, boolean teleport) {
UUID uuid = player.getUniqueId();
if (!teleport) {
this.deadPlayers.remove(uuid);
this.hasLeftClicked.remove(uuid);
Players.resetPlayer(player);
setBlood(player, false);
destroyArmorStandPacket(player);
CardinalSpawnEvent respawnEvent = new CardinalSpawnEvent(player, Teams.getTeamByPlayer(player).get());
GameHandler.getGameHandler().getPlugin().getServer().getPluginManager().callEvent(respawnEvent);
PlayerNameUpdateEvent nameUpdateEvent = new PlayerNameUpdateEvent(player);
Bukkit.getServer().getPluginManager().callEvent(nameUpdateEvent);
return;
}
if (playerCanRespawn(player)) {
this.deadPlayers.remove(uuid);
this.hasLeftClicked.remove(uuid);
} else {
if (!deadPlayers.containsKey(uuid)) {
deadPlayers.put(uuid, 0L);
hasLeftClicked.add(uuid);
Players.setObserver(player);
player.showTitle(new TextComponent(""), new TextComponent(""), 0, Integer.MAX_VALUE, 0);
GameHandler.getGameHandler().getMatch().getModules().getModule(Visibility.class).showOrHide(player);
}
return;
}
setBlood(player, false);
destroyArmorStandPacket(player);
player.setTitleTimes(0, 0, 10);
CardinalSpawnEvent respawnEvent = new CardinalSpawnEvent(player, Teams.getTeamByPlayer(player).get());
GameHandler.getGameHandler().getPlugin().getServer().getPluginManager().callEvent(respawnEvent);
PlayerNameUpdateEvent nameUpdateEvent = new PlayerNameUpdateEvent(player);
Bukkit.getServer().getPluginManager().callEvent(nameUpdateEvent);
if (this.bed && player.getBedSpawnLocation() != null) {
player.teleport(player.getBedSpawnLocation(), PlayerTeleportEvent.TeleportCause.UNKNOWN);
} else {
player.teleport(respawnEvent.getSpawn().getLocation(), PlayerTeleportEvent.TeleportCause.UNKNOWN);
}
}
private void killPlayer(final Player player, Player killer, EntityDamageEvent.DamageCause cause) {
if (deadPlayers.containsKey(player.getUniqueId())) return;
this.deadPlayers.put(player.getUniqueId(), System.currentTimeMillis());
CardinalDeathEvent cardinalDeathEvent = new CardinalDeathEvent(player, killer, cause);
Bukkit.getServer().getPluginManager().callEvent(cardinalDeathEvent);
dropInventory(player, true);
PlayerDeathEvent deathEvent = new PlayerDeathEvent(player, new ArrayList<ItemStack>(), player.getExpToLevel(), 0, 0, 0, null);
deathEvent.setDeathMessage(null);
Bukkit.getServer().getPluginManager().callEvent(deathEvent);
if (!isDeadUUID(player.getUniqueId())) return;
Players.setObserver(player);
PlayerNameUpdateEvent nameUpdateEvent = new PlayerNameUpdateEvent(player);
Bukkit.getServer().getPluginManager().callEvent(nameUpdateEvent);
sendTitle(player);
player.setGameMode(GameMode.CREATIVE);
setBlood(player, true);
playDeathAnimation(player);
if (!this.spectate) sendArmorStandPacket(player);
player.addPotionEffect(new PotionEffect(PotionEffectType.CONFUSION, 100, 0, true, false), false);
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, this.blackout ? Integer.MAX_VALUE : 20, 0, true, false), false);
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(Cardinal.getInstance(), new Runnable() {
public void run() {
GameHandler.getGameHandler().getMatch().getModules().getModule(Visibility.class).showOrHide(player);
}
}, 15L);
if (this.auto) hasLeftClicked.add(player.getUniqueId());
}
@EventHandler
public void onMatchStart(MatchStartEvent event) {
for (final Player player : Bukkit.getOnlinePlayers()) {
Optional<TeamModule> team = Teams.getTeamByPlayer(player);
if (!team.isPresent() || !team.get().isObserver()) {
respawnPlayer(player, true);
Bukkit.getScheduler().scheduleSyncDelayedTask(Cardinal.getInstance(), new Runnable() {
@Override
public void run() {
if (TitleRespawn.this.isDeadUUID(player.getUniqueId())) {
player.showTitle(new TextComponent(""), new TextComponent(""), 0, Integer.MAX_VALUE, 0);
getSubtitleLogic();
}
}
}, 1L);
}
}
}
@EventHandler
public void onLocaleChange(PlayerLocaleChangeEvent event) {
if (isDeadUUID(event.getPlayer().getUniqueId()) && deadPlayers.get(event.getPlayer().getUniqueId()) != 0) sendTitle(event.getPlayer());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityDamage(EntityDamageEvent event) {
if (!(event.getEntity() instanceof Player)) return;
Player player = (Player) event.getEntity();
double finalHealth = player.getHealth() - event.getFinalDamage();
if (finalHealth > 0.01) return;
player.setMaxHealth(20);
player.setHealth(player.getMaxHealth());
event.setDamage(1);
player.setLastDamageCause(event);
killPlayer(player, null, event.getCause());
}
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
public void onEntityDamage(EntityDamageByEntityEvent event) {
if (!(event.getEntity() instanceof Player)) return;
Player player = (Player) event.getEntity();
double finalHealth = player.getHealth() - event.getFinalDamage();
if (finalHealth > 0.01) return;
player.setMaxHealth(20);
player.setHealth(player.getMaxHealth());
event.setDamage(1);
player.setLastDamageCause(event);
if (event.getActor() instanceof Player) {
killPlayer(player, (Player)event.getActor(), event.getCause());
} else if (event.getActor() instanceof Projectile && ((Projectile) event.getActor()).getShooter() instanceof Player) {
killPlayer(player, (Player)((Projectile) event.getActor()).getShooter(), event.getCause());
} else {
killPlayer(player, null, event.getCause());
}
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerInteract(PlayerInteractEvent event) {
UUID id = event.getPlayer().getUniqueId();
boolean action = event.getAction() == Action.LEFT_CLICK_AIR || event.getAction() == Action.LEFT_CLICK_BLOCK;
if (isDeadUUID(id)) {
event.setCancelled(true);
if (action && !hasLeftClicked.contains(id)) hasLeftClicked.add(id);
}
}
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
respawnPlayer(event.getPlayer(), true);
}
@EventHandler
public void onMatchEnd(MatchEndEvent event) {
for (Player player : Bukkit.getOnlinePlayers()) {
respawnPlayer(player, false);
}
}
@EventHandler(priority = EventPriority.LOW)
public void onPlayerLeave(PlayerQuitEvent event) {
Optional<TeamModule> team = Teams.getTeamByPlayer(event.getPlayer());
if (!team.isPresent() || !team.get().isObserver()) dropInventory(event.getPlayer());
this.deadPlayers.remove(event.getPlayer().getUniqueId());
this.hasLeftClicked.remove(event.getPlayer().getUniqueId());
}
@EventHandler(priority = EventPriority.HIGH)
public void onPlayerSwitchTeam(PlayerChangeTeamEvent event) {
respawnPlayer(event.getPlayer(), GameHandler.getGameHandler().getMatch().isRunning() && !(event.getNewTeam().get().isObserver() && isDeadUUID(event.getPlayer().getUniqueId())));
}
@EventHandler(priority = EventPriority.LOWEST)
public void onPlayerSwitchTeam2(PlayerChangeTeamEvent event) {
if (!GameHandler.getGameHandler().getMatch().isRunning() || (event.getOldTeam().isPresent() && event.getOldTeam().get().isObserver())) return;
dropInventory(event.getPlayer());
}
@EventHandler
public void onPlayerMove(PlayerTeleportEvent event) {
Player player = event.getPlayer();
if (!this.spectate && (isDeadUUID(player.getUniqueId()) && deadPlayers.get(player.getUniqueId()) != 0)) {
event.setCancelled(true);
}
}
@EventHandler
public void onBucketEmpty(PlayerBucketEmptyEvent event) {
boolean material = event.getBucket().equals(Material.WATER_BUCKET) || event.getBucket().equals(Material.LAVA_BUCKET) || event.getBucket().equals(Material.LAVA) || event.getBucket().equals(Material.WATER) || event.getBucket().equals(Material.AIR);
if (material && isDeadUUID(event.getPlayer().getUniqueId())) {
event.setCancelled(true);
}
}
@EventHandler(priority = EventPriority.HIGHEST)
public void onCycleComplete(CycleCompleteEvent event) {
for (Player player : Bukkit.getOnlinePlayers()) {
respawnPlayer(player, true);
}
}
}