/*
* This file is part of Kenshins Hide and Seek
*
* Copyright (c) 2020-2021. Tyler Murphy
*
* Kenshins Hide and Seek free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* he Free Software Foundation version 3.
*
* Kenshins Hide and Seek is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
package cat.freya.khs.game;
import com.cryptomorin.xseries.messages.ActionBar;
import com.cryptomorin.xseries.messages.Titles;
import com.google.common.io.ByteArrayDataOutput;
import com.google.common.io.ByteStreams;
import cat.freya.khs.game.events.Glow;
import cat.freya.khs.game.events.Taunt;
import cat.freya.khs.game.listener.RespawnHandler;
import cat.freya.khs.game.util.CountdownDisplay;
import cat.freya.khs.game.util.Status;
import cat.freya.khs.Main;
import cat.freya.khs.configuration.Map;
import cat.freya.khs.configuration.Maps;
import cat.freya.khs.game.util.WinType;
import org.bukkit.*;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import java.util.*;
import java.util.stream.Collectors;
import static cat.freya.khs.configuration.Config.*;
import static cat.freya.khs.configuration.Localization.message;
public class Game {
private final Taunt taunt;
private final Glow glow;
private final Board board;
private Status status;
private Map currentMap;
private int gameTick;
private int lobbyTimer;
private int startingTimer;
private int gameTimer;
private boolean hiderLeft;
public Game(Map map, Board board) {
this.currentMap = map;
this.taunt = new Taunt();
this.glow = new Glow();
this.status = Status.STANDBY;
this.board = board;
this.gameTick = 0;
this.lobbyTimer = -1;
this.startingTimer = -1;
this.gameTimer = 0;
this.hiderLeft = false;
}
public Status getStatus(){
return status;
}
public int getTimeLeft(){
return gameTimer;
}
public int getLobbyTime(){
return lobbyTimer;
}
public Glow getGlow(){
return glow;
}
public Taunt getTaunt(){
return taunt;
}
public void start() {
List seekers = new ArrayList<>(startingSeekerCount);
List pool = board.getPlayers();
for (int i = 0; i < startingSeekerCount; i++) {
try {
int rand = (int)(Math.random() * pool.size());
seekers.add(pool.remove(rand));
} catch (Exception e){
Main.getInstance().getLogger().warning("Failed to select random seeker.");
return;
}
}
start(seekers);
}
public void start(List seekers) {
if (mapSaveEnabled) currentMap.getWorldLoader().rollback();
board.reload();
board.setInitialSeekers(seekers.stream().map(Player::getUniqueId).collect(Collectors.toList()));
seekers.forEach(seeker -> {
board.addSeeker(seeker);
PlayerLoader.loadSeeker(seeker, currentMap);
});
board.getPlayers().forEach(player -> {
if(board.isSeeker(player)) return;
board.addHider(player);
PlayerLoader.loadHider(player, currentMap);
});
board.getPlayers().forEach(board::createGameBoard);
currentMap.getWorldBorder().resetWorldBorder();
if (gameLength > 0) gameTimer = gameLength;
status = Status.STARTING;
startingTimer = hidingTimer;
}
private void stop(WinType type) {
status = Status.ENDING;
List players = board.getPlayers().stream().map(Entity::getUniqueId).collect(Collectors.toList());
if (type == WinType.HIDER_WIN) {
List winners = board.getHiders().stream().map(Entity::getUniqueId).collect(Collectors.toList());
Main.getInstance().getDatabase().getGameData().addWins(board, players, winners, board.getHiderKills(), board.getHiderDeaths(), board.getSeekerKills(), board.getSeekerDeaths(), type);
} else if (type == WinType.SEEKER_WIN) {
List winners = new ArrayList<>();
board.getInitialSeekers().forEach(p -> {
winners.add(p.getUniqueId());
});
if (!waitTillNoneLeft && board.getHiders().size() == 1) {
winners.add(board.getHiders().get(0).getUniqueId());
}
Main.getInstance().getDatabase().getGameData().addWins(board, players, winners, board.getHiderKills(), board.getHiderDeaths(), board.getSeekerKills(), board.getSeekerDeaths(), type);
}
Bukkit.getScheduler().scheduleSyncDelayedTask(Main.getInstance(), this::end, endGameDelay*20);
}
public void end() {
board.getPlayers().forEach(PlayerLoader::unloadPlayer);
currentMap.getWorldBorder().resetWorldBorder();
Map nextMap = Maps.getRandomMap();
if(nextMap != null) this.currentMap = nextMap;
board.getPlayers().forEach(player -> {
if (leaveOnEnd) {
board.removeBoard(player);
board.remove(player);
handleBungeeLeave(player);
} else {
currentMap.getLobby().teleport(player);
board.createLobbyBoard(player);
board.addHider(player);
PlayerLoader.joinPlayer(player, currentMap);
}
});
RespawnHandler.temp_loc.clear();
if (mapSaveEnabled) currentMap.getWorldLoader().unloadMap();
board.reloadLobbyBoards();
status = Status.ENDED;
}
public void join(Player player) {
if (status != Status.STARTING && status != Status.PLAYING) {
if(saveInventory) {
ItemStack[] data = player.getInventory().getContents();
Main.getInstance().getDatabase().getInventoryData().saveInventory(player.getUniqueId(), data);
}
PlayerLoader.joinPlayer(player, currentMap);
board.addHider(player);
board.createLobbyBoard(player);
board.reloadLobbyBoards();
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(messagePrefix + message("GAME_JOIN").addPlayer(player));
else broadcastMessage(messagePrefix + message("GAME_JOIN").addPlayer(player));
} else {
PlayerLoader.loadSpectator(player, currentMap);
board.addSpectator(player);
board.createGameBoard(player);
player.sendMessage(messagePrefix + message("GAME_JOIN_SPECTATOR"));
}
}
public void leave(Player player) {
PlayerLoader.unloadPlayer(player);
if(saveInventory) {
ItemStack[] data = Main.getInstance().getDatabase().getInventoryData().getInventory(player.getUniqueId());
try {
player.getInventory().setContents(data);
} catch (NullPointerException ignored){}
}
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(messagePrefix + message("GAME_LEAVE").addPlayer(player));
else broadcastMessage(messagePrefix + message("GAME_LEAVE").addPlayer(player));
if (board.isHider(player) && status != Status.ENDING && status != Status.STANDBY) {
hiderLeft = true;
}
board.removeBoard(player);
board.remove(player);
if (status == Status.STANDBY) {
board.reloadLobbyBoards();
} else {
board.reloadGameBoards();
board.reloadBoardTeams();
}
handleBungeeLeave(player);
}
@SuppressWarnings("UnstableApiUsage")
private void handleBungeeLeave(Player player) {
if (bungeeLeave) {
ByteArrayDataOutput out = ByteStreams.newDataOutput();
out.writeUTF("Connect");
out.writeUTF(leaveServer);
player.sendPluginMessage(Main.getInstance(), "BungeeCord", out.toByteArray());
} else {
exitPosition.teleport(player);
}
}
public void onTick() {
if (currentMap == null || currentMap.isNotSetup()) return;
if (status == Status.STANDBY) whileWaiting();
else if (status == Status.STARTING) whileStarting();
else if (status == Status.PLAYING) whilePlaying();
gameTick++;
}
private void whileWaiting() {
if (!lobbyCountdownEnabled) return;
if (lobbyMin <= board.size()) {
if (lobbyTimer < 0)
lobbyTimer = countdown;
if (board.size() >= changeCountdown)
lobbyTimer = Math.min(lobbyTimer, 10);
if (gameTick % 20 == 0) {
lobbyTimer--;
board.reloadLobbyBoards();
}
if (lobbyTimer == 0) {
start();
}
} else {
lobbyTimer = -1;
if (gameTick % 20 == 0) {
board.reloadLobbyBoards();
}
}
}
private void whileStarting() {
if(gameTick % 20 == 0) {
if (startingTimer % 5 == 0 || startingTimer < 5) {
String message;
if (startingTimer == 0) {
message = message("START").toString();
status = Status.PLAYING;
board.getPlayers().forEach(player -> {
PlayerLoader.resetPlayer(player, board);
if(board.isSeeker(player)){
currentMap.getGameSpawn().teleport(player);
}
});
} else if (startingTimer == 1){
message = message("START_COUNTDOWN_LAST").addAmount(startingTimer).toString();
} else {
message = message("START_COUNTDOWN").addAmount(startingTimer).toString();
}
board.getPlayers().forEach(player -> {
if (countdownDisplay == CountdownDisplay.CHAT) {
player.sendMessage(messagePrefix + message);
} else if (countdownDisplay == CountdownDisplay.ACTIONBAR) {
ActionBar.clearActionBar(player);
ActionBar.sendActionBar(player, messagePrefix + message);
} else if (countdownDisplay == CountdownDisplay.TITLE && startingTimer != 30) {
Titles.clearTitle(player);
Titles.sendTitle(player, 10, 40, 10, " ", message);
}
});
}
startingTimer--;
}
checkWinConditions();
}
private void whilePlaying() {
for(Player hider : board.getHiders()) {
int distance = 100, temp = 100;
for(Player seeker : board.getSeekers()) {
try {
temp = (int) hider.getLocation().distance(seeker.getLocation());
} catch (Exception e) {
//Players in different worlds, NOT OK!!!
}
if (distance > temp) {
distance = temp;
}
}
if (seekerPing) switch(gameTick %10) {
case 0:
if (distance < seekerPingLevel1) heartbeatSound.play(hider, seekerPingLeadingVolume, seekerPingPitch);
if (distance < seekerPingLevel3) ringingSound.play(hider, seekerPingVolume, seekerPingPitch);
break;
case 3:
if (distance < seekerPingLevel1) heartbeatSound.play(hider, seekerPingVolume, seekerPingPitch);
if (distance < seekerPingLevel3) ringingSound.play(hider, seekerPingVolume, seekerPingPitch);
break;
case 6:
if (distance < seekerPingLevel3) ringingSound.play(hider, seekerPingVolume, seekerPingPitch);
break;
case 9:
if (distance < seekerPingLevel2) ringingSound.play(hider, seekerPingVolume, seekerPingPitch);
break;
}
}
if (gameTick %20 == 0) {
if (gameLength > 0) {
board.reloadGameBoards();
gameTimer--;
}
if (currentMap.isWorldBorderEnabled()) currentMap.getWorldBorder().update();
if (tauntEnabled) taunt.update();
if (glowEnabled || alwaysGlow) glow.update();
}
board.getSpectators().forEach(spectator -> spectator.setFlying(spectator.getAllowFlight()));
checkWinConditions();
}
public void broadcastMessage(String message) {
for(Player player : board.getPlayers()) {
player.sendMessage(message);
}
}
public void broadcastTitle(String title, String subtitle) {
for (Player player : board.getPlayers()) {
Titles.sendTitle(player, 10, 70, 20, title, subtitle);
}
}
public boolean isCurrentMapValid() {
return currentMap != null && !currentMap.isNotSetup();
}
public boolean checkCurrentMap() {
if(currentMap != null && !currentMap.isNotSetup()) return false;
this.currentMap = Maps.getRandomMap();
return this.currentMap == null;
}
public void setCurrentMap(Map map) {
this.currentMap = map;
}
public Map getCurrentMap() {
return currentMap;
}
private void checkWinConditions() {
int hiderCount = board.sizeHider();
if (hiderCount < 1 || (!waitTillNoneLeft && hiderCount < 2)) {
if (hiderLeft && dontRewardQuit) {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_QUIT"));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_QUIT"));
if (gameOverTitle) broadcastTitle(message("GAME_TITLE_NO_WIN").toString(), message("GAME_GAMEOVER_HIDERS_QUIT").toString());
stop(WinType.NONE);
} else {
if (hiderCount < 1 || waitTillNoneLeft) {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_FOUND"));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_FOUND"));
if (gameOverTitle) broadcastTitle(message("GAME_TITLE_SEEKERS_WIN").toString(), message("GAME_GAMEOVER_HIDERS_FOUND").toString());
} else {
Player hider = board.getHiders().get(0);
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_LAST_HIDER").addPlayer(hider));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_LAST_HIDER").addPlayer(hider));
if (gameOverTitle) broadcastTitle(message("GAME_TITLE_SINGLE_HIDER_WIN").addPlayer(hider).toString(), message("GAME_SUBTITLE_SINGLE_HIDER_WIN").addPlayer(hider).toString());
}
stop(WinType.SEEKER_WIN);
}
} else if (board.sizeSeeker() < 1) {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(abortPrefix + message("GAME_GAMEOVER_SEEKERS_QUIT"));
else broadcastMessage(abortPrefix + message("GAME_GAMEOVER_SEEKERS_QUIT"));
if (gameOverTitle) broadcastTitle(message("GAME_TITLE_NO_WIN").toString(), message("GAME_GAMEOVER_SEEKERS_QUIT").toString());
stop(dontRewardQuit ? WinType.NONE : WinType.HIDER_WIN);
} else if (gameTimer < 1) {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_TIME"));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_TIME"));
if (gameOverTitle) broadcastTitle(message("GAME_TITLE_HIDERS_WIN").toString(), message("GAME_GAMEOVER_TIME").toString());
stop(WinType.HIDER_WIN);
}
hiderLeft = false;
}
}