Adds code for winning and losing

Adds methods for triggering a win, a loss and a quit
Calls the win, lose and quit methods as necessay in the listeners
Makes DropperArenaHandler store arena sessions and cleared stages
This commit is contained in:
Kristian Knarvik 2023-03-23 23:13:56 +01:00
parent 62450e8764
commit ee8f232b0b
6 changed files with 239 additions and 39 deletions

View File

@ -81,6 +81,15 @@ public class DropperArena {
this.recordsRegistry = new DropperArenaRecordsRegistry();
}
/**
* Gets the registry keeping track of this arena's records
*
* @return <p>This arena's record registry</p>
*/
public @NotNull DropperArenaRecordsRegistry getRecordsRegistry() {
return this.recordsRegistry;
}
/**
* Gets the name of this arena
*

View File

@ -2,12 +2,17 @@ package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.util.ArenaStorageHelper;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.io.File;
import java.io.IOException;
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.logging.Level;
/**
@ -18,6 +23,44 @@ public class DropperArenaHandler {
private static final File arenaFile = new File(Dropper.getInstance().getDataFolder(), "arenas.yml");
private List<DropperArena> arenas = new ArrayList<>();
private final Set<DropperArenaSession> activeSessions = new HashSet<>();
private final Map<Player, Integer> stagesCleared = new HashMap<>();
/**
* Registers the given arena session to known active arena sessions
*
* @param session <p>The arena session to register</p>
*/
public void registerArenaSession(@NotNull DropperArenaSession session) {
this.activeSessions.add(session);
}
/**
* Unregisters the given arena session from known active arena sessions
*
* @param session <p>The session to remove</p>
* @return <p>True if the session was removed</p>
*/
public boolean unregisterArenaSession(@NotNull DropperArenaSession session) {
return this.activeSessions.remove(session);
}
/**
* Tries to register the given stage as cleared
*
* @param player <p>The player that cleared a stage</p>
* @param stage <p>The stage the player cleared</p>
* @return <p>True if the player cleared a new stage</p>
*/
public boolean registerStageCleared(@NotNull Player player, int stage) {
if ((!stagesCleared.containsKey(player) && stage == 1) || (stagesCleared.containsKey(player) &&
stage == stagesCleared.get(player) + 1)) {
stagesCleared.put(player, stage);
return true;
} else {
return false;
}
}
/**
* Adds a new arena

View File

@ -1,15 +1,25 @@
package net.knarcraft.dropper.arena;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.property.ArenaGameMode;
import net.knarcraft.dropper.property.RecordResult;
import net.knarcraft.dropper.util.PlayerTeleporter;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.logging.Level;
/**
* A representation of a player's current session in a dropper arena
*/
public class DropperArenaSession {
private final DropperArena arena;
private final Player player;
private final ArenaGameMode gameMode;
private final int deaths;
private final @NotNull DropperArena arena;
private final @NotNull Player player;
private final @NotNull ArenaGameMode gameMode;
private final @NotNull Location entryLocation;
private int deaths;
private final long startTime;
/**
@ -26,27 +36,122 @@ public class DropperArenaSession {
this.gameMode = gameMode;
this.deaths = 0;
this.startTime = System.currentTimeMillis();
this.entryLocation = player.getLocation();
// Prevent Spigot interference when traveling at high velocities
player.setAllowFlight(true);
}
/**
* Triggers a win for the player playing in this session
*/
public void triggerWin() {
//TODO: Kick the player from the arena
//TODO: Register the player's record, if applicable, and announce the result
// Remove this session from game sessions to stop listeners from fiddling more with the player
removeSession();
// No longer allow the player to avoid fly checks
player.setAllowFlight(false);
// Check for, and display, records
registerRecord();
//TODO: Give reward?
//TODO: If a staged arena, register the stage as cleared
//TODO: Teleport the player out of the dropper arena
// Register and announce any cleared stages
Integer arenaStage = arena.getStage();
if (arenaStage != null) {
boolean clearedNewStage = Dropper.getInstance().getArenaHandler().registerStageCleared(player, arenaStage);
if (clearedNewStage) {
player.sendMessage("You cleared stage " + arenaStage + "!");
}
}
// Teleport the player out of the arena
teleportToExit();
}
public void triggerLoss() {
switch (gameMode) {
case DEFAULT:
//TODO: Kick the player, and teleport the player away
break;
case LEAST_TIME:
//TODO: Teleport the player back to the top
break;
case LEAST_DEATHS:
//TODO: Add 1 to the death count, and teleport the player back to the top
/**
* Teleports the playing player out of the arena
*/
private void teleportToExit() {
// Teleport the player out of the arena
Location exitLocation;
if (arena.getExitLocation() != null) {
exitLocation = arena.getExitLocation();
} else {
exitLocation = entryLocation;
}
PlayerTeleporter.teleportPlayer(player, exitLocation, true);
}
/**
* Removes this session from current sessions
*/
private void removeSession() {
// Remove this session for game sessions to stop listeners from fiddling more with the player
boolean removedSession = Dropper.getInstance().getArenaHandler().unregisterArenaSession(this);
if (!removedSession) {
Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to remove dropper arena session for " +
player.getName() + ". This will have unintended consequences.");
}
}
/**
* Registers the player's record if necessary, and prints record information to the player
*/
private void registerRecord() {
DropperArenaRecordsRegistry recordsRegistry = arena.getRecordsRegistry();
RecordResult recordResult = switch (gameMode) {
case LEAST_TIME -> recordsRegistry.registerTimeRecord(player,
System.currentTimeMillis() - startTime);
case LEAST_DEATHS -> recordsRegistry.registerDeathRecord(player, deaths);
case DEFAULT -> RecordResult.NONE;
};
switch (recordResult) {
case WORLD_RECORD -> player.sendMessage("You just set a new record for this arena!");
case PERSONAL_BEST -> player.sendMessage("You just got a new personal record!");
}
}
/**
* Triggers a loss for the player playing in this session
*/
public void triggerLoss() {
// Add to the death count if playing the least-deaths game-mode
if (gameMode == ArenaGameMode.LEAST_DEATHS) {
deaths++;
}
//Teleport the player back to the top
PlayerTeleporter.teleportPlayer(player, arena.getSpawnLocation(), true);
}
/**
* Triggers a quit for the player playing in this session
*/
public void triggerQuit() {
// Remove this session from game sessions to stop listeners from fiddling more with the player
removeSession();
// No longer allow the player to avoid fly checks
player.setAllowFlight(false);
// Teleport the player out of the arena
teleportToExit();
}
/**
* Gets the arena this session is being played in
*
* @return <p>The session's arena</p>
*/
public @NotNull DropperArena getArena() {
return this.arena;
}
/**
* Gets the player playing in this session
*
* @return <p>This session's player</p>
*/
public @NotNull Player getPlayer() {
return this.player;
}
}

View File

@ -1,6 +1,7 @@
package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.DropperArenaSession;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -22,15 +23,14 @@ public class DamageListener implements Listener {
Player player = (Player) event.getEntity();
// We don't care about damage outside arenas
if (!Dropper.getInstance().getPlayerRegistry().isInArena(player)) {
DropperArenaSession arenaSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player);
if (arenaSession == null) {
return;
}
event.setCancelled(true);
//TODO: Kick the player from the arena
//TODO: Teleport the player to the location they entered the arena from, or to the spawn
//TODO: Do whatever else should be done for losing players (sending a message?)
arenaSession.triggerLoss();
}
}

