mirror of
https://github.com/SunNetservers/MiniGames.git
synced 2025-06-25 10:44:43 +02:00
Removes deaths and time as separate game-modes, and instead always tracks records. Adds two proper game-modes. One inverts the player's controls, and the other randomly inverts the player's controls every 7 seconds. Saves cleared status and records for each game-mode separately Only allows 0-1 for the horizontal velocity Requires arenas in arena groups to be cleared in sequence on all game-modes, not just the default one.
This commit is contained in:
@ -22,6 +22,7 @@ import net.knarcraft.dropper.listener.CommandListener;
|
||||
import net.knarcraft.dropper.listener.DamageListener;
|
||||
import net.knarcraft.dropper.listener.MoveListener;
|
||||
import net.knarcraft.dropper.listener.PlayerLeaveListener;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
@ -89,6 +90,7 @@ public final class Dropper extends JavaPlugin {
|
||||
ConfigurationSerialization.registerClass(SerializableUUID.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaData.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaGroup.class);
|
||||
ConfigurationSerialization.registerClass(ArenaGameMode.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import net.knarcraft.dropper.util.ArenaStorageHelper;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@ -8,7 +9,8 @@ import org.bukkit.World;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
@ -106,8 +108,13 @@ public class DropperArena {
|
||||
this.exitLocation = null;
|
||||
this.playerVerticalVelocity = 3.92;
|
||||
this.playerHorizontalVelocity = 1;
|
||||
this.dropperArenaData = new DropperArenaData(this.arenaId, new DropperArenaRecordsRegistry(this.arenaId),
|
||||
new HashSet<>());
|
||||
|
||||
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries = new HashMap<>();
|
||||
for (ArenaGameMode arenaGameMode : ArenaGameMode.values()) {
|
||||
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(this.arenaId));
|
||||
}
|
||||
|
||||
this.dropperArenaData = new DropperArenaData(this.arenaId, recordRegistries, new HashMap<>());
|
||||
this.winBlockType = Material.WATER;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.container.SerializableUUID;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -16,34 +17,37 @@ import java.util.UUID;
|
||||
* Data stored for an arena
|
||||
*
|
||||
* @param arenaId <p>The id of the arena this data belongs to</p>
|
||||
* @param recordsRegistry <p>The records belonging to the arena</p>
|
||||
* @param recordRegistries <p>The records belonging to the arena</p>
|
||||
* @param playersCompleted <p>A list of all player that have completed this arena</p>
|
||||
*/
|
||||
public record DropperArenaData(@NotNull UUID arenaId, @NotNull DropperArenaRecordsRegistry recordsRegistry,
|
||||
@NotNull Set<UUID> playersCompleted) implements ConfigurationSerializable {
|
||||
public record DropperArenaData(@NotNull UUID arenaId,
|
||||
@NotNull Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries,
|
||||
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) implements ConfigurationSerializable {
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena data object
|
||||
*
|
||||
* @param arenaId <p>The id of the arena this data belongs to</p>
|
||||
* @param recordsRegistry <p>The registry of this arena's records</p>
|
||||
* @param playersCompleted <p>The set of ids for players that have cleared this data's arena</p>
|
||||
* @param recordRegistries <p>The registries of this arena's records</p>
|
||||
* @param playersCompleted <p>The set of the players that have cleared this arena for each game-mode</p>
|
||||
*/
|
||||
public DropperArenaData(@NotNull UUID arenaId, @NotNull DropperArenaRecordsRegistry recordsRegistry,
|
||||
@NotNull Set<UUID> playersCompleted) {
|
||||
public DropperArenaData(@NotNull UUID arenaId,
|
||||
@NotNull Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries,
|
||||
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) {
|
||||
this.arenaId = arenaId;
|
||||
this.recordsRegistry = recordsRegistry;
|
||||
this.playersCompleted = new HashSet<>(playersCompleted);
|
||||
this.recordRegistries = recordRegistries;
|
||||
this.playersCompleted = new HashMap<>(playersCompleted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the given player has cleared this arena
|
||||
*
|
||||
* @param player <p>The player to check</p>
|
||||
* @param arenaGameMode <p>The game-mode to check for</p>
|
||||
* @param player <p>The player to check</p>
|
||||
* @return <p>True if the player has cleared the arena this data belongs to</p>
|
||||
*/
|
||||
public boolean hasNotCompleted(@NotNull Player player) {
|
||||
return !this.playersCompleted.contains(player.getUniqueId());
|
||||
public boolean hasNotCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
return !this.playersCompleted.getOrDefault(arenaGameMode, new HashSet<>()).contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,8 +55,13 @@ public record DropperArenaData(@NotNull UUID arenaId, @NotNull DropperArenaRecor
|
||||
*
|
||||
* @param player <p>The player that completed this data's arena</p>
|
||||
*/
|
||||
public boolean addCompleted(@NotNull Player player) {
|
||||
boolean added = this.playersCompleted.add(player.getUniqueId());
|
||||
public boolean addCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
// Make sure to add an empty set to prevent a NullPointerException
|
||||
if (!this.playersCompleted.containsKey(arenaGameMode)) {
|
||||
this.playersCompleted.put(arenaGameMode, new HashSet<>());
|
||||
}
|
||||
|
||||
boolean added = this.playersCompleted.get(arenaGameMode).add(player.getUniqueId());
|
||||
// Persistently save the completion
|
||||
if (added) {
|
||||
Dropper.getInstance().getArenaHandler().saveData(this.arenaId);
|
||||
@ -65,13 +74,18 @@ public record DropperArenaData(@NotNull UUID arenaId, @NotNull DropperArenaRecor
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("arenaId", new SerializableUUID(this.arenaId));
|
||||
data.put("recordsRegistry", this.recordsRegistry);
|
||||
data.put("recordsRegistry", this.recordRegistries);
|
||||
|
||||
Set<SerializableUUID> playersCompleted = new HashSet<>();
|
||||
for (UUID playerCompleted : this.playersCompleted) {
|
||||
playersCompleted.add(new SerializableUUID(playerCompleted));
|
||||
// Convert normal UUIDs to serializable UUIDs
|
||||
Map<ArenaGameMode, Set<SerializableUUID>> serializablePlayersCompleted = new HashMap<>();
|
||||
for (ArenaGameMode arenaGameMode : this.playersCompleted.keySet()) {
|
||||
Set<SerializableUUID> playersCompleted = new HashSet<>();
|
||||
for (UUID playerCompleted : this.playersCompleted.get(arenaGameMode)) {
|
||||
playersCompleted.add(new SerializableUUID(playerCompleted));
|
||||
}
|
||||
serializablePlayersCompleted.put(arenaGameMode, playersCompleted);
|
||||
}
|
||||
data.put("playersCompleted", playersCompleted);
|
||||
data.put("playersCompleted", serializablePlayersCompleted);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -84,13 +98,21 @@ public record DropperArenaData(@NotNull UUID arenaId, @NotNull DropperArenaRecor
|
||||
@SuppressWarnings({"unused", "unchecked"})
|
||||
public static @NotNull DropperArenaData deserialize(@NotNull Map<String, Object> data) {
|
||||
SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
|
||||
DropperArenaRecordsRegistry recordsRegistry = (DropperArenaRecordsRegistry) data.get("recordsRegistry");
|
||||
Set<SerializableUUID> playersCompletedData = (Set<SerializableUUID>) data.get("playersCompleted");
|
||||
Set<UUID> playersCompleted = new HashSet<>();
|
||||
for (SerializableUUID completedId : playersCompletedData) {
|
||||
playersCompleted.add(completedId.uuid());
|
||||
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordsRegistry =
|
||||
(Map<ArenaGameMode, DropperArenaRecordsRegistry>) data.get("recordsRegistry");
|
||||
Map<ArenaGameMode, Set<SerializableUUID>> playersCompletedData =
|
||||
(Map<ArenaGameMode, Set<SerializableUUID>>) data.get("playersCompleted");
|
||||
|
||||
// Convert the serializable UUIDs to normal UUIDs
|
||||
Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
|
||||
for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
|
||||
Set<UUID> playersCompleted = new HashSet<>();
|
||||
for (SerializableUUID completedId : playersCompletedData.get(arenaGameMode)) {
|
||||
playersCompleted.add(completedId.uuid());
|
||||
}
|
||||
allPlayersCompleted.put(arenaGameMode, playersCompleted);
|
||||
}
|
||||
return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, playersCompleted);
|
||||
return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, allPlayersCompleted);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.container.SerializableUUID;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import net.knarcraft.dropper.util.ArenaStorageHelper;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.entity.Player;
|
||||
@ -117,12 +118,13 @@ public class DropperArenaGroup implements ConfigurationSerializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given player has beaten all arenas in this group
|
||||
* Checks whether the given player has beaten all arenas in this group on the given game-mode
|
||||
*
|
||||
* @param player <p>The player to check</p>
|
||||
* @param gameMode <p>The game-mode to check</p>
|
||||
* @param player <p>The player to check</p>
|
||||
* @return <p>True if the player has beaten all arenas, false otherwise</p>
|
||||
*/
|
||||
public boolean hasBeatenAll(Player player) {
|
||||
public boolean hasBeatenAll(ArenaGameMode gameMode, Player player) {
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
for (UUID anArenaId : this.getArenas()) {
|
||||
DropperArena dropperArena = arenaHandler.getArena(anArenaId);
|
||||
@ -133,7 +135,7 @@ public class DropperArenaGroup implements ConfigurationSerializable {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dropperArena.getData().hasNotCompleted(player)) {
|
||||
if (dropperArena.getData().hasNotCompleted(gameMode, player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -141,14 +143,15 @@ public class DropperArenaGroup implements ConfigurationSerializable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the given player can play the given arena part of this group
|
||||
* Gets whether the given player can play the given arena part of this group, on the given game-mode
|
||||
*
|
||||
* @param player <p>The player to check</p>
|
||||
* @param arenaId <p>The id of the arena in this group to check</p>
|
||||
* @param gameMode <p>The game-mode the player is trying to play</p>
|
||||
* @param player <p>The player to check</p>
|
||||
* @param arenaId <p>The id of the arena in this group to check</p>
|
||||
* @return <p>True if the player is allowed to play the arena</p>
|
||||
* @throws IllegalArgumentException <p>If checking an arena not in this group</p>
|
||||
*/
|
||||
public boolean canPlay(Player player, UUID arenaId) throws IllegalArgumentException {
|
||||
public boolean canPlay(ArenaGameMode gameMode, Player player, UUID arenaId) throws IllegalArgumentException {
|
||||
if (!this.arenas.contains(arenaId)) {
|
||||
throw new IllegalArgumentException("Cannot check for playability for arena not in this group!");
|
||||
}
|
||||
@ -170,7 +173,7 @@ public class DropperArenaGroup implements ConfigurationSerializable {
|
||||
}
|
||||
|
||||
// This is a lower-numbered arena the player has yet to complete
|
||||
if (dropperArena.getData().hasNotCompleted(player)) {
|
||||
if (dropperArena.getData().hasNotCompleted(gameMode, player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -37,11 +37,20 @@ public class DropperArenaSession {
|
||||
this.deaths = 0;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
|
||||
this.entryState = new PlayerEntryState(player);
|
||||
this.entryState = new PlayerEntryState(player, gameMode);
|
||||
// Make the player fly to improve mobility in the air
|
||||
this.entryState.setArenaState(this.arena.getPlayerHorizontalVelocity());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the game-mode the player is playing in this session
|
||||
*
|
||||
* @return <p>The game-mode for this session</p>
|
||||
*/
|
||||
public @NotNull ArenaGameMode getGameMode() {
|
||||
return this.gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the state of the player when they joined the session
|
||||
*
|
||||
@ -62,7 +71,7 @@ public class DropperArenaSession {
|
||||
registerRecord();
|
||||
|
||||
// Mark the arena as cleared
|
||||
if (this.arena.getData().addCompleted(this.player)) {
|
||||
if (this.arena.getData().addCompleted(this.gameMode, this.player)) {
|
||||
this.player.sendMessage("You cleared the arena!");
|
||||
}
|
||||
this.player.sendMessage("You won!");
|
||||
@ -101,27 +110,44 @@ public class DropperArenaSession {
|
||||
* Registers the player's record if necessary, and prints record information to the player
|
||||
*/
|
||||
private void registerRecord() {
|
||||
DropperArenaRecordsRegistry recordsRegistry = this.arena.getData().recordsRegistry();
|
||||
RecordResult recordResult = switch (this.gameMode) {
|
||||
case LEAST_TIME -> recordsRegistry.registerTimeRecord(this.player.getUniqueId(),
|
||||
System.currentTimeMillis() - this.startTime);
|
||||
case LEAST_DEATHS -> recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths);
|
||||
case DEFAULT -> RecordResult.NONE;
|
||||
};
|
||||
switch (recordResult) {
|
||||
case WORLD_RECORD -> this.player.sendMessage("You just set a new record for this arena!");
|
||||
case PERSONAL_BEST -> this.player.sendMessage("You just got a new personal record!");
|
||||
DropperArenaRecordsRegistry recordsRegistry = this.arena.getData().recordRegistries().get(this.gameMode);
|
||||
long timeElapsed = System.currentTimeMillis() - this.startTime;
|
||||
announceRecord(recordsRegistry.registerTimeRecord(this.player.getUniqueId(), timeElapsed), "time");
|
||||
announceRecord(recordsRegistry.registerDeathRecord(this.player.getUniqueId(), this.deaths), "least deaths");
|
||||
}
|
||||
|
||||
/**
|
||||
* Announces a record set by this player
|
||||
*
|
||||
* @param recordResult <p>The result of the record</p>
|
||||
* @param type <p>The type of record set (time or deaths)</p>
|
||||
*/
|
||||
private void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) {
|
||||
if (recordResult == RecordResult.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gets a string representation of the played game-mode
|
||||
String gameModeString = switch (this.gameMode) {
|
||||
case DEFAULT -> "default";
|
||||
case INVERTED -> "inverted";
|
||||
case RANDOM_INVERTED -> "random";
|
||||
};
|
||||
|
||||
String recordString = "You just set a %s on the %s game-mode!";
|
||||
recordString = switch (recordResult) {
|
||||
case WORLD_RECORD -> String.format(recordString, "new %s record", gameModeString);
|
||||
case PERSONAL_BEST -> String.format(recordString, "personal %s record", gameModeString);
|
||||
default -> throw new IllegalStateException("Unexpected value: " + recordResult);
|
||||
};
|
||||
player.sendMessage(String.format(recordString, type));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (this.gameMode == ArenaGameMode.LEAST_DEATHS) {
|
||||
this.deaths++;
|
||||
}
|
||||
this.deaths++;
|
||||
//Teleport the player back to the top
|
||||
PlayerTeleporter.teleportPlayer(this.player, this.arena.getSpawnLocation(), true, false);
|
||||
this.entryState.setArenaState(this.arena.getPlayerHorizontalVelocity());
|
||||
|
@ -1,8 +1,10 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The state of a player before entering a dropper arena
|
||||
@ -17,13 +19,14 @@ public class PlayerEntryState {
|
||||
private final boolean originalAllowFlight;
|
||||
private final boolean originalInvulnerable;
|
||||
private final boolean originalIsSwimming;
|
||||
private final ArenaGameMode arenaGameMode;
|
||||
|
||||
/**
|
||||
* Instantiates a new player state
|
||||
*
|
||||
* @param player <p>The player whose state should be stored</p>
|
||||
*/
|
||||
public PlayerEntryState(Player player) {
|
||||
public PlayerEntryState(@NotNull Player player, @NotNull ArenaGameMode arenaGameMode) {
|
||||
this.player = player;
|
||||
this.entryLocation = player.getLocation().clone();
|
||||
this.originalFlySpeed = player.getFlySpeed();
|
||||
@ -32,6 +35,7 @@ public class PlayerEntryState {
|
||||
this.originalAllowFlight = player.getAllowFlight();
|
||||
this.originalInvulnerable = player.isInvulnerable();
|
||||
this.originalIsSwimming = player.isSwimming();
|
||||
this.arenaGameMode = arenaGameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -42,9 +46,15 @@ public class PlayerEntryState {
|
||||
public void setArenaState(float horizontalVelocity) {
|
||||
this.player.setAllowFlight(true);
|
||||
this.player.setFlying(true);
|
||||
this.player.setFlySpeed(horizontalVelocity);
|
||||
this.player.setGameMode(GameMode.ADVENTURE);
|
||||
this.player.setSwimming(false);
|
||||
|
||||
// If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
|
||||
if (arenaGameMode == ArenaGameMode.INVERTED) {
|
||||
this.player.setFlySpeed(-horizontalVelocity);
|
||||
} else {
|
||||
this.player.setFlySpeed(horizontalVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -120,7 +120,7 @@ public class EditArenaCommand implements CommandExecutor {
|
||||
}
|
||||
|
||||
// If outside bonds, choose the most extreme value
|
||||
return Math.min(Math.max(-1, velocity), 1);
|
||||
return Math.min(Math.max(0.1f, velocity), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -76,9 +76,10 @@ public class JoinArenaCommand implements CommandExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the player has beaten the arena once before playing a challenge mode
|
||||
if (gameMode != ArenaGameMode.DEFAULT && specifiedArena.getData().hasNotCompleted(player)) {
|
||||
player.sendMessage("You must complete this arena in normal mode before starting a challenge!");
|
||||
// Make sure the player has beaten the arena once in normal mode before playing another mode
|
||||
if (gameMode != ArenaGameMode.DEFAULT &&
|
||||
specifiedArena.getData().hasNotCompleted(ArenaGameMode.DEFAULT, player)) {
|
||||
player.sendMessage("You must complete this arena in normal mode first!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -112,18 +113,20 @@ public class JoinArenaCommand implements CommandExecutor {
|
||||
*/
|
||||
private boolean doGroupChecks(@NotNull DropperArena dropperArena, @NotNull DropperArenaGroup arenaGroup,
|
||||
@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
if (arenaGameMode == ArenaGameMode.DEFAULT) {
|
||||
if (!arenaGroup.canPlay(player, dropperArena.getArenaId())) {
|
||||
player.sendMessage("You have not yet beaten the previous arena!");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (arenaGroup.hasBeatenAll(player)) {
|
||||
// Require that players beat all arenas in the group in the normal game-mode before trying challenge modes
|
||||
if (arenaGameMode != ArenaGameMode.DEFAULT) {
|
||||
if (!arenaGroup.hasBeatenAll(ArenaGameMode.DEFAULT, player)) {
|
||||
player.sendMessage("You have not yet beaten all arenas in this group!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Require that the player has beaten the previous arena on the same game-mode before trying this one
|
||||
if (!arenaGroup.canPlay(arenaGameMode, player, dropperArena.getArenaId())) {
|
||||
player.sendMessage("You have not yet beaten the previous arena!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -23,8 +23,8 @@ public class JoinArenaTabCompleter implements TabCompleter {
|
||||
} else if (arguments.length == 2) {
|
||||
List<String> gameModes = new ArrayList<>();
|
||||
gameModes.add("default");
|
||||
gameModes.add("deaths");
|
||||
gameModes.add("time");
|
||||
gameModes.add("inverted");
|
||||
gameModes.add("random");
|
||||
return gameModes;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
|
@ -3,6 +3,7 @@ package net.knarcraft.dropper.listener;
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
@ -11,7 +12,9 @@ import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
@ -40,6 +43,27 @@ public class MoveListener implements Listener {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Only do block type checking if the block beneath the player changes
|
||||
if (event.getFrom().getBlock() != event.getTo().getBlock() &&
|
||||
checkForSpecialBlock(arenaSession, event.getTo())) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Updates the player's velocity to the one set by the arena
|
||||
updatePlayerVelocity(arenaSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the player in the session is triggering a block with a special significance
|
||||
*
|
||||
* <p>This basically looks for the win block, or whether the player is hitting a solid block.</p>
|
||||
*
|
||||
* @param arenaSession <p>The arena session to check for</p>
|
||||
* @param toLocation <p>The location the player's session is about to hit</p>
|
||||
* @return <p>True if a special block has been hit</p>
|
||||
*/
|
||||
private boolean checkForSpecialBlock(DropperArenaSession arenaSession, Location toLocation) {
|
||||
/* This decides how far inside a non-solid block the player must go before detection triggers. The closer to -1
|
||||
it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit decreases */
|
||||
double liquidDepth = -0.8;
|
||||
@ -47,31 +71,27 @@ public class MoveListener implements Listener {
|
||||
likelihood of detecting the hit decreases, but the immersion increases. */
|
||||
double solidDepth = 0.2;
|
||||
|
||||
// Only do block type checking if the block beneath the player changes
|
||||
if (event.getFrom().getBlock() != event.getTo().getBlock()) {
|
||||
// Check if the player enters water
|
||||
Material winBlockType = arenaSession.getArena().getWinBlockType();
|
||||
// For water, only trigger when the player enters the water, but trigger earlier for everything else
|
||||
double depth = !winBlockType.isSolid() ? liquidDepth : solidDepth;
|
||||
for (Block block : getBlocksBeneathLocation(event.getTo(), depth)) {
|
||||
if (block.getType() == winBlockType) {
|
||||
arenaSession.triggerWin();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the player is about to hit a non-air and non-liquid block
|
||||
for (Block block : getBlocksBeneathLocation(event.getTo(), solidDepth)) {
|
||||
if (!block.getType().isAir() && block.getType() != Material.STRUCTURE_VOID &&
|
||||
block.getType() != Material.WATER && block.getType() != Material.LAVA) {
|
||||
arenaSession.triggerLoss();
|
||||
return;
|
||||
}
|
||||
// Check if the player enters water
|
||||
Material winBlockType = arenaSession.getArena().getWinBlockType();
|
||||
// For water, only trigger when the player enters the water, but trigger earlier for everything else
|
||||
double depth = !winBlockType.isSolid() ? liquidDepth : solidDepth;
|
||||
for (Block block : getBlocksBeneathLocation(toLocation, depth)) {
|
||||
if (block.getType() == winBlockType) {
|
||||
arenaSession.triggerWin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//Updates the player's velocity to the one set by the arena
|
||||
updatePlayerVelocity(arenaSession);
|
||||
// Check if the player is about to hit a non-air and non-liquid block
|
||||
for (Block block : getBlocksBeneathLocation(toLocation, solidDepth)) {
|
||||
if (!block.getType().isAir() && block.getType() != Material.STRUCTURE_VOID &&
|
||||
block.getType() != Material.WATER && block.getType() != Material.LAVA) {
|
||||
arenaSession.triggerLoss();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -95,12 +115,40 @@ public class MoveListener implements Listener {
|
||||
*
|
||||
* @param session <p>The session to update the velocity for</p>
|
||||
*/
|
||||
private void updatePlayerVelocity(DropperArenaSession session) {
|
||||
private void updatePlayerVelocity(@NotNull DropperArenaSession session) {
|
||||
Player player = session.getPlayer();
|
||||
Vector playerVelocity = player.getVelocity();
|
||||
double arenaVelocity = session.getArena().getPlayerVerticalVelocity();
|
||||
Vector newVelocity = new Vector(playerVelocity.getX(), -arenaVelocity, playerVelocity.getZ());
|
||||
player.setVelocity(newVelocity);
|
||||
|
||||
// Toggle the direction of the player's flying, as necessary
|
||||
toggleFlyInversion(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggles the player's flying direction inversion if playing on the random game-mode
|
||||
*
|
||||
* @param session <p>The session to possibly invert flying for</p>
|
||||
*/
|
||||
private void toggleFlyInversion(@NotNull DropperArenaSession session) {
|
||||
if (session.getGameMode() != ArenaGameMode.RANDOM_INVERTED) {
|
||||
return;
|
||||
}
|
||||
Player player = session.getPlayer();
|
||||
float horizontalVelocity = session.getArena().getPlayerHorizontalVelocity();
|
||||
float secondsBetweenToggle = 7;
|
||||
int seconds = Calendar.getInstance().get(Calendar.SECOND);
|
||||
|
||||
/*
|
||||
* A trick to make the inversion change after a customizable amount of seconds
|
||||
* If the quotient of dividing the current number of seconds with the set amount of seconds is even, invert.
|
||||
* So, if the number of seconds between toggles is 5, that would mean for the first 5 seconds, the flying would
|
||||
* be inverted. Once 5 seconds have passed, the quotient becomes 1, which is odd, so the flying is no longer
|
||||
* inverted. After 10 seconds, the quotient is 2, which is even, and inverts the flying.
|
||||
*/
|
||||
boolean invert = Math.floor(seconds / secondsBetweenToggle) % 2 == 0;
|
||||
player.setFlySpeed(invert ? -horizontalVelocity : horizontalVelocity);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,15 @@
|
||||
package net.knarcraft.dropper.property;
|
||||
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A representation of possible arena game-modes
|
||||
*/
|
||||
public enum ArenaGameMode {
|
||||
public enum ArenaGameMode implements ConfigurationSerializable {
|
||||
|
||||
/**
|
||||
* The default game-mode. Failing once throws the player out.
|
||||
@ -13,14 +17,14 @@ public enum ArenaGameMode {
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* The least-deaths game-mode. Player plays until they manage to win. The number of deaths is recorded.
|
||||
* A game-mode where the player's directional buttons are inverted
|
||||
*/
|
||||
LEAST_DEATHS,
|
||||
INVERTED,
|
||||
|
||||
/**
|
||||
* The least-time game-mode. Player plays until they manage to win. The total time of the session is recorded.
|
||||
* A game-mode which swaps between normal and inverted controls on a set schedule of a few seconds
|
||||
*/
|
||||
LEAST_TIME,
|
||||
RANDOM_INVERTED,
|
||||
;
|
||||
|
||||
/**
|
||||
@ -31,13 +35,32 @@ public enum ArenaGameMode {
|
||||
*/
|
||||
public static @NotNull ArenaGameMode matchGamemode(@NotNull String gameMode) {
|
||||
String sanitized = gameMode.trim().toLowerCase();
|
||||
if (sanitized.matches("(least)?deaths?")) {
|
||||
return ArenaGameMode.LEAST_DEATHS;
|
||||
} else if (sanitized.matches("(least)?time")) {
|
||||
return ArenaGameMode.LEAST_TIME;
|
||||
if (sanitized.matches("(invert(ed)?|inverse)")) {
|
||||
return ArenaGameMode.INVERTED;
|
||||
} else if (sanitized.matches("rand(om)?")) {
|
||||
return ArenaGameMode.RANDOM_INVERTED;
|
||||
} else {
|
||||
return ArenaGameMode.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("name", this.name());
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the arena game-mode specified by the given data
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized arena game-mode</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static ArenaGameMode deserialize(Map<String, Object> data) {
|
||||
return ArenaGameMode.valueOf((String) data.get("name"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import net.knarcraft.dropper.arena.DropperArenaGroup;
|
||||
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
|
||||
import net.knarcraft.dropper.container.SerializableMaterial;
|
||||
import net.knarcraft.dropper.container.SerializableUUID;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import net.knarcraft.dropper.property.ArenaStorageKey;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
@ -164,7 +165,12 @@ public final class ArenaStorageHelper {
|
||||
DropperArenaData arenaData = loadArenaData(arenaId);
|
||||
if (arenaData == null) {
|
||||
Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to load arena data for " + arenaId);
|
||||
arenaData = new DropperArenaData(arenaId, new DropperArenaRecordsRegistry(arenaId), new HashSet<>());
|
||||
|
||||
Map<ArenaGameMode, DropperArenaRecordsRegistry> recordRegistries = new HashMap<>();
|
||||
for (ArenaGameMode arenaGameMode : ArenaGameMode.values()) {
|
||||
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(arenaId));
|
||||
}
|
||||
arenaData = new DropperArenaData(arenaId, recordRegistries, new HashMap<>());
|
||||
}
|
||||
|
||||
return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity,
|
||||
|
@ -24,8 +24,8 @@ commands:
|
||||
usage: |
|
||||
/<command> <arena> [mode]
|
||||
Mode can be used to select challenge modes which can be played after beating the arena.
|
||||
deaths = A least-deaths competitive game-mode
|
||||
time = A shortest-time competitive game-mode
|
||||
inverted = A game-mode where the WASD buttons are inverted
|
||||
random = A game-mode where the WASD buttons toggle between being inverted and not
|
||||
description: Used to join a dropper arena
|
||||
dropperleave:
|
||||
aliases:
|
||||
|
Reference in New Issue
Block a user