/*
* 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 net.tylermurphy.hideAndSeek.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 net.tylermurphy.hideAndSeek.Main;
import net.tylermurphy.hideAndSeek.configuration.Map;
import net.tylermurphy.hideAndSeek.configuration.Maps;
import net.tylermurphy.hideAndSeek.game.events.Glow;
import net.tylermurphy.hideAndSeek.game.events.Taunt;
import net.tylermurphy.hideAndSeek.game.listener.RespawnHandler;
import net.tylermurphy.hideAndSeek.game.util.*;
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 net.tylermurphy.hideAndSeek.configuration.Config.*;
import static net.tylermurphy.hideAndSeek.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() {
try {
Optional rand = board.getPlayers().stream().skip(new Random().nextInt(board.size())).findFirst();
Player seeker = rand.orElse(board.getPlayers().get(0));
start(seeker);
} catch (Exception e){
Main.getInstance().getLogger().warning("Failed to select random seeker.");
}
}
public void start(Player seeker) {
if (mapSaveEnabled) currentMap.getWorldLoader().rollback();
board.reload();
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 = 30;
}
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<>();
winners.add(board.getFirstSeeker().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, 5*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 {
player.teleport(currentMap.getLobby());
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());
player.getInventory().setContents(data);
}
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 {
player.teleport(exitPosition);
}
}
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;
}
}
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)){
player.teleport(currentMap.getGameSpawn());
}
});
} 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 Map getCurrentMap() {
return currentMap;
}
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 String getGameWorld() {
if(currentMap == null) return null;
else return currentMap.getGameSpawnName();
}
private void checkWinConditions() {
if (board.sizeHider() < 1) {
if (hiderLeft) {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_QUIT"));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_QUIT"));
stop(WinType.NONE);
} else {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_FOUND"));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_HIDERS_FOUND"));
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"));
stop(WinType.NONE);
} else if (gameTimer < 1) {
if (announceMessagesToNonPlayers) Bukkit.broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_TIME"));
else broadcastMessage(gameOverPrefix + message("GAME_GAMEOVER_TIME"));
stop(WinType.HIDER_WIN);
}
hiderLeft = false;
}
}