View File

@ -1,40 +1,71 @@
package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
import net.knarcraft.dropper.arena.DropperArenaSession;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerMoveEvent;
import org.bukkit.util.Vector;
/**
* A listener for players moving inside a dropper arena
*/
public class MoveListener implements Listener {
/**
* The terminal velocity of a falling player, as defined by the WIKI
*/
private static final double TERMINAL_VELOCITY = 78.4;
@EventHandler
public void onPlayerMove(PlayerMoveEvent event) {
if (!Dropper.getInstance().getPlayerRegistry().isInArena(event.getPlayer())) {
Player player = event.getPlayer();
DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry();
DropperArenaSession arenaSession = playerRegistry.getArenaSession(player);
if (arenaSession == null) {
return;
}
Block targetBlock = event.getTo().getBlock();
Material targetBlockType = targetBlock.getType();
// Hitting water is the trigger for winning
if (targetBlock.getType() == Material.WATER) {
//TODO: Register the win, and teleport the player out
if (targetBlockType == Material.WATER) {
arenaSession.triggerWin();
return;
}
Location targetLocation = targetBlock.getLocation();
Block beneathPlayer = targetLocation.getWorld().getBlockAt(targetLocation.add(0, -0.1, 0));
Material beneathPlayerType = targetLocation.getWorld().getBlockAt(targetLocation.add(0, -0.1, 0)).getType();
// If hitting something which is not air or water, it must be a solid block, and would end in a loss
if (!targetBlock.getType().isAir() || (beneathPlayer.getType() != Material.WATER &&
!beneathPlayer.getType().isAir())) {
//TODO: This that the same as @see{DamageListener#onPlayerDamage}
if (!targetBlockType.isAir() || (beneathPlayerType != Material.WATER &&
!beneathPlayerType.isAir())) {
arenaSession.triggerLoss();
return;
}
//Updates the player's velocity to the one set by the arena
updatePlayerVelocity(arenaSession);
}
/**
* Updates the velocity of the player in the given session
*
* @param session <p>The session to update the velocity for</p>
*/
private void updatePlayerVelocity(DropperArenaSession session) {
Player player = session.getPlayer();
Vector playerVelocity = player.getVelocity();
double arenaVelocity = session.getArena().getPlayerVelocity();
double yVelocity = TERMINAL_VELOCITY * arenaVelocity;
Vector newVelocity = new Vector(playerVelocity.getX(), yVelocity, playerVelocity.getZ());
player.setVelocity(newVelocity);
}
}

View File

@ -1,31 +1,43 @@
package net.knarcraft.dropper.listener;
import net.knarcraft.dropper.Dropper;
import net.knarcraft.dropper.arena.DropperArenaSession;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerTeleportEvent;
/**
* A listener for players leaving the server or the arena
*/
public class PlayerLeaveListener implements Listener {
@EventHandler
public void onPlayerLeave(PlayerQuitEvent event) {
if (!Dropper.getInstance().getPlayerRegistry().isInArena(event.getPlayer())) {
return;
}
//TODO: If in an arena, kick the player.
//TODO: Teleport the player away from the arena. It might only be possible to teleport the player when they join
// again.
triggerQuit(event.getPlayer());
}
@EventHandler
public void onPlayerTeleport(PlayerTeleportEvent event) {
if (!Dropper.getInstance().getPlayerRegistry().isInArena(event.getPlayer())) {
triggerQuit(event.getPlayer());
}
/**
* Forces the given player to quit their current arena
*
* @param player <p>The player to trigger a quit for</p>
*/
private void triggerQuit(Player player) {
DropperArenaSession arenaSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player);
if (arenaSession == null) {
return;
}
//TODO: Treat this the same as onPlayerLeave if the player is in an arena. If the player doesn't change worlds,
// it should be safe to immediately teleport the player to the arena's exit.
//TODO: Because of this, make sure to remove the player from the arena before teleporting the player out
arenaSession.triggerQuit();
//TODO: It might not be possible to alter the player's location here. It might be necessary to move them once
// they join again
}
}