mirror of
https://github.com/SunNetservers/MiniGames.git
synced 2024-12-05 00:43:15 +01:00
Merge pull request #17 from SunNetservers/dev
Many changes and implementations
This commit is contained in:
commit
fb6550afe7
104
README.md
104
README.md
@ -8,47 +8,93 @@ To modify
|
||||
|
||||
## Permissions
|
||||
|
||||
| Node | Description |
|
||||
|----------------|---------------------------------------------------|
|
||||
| dropper.admin | Gives all permissions |
|
||||
| dropper.join | Allows a player to participate in dropper arenas |
|
||||
| dropper.create | Allows a player to create a new dropper arena |
|
||||
| dropper.edit | Allows a player to edit an existing dropper arena |
|
||||
| dropper.remove | Allows a player to remove a dropper arena |
|
||||
| Node | Description |
|
||||
|----------------|----------------------------------------------------|
|
||||
| dropper.admin | Gives all permissions. |
|
||||
| dropper.join | Allows a player to participate in dropper arenas. |
|
||||
| dropper.create | Allows a player to create a new dropper arena. |
|
||||
| dropper.edit | Allows a player to edit an existing dropper arena. |
|
||||
| dropper.remove | Allows a player to remove a dropper arena. |
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Arguments | Description |
|
||||
|------------------------------|-----------------------------|-------------------------------------------------|
|
||||
| /dropperlist | | Lists available dropper arenas |
|
||||
| [/dropperjoin](#dropperjoin) | \<arena> \[mode] | Joins the selected arena |
|
||||
| /dropperleave | | Leaves the current dropper arena |
|
||||
| /droppercreate | \<name> | Creates a new dropper arena with the given name |
|
||||
| /dropperremove | \<arena> | Removes the specified dropper arena |
|
||||
| [/dropperedit](#dropperedit) | \<arena> \<option> \[value] | Gets or sets a dropper arena option |
|
||||
| /dropperreload | | Reloads all data from disk |
|
||||
| Command | Alias | Arguments | Description |
|
||||
|-----------------------------------------|----------|-----------------------------|-------------------------------------------------------------------------------------|
|
||||
| /dropperList | /dlist | | Lists available dropper arenas. |
|
||||
| [/dropperJoin](#/dropperJoin) | /djoin | \<arena> \[mode] | Joins the selected arena. |
|
||||
| /dropperLeave | /dleave | | Leaves the current dropper arena. |
|
||||
| /dropperCreate | /dcreate | \<name> | Creates a new dropper arena with the given name. The spawn is set to your location. |
|
||||
| /dropperRemove | /dremove | \<arena> | Removes the specified dropper arena. |
|
||||
| [/dropperEdit](#/dropperEdit) | /dedit | \<arena> \<option> \[value] | Gets or sets a dropper arena option. |
|
||||
| /dropperReload | /dreload | | Reloads all data from disk. |
|
||||
| [/dropperGroupSet](#/dropperGroupSet) | /dgset | \<arena> \<group> | Puts the given arena in the given group. Use "none" to remove an existing group. |
|
||||
| /dropperGroupList | /dglist | \[group] | Lists groups, or the stages of a group if a group is specified. |
|
||||
| [/dropperGroupSwap](#/dropperGroupSwap) | /dgswap | \<arena1> \<arena2> | Swaps the two arenas in the group's ordered list. |
|
||||
|
||||
## Command explanation
|
||||
|
||||
### /dropperjoin
|
||||
### /dropperJoin
|
||||
|
||||
This command is used for joining a dropper arena.
|
||||
|
||||
`/droppejoin <arena> [mode]`
|
||||
`/dropperjoin <arena> [mode]`
|
||||
|
||||
| Argument | Usage |
|
||||
|----------|------------------------------------------------------------------------------------------------------------------|
|
||||
| arena | The name of the arena to join |
|
||||
| mode | Additional challenge modes can be played after an arena has been cleared once. Available modes: deaths and time. |
|
||||
| Argument | Usage |
|
||||
|----------|----------------------------------------------------------------------------------------------------------------------|
|
||||
| arena | The name of the arena to join. |
|
||||
| mode | Additional challenge modes can be played after an arena has been cleared once. Available modes: inverted and random. |
|
||||
|
||||
### /dropperedit
|
||||
### /dropperEdit
|
||||
|
||||
This command allows editing the specified property for the specified dropper arena
|
||||
This command allows editing the specified property for the specified dropper arena.
|
||||
|
||||
`/dropperedit <arena> <option> [value]`
|
||||
|
||||
| Argument | Usage |
|
||||
|----------|--------------------------------------|
|
||||
| arena | The name of the arena to edit |
|
||||
| option | The option to display or change |
|
||||
| value | The new value of the selected option |
|
||||
| Argument | Usage |
|
||||
|----------|---------------------------------------|
|
||||
| arena | The name of the arena to edit. |
|
||||
| option | The option to display or change. |
|
||||
| value | The new value of the selected option. |
|
||||
|
||||
These are all the options that can be changed for an arena.
|
||||
|
||||
| Option | Details |
|
||||
|--------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| name | The name of the arena. Used mainly to select the arena in commands. |
|
||||
| spawnLocation | The spawn location of any player joining the arena. Use `56.546,64.0,44.45` to specify coordinates, or `here`, `this` or any other string to select your current location. |
|
||||
| exitLocation | The location players will be sent to when exiting the arena. If not set, the player will be sent to where they joined from. Valid values are the same as for spawnLocation. |
|
||||
| verticalVelocity | The vertical velocity set for players in the arena (basically their falling speed). It must be greater than 0, but max 75. `12.565` and other decimals are allowed. |
|
||||
| horizontalVelocity | The horizontal velocity (technically fly speed) set for players in the arena. It must be between 0 and 1, and cannot be 0. Decimals are allowed. |
|
||||
| winBlockType | The type of block players must hit to win the arena. It can be any material as long as it's a block, and not a type of air. |
|
||||
|
||||
### /dropperGroupSet
|
||||
|
||||
This command is used to set the group of an arena
|
||||
|
||||
`/droppergroupset <arena> <group>`
|
||||
|
||||
Dropper groups are created and removed as necessary. If you specify a group named "potato", that group is created, and
|
||||
will be used again if you specify the "potato" group for another arena. You use "none" or "null" to remove an arena from
|
||||
its group. If the group has no arenas, it will be automatically removed. If the arena already is in a group, it will be
|
||||
moved to the new group.
|
||||
|
||||
### /dropperGroupSwap
|
||||
|
||||
This command is used for changing the order of arenas within a group.
|
||||
|
||||
`/droppergroupswap <arena1> <arena2>`
|
||||
|
||||
Groups define an order the arenas within that group has to be completed in. Use `/droppergrouplist group` to see the
|
||||
actual order of the group. So, assuming your arenas in the group looked something like:
|
||||
|
||||
1. Forest
|
||||
2. Sea
|
||||
3. Nether
|
||||
4. Savanna
|
||||
|
||||
You could use `/droppergroupswap Sea Savanna` to change the order to:
|
||||
|
||||
1. Forest
|
||||
2. Savanna
|
||||
3. Nether
|
||||
4. Sea
|
6
pom.xml
6
pom.xml
@ -73,5 +73,11 @@
|
||||
<version>24.0.1</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>5.9.2</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.knarcraft.dropper;
|
||||
|
||||
import net.knarcraft.dropper.arena.DropperArenaData;
|
||||
import net.knarcraft.dropper.arena.DropperArenaGroup;
|
||||
import net.knarcraft.dropper.arena.DropperArenaHandler;
|
||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
|
||||
@ -8,6 +9,9 @@ import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import net.knarcraft.dropper.command.CreateArenaCommand;
|
||||
import net.knarcraft.dropper.command.EditArenaCommand;
|
||||
import net.knarcraft.dropper.command.EditArenaTabCompleter;
|
||||
import net.knarcraft.dropper.command.GroupListCommand;
|
||||
import net.knarcraft.dropper.command.GroupSetCommand;
|
||||
import net.knarcraft.dropper.command.GroupSwapCommand;
|
||||
import net.knarcraft.dropper.command.JoinArenaCommand;
|
||||
import net.knarcraft.dropper.command.JoinArenaTabCompleter;
|
||||
import net.knarcraft.dropper.command.LeaveArenaCommand;
|
||||
@ -21,6 +25,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;
|
||||
@ -76,6 +81,7 @@ public final class Dropper extends JavaPlugin {
|
||||
public void reload() {
|
||||
// Load all arenas again
|
||||
this.arenaHandler.loadArenas();
|
||||
this.arenaHandler.loadGroups();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -86,6 +92,8 @@ public final class Dropper extends JavaPlugin {
|
||||
ConfigurationSerialization.registerClass(DropperArenaRecordsRegistry.class);
|
||||
ConfigurationSerialization.registerClass(SerializableUUID.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaData.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaGroup.class);
|
||||
ConfigurationSerialization.registerClass(ArenaGameMode.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -95,14 +103,7 @@ public final class Dropper extends JavaPlugin {
|
||||
this.playerRegistry = new DropperArenaPlayerRegistry();
|
||||
this.arenaHandler = new DropperArenaHandler();
|
||||
this.arenaHandler.loadArenas();
|
||||
|
||||
//TODO: Store various information about players' performance, and hook into PlaceholderAPI
|
||||
|
||||
//TODO: Possibly implement an optional queue mode, which only allows one player inside one dropper arena at any
|
||||
// time (to prevent players from pushing each-other)?
|
||||
|
||||
//TODO: Store which players have cleared which arenas to keep track of whether the trial game-modes should be
|
||||
// available
|
||||
this.arenaHandler.loadGroups();
|
||||
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
pluginManager.registerEvents(new DamageListener(), this);
|
||||
@ -110,13 +111,16 @@ public final class Dropper extends JavaPlugin {
|
||||
pluginManager.registerEvents(new PlayerLeaveListener(), this);
|
||||
pluginManager.registerEvents(new CommandListener(), this);
|
||||
|
||||
registerCommand("dropperreload", new ReloadCommand(), null);
|
||||
registerCommand("droppercreate", new CreateArenaCommand(), null);
|
||||
registerCommand("dropperlist", new ListArenaCommand(), null);
|
||||
registerCommand("dropperjoin", new JoinArenaCommand(), new JoinArenaTabCompleter());
|
||||
registerCommand("dropperleave", new LeaveArenaCommand(), null);
|
||||
registerCommand("dropperedit", new EditArenaCommand(), new EditArenaTabCompleter());
|
||||
registerCommand("dropperremove", new RemoveArenaCommand(), new RemoveArenaTabCompleter());
|
||||
registerCommand("dropperReload", new ReloadCommand(), null);
|
||||
registerCommand("dropperCreate", new CreateArenaCommand(), null);
|
||||
registerCommand("dropperList", new ListArenaCommand(), null);
|
||||
registerCommand("dropperJoin", new JoinArenaCommand(), new JoinArenaTabCompleter());
|
||||
registerCommand("dropperLeave", new LeaveArenaCommand(), null);
|
||||
registerCommand("dropperEdit", new EditArenaCommand(), new EditArenaTabCompleter());
|
||||
registerCommand("dropperRemove", new RemoveArenaCommand(), new RemoveArenaTabCompleter());
|
||||
registerCommand("dropperGroupSet", new GroupSetCommand(), null);
|
||||
registerCommand("dropperGroupSwap", new GroupSwapCommand(), null);
|
||||
registerCommand("dropperGroupList", new GroupListCommand(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,11 +1,16 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import net.knarcraft.dropper.util.StringSanitizer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
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;
|
||||
|
||||
/**
|
||||
@ -21,47 +26,42 @@ public class DropperArena {
|
||||
/**
|
||||
* A name used when listing and storing this arena.
|
||||
*/
|
||||
private final @NotNull String arenaName;
|
||||
private @NotNull String arenaName;
|
||||
|
||||
/**
|
||||
* The location players are teleported to when joining this arena.
|
||||
*/
|
||||
private final @NotNull Location spawnLocation;
|
||||
private @NotNull Location spawnLocation;
|
||||
|
||||
/**
|
||||
* The location players will be sent to when they win or lose the arena. If not set, their entry location should be
|
||||
* used instead.
|
||||
*/
|
||||
private final @Nullable Location exitLocation;
|
||||
private @Nullable Location exitLocation;
|
||||
|
||||
/**
|
||||
* The velocity in the y-direction to apply to all players in this arena.
|
||||
*/
|
||||
private final double playerVerticalVelocity;
|
||||
private double playerVerticalVelocity;
|
||||
|
||||
/**
|
||||
* The velocity in the x-direction to apply to all players in this arena
|
||||
*
|
||||
* <p>This is technically the fly speed</p>
|
||||
*/
|
||||
private final float playerHorizontalVelocity;
|
||||
|
||||
/**
|
||||
* The stage number of this arena. If not null, the previous stage number must be cleared before access.
|
||||
*/
|
||||
private final @Nullable Integer stage;
|
||||
private float playerHorizontalVelocity;
|
||||
|
||||
/**
|
||||
* The material of the block players have to hit to win this dropper arena
|
||||
*/
|
||||
private final @NotNull Material winBlockType;
|
||||
private @NotNull Material winBlockType;
|
||||
|
||||
/**
|
||||
* The arena data for this arena
|
||||
*/
|
||||
private final DropperArenaData dropperArenaData;
|
||||
|
||||
//TODO: It should be possible to get records in sorted order (smallest to largest)
|
||||
private static DropperArenaHandler dropperArenaHandler = null;
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena
|
||||
@ -72,23 +72,24 @@ public class DropperArena {
|
||||
* @param exitLocation <p>The location the players are teleported to when exiting the arena, or null</p>
|
||||
* @param playerVerticalVelocity <p>The velocity to use for players' vertical velocity</p>
|
||||
* @param playerHorizontalVelocity <p>The velocity to use for players' horizontal velocity (-1 to 1)</p>
|
||||
* @param stage <p>The stage number of this stage, or null if not limited to stages</p>
|
||||
* @param winBlockType <p>The material of the block players have to hit to win this dropper arena</p>
|
||||
* @param dropperArenaData <p>The arena data keeping track of which players have done what in this arena</p>
|
||||
*/
|
||||
public DropperArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
|
||||
@Nullable Location exitLocation, double playerVerticalVelocity, float playerHorizontalVelocity,
|
||||
@Nullable Integer stage, @NotNull Material winBlockType,
|
||||
@NotNull DropperArenaData dropperArenaData) {
|
||||
@NotNull Material winBlockType, @NotNull DropperArenaData dropperArenaData) {
|
||||
this.arenaId = arenaId;
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = exitLocation;
|
||||
this.playerVerticalVelocity = playerVerticalVelocity;
|
||||
this.playerHorizontalVelocity = playerHorizontalVelocity;
|
||||
this.stage = stage;
|
||||
this.winBlockType = winBlockType;
|
||||
this.dropperArenaData = dropperArenaData;
|
||||
|
||||
if (dropperArenaHandler == null) {
|
||||
dropperArenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -105,11 +106,15 @@ public class DropperArena {
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = null;
|
||||
this.playerVerticalVelocity = 1;
|
||||
this.playerVerticalVelocity = 3.92;
|
||||
this.playerHorizontalVelocity = 1;
|
||||
this.stage = null;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -183,18 +188,6 @@ public class DropperArena {
|
||||
return this.playerHorizontalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the stage this arena belongs to
|
||||
*
|
||||
* <p>It's assumed that arena stages go from 1,2,3,4,... and upwards. If the stage number is set, this arena can
|
||||
* only be played if all previous stages have been beaten. If not set, however, this arena can be used freely.</p>
|
||||
*
|
||||
* @return <p>This arena's stage number</p>
|
||||
*/
|
||||
public @Nullable Integer getStage() {
|
||||
return this.stage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of block a player has to hit to win this arena
|
||||
*
|
||||
@ -204,4 +197,135 @@ public class DropperArena {
|
||||
return this.winBlockType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this arena's sanitized name
|
||||
*
|
||||
* @return <p>This arena's sanitized name</p>
|
||||
*/
|
||||
public @NotNull String getArenaNameSanitized() {
|
||||
return StringSanitizer.sanitizeArenaName(this.getArenaName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the spawn location for this arena
|
||||
*
|
||||
* @param newLocation <p>The new spawn location</p>
|
||||
* @return <p>True if successfully updated</p>
|
||||
*/
|
||||
public boolean setSpawnLocation(@NotNull Location newLocation) {
|
||||
if (isInvalid(newLocation)) {
|
||||
return false;
|
||||
} else {
|
||||
this.spawnLocation = newLocation;
|
||||
dropperArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the exit location for this arena
|
||||
*
|
||||
* @param newLocation <p>The new exit location</p>
|
||||
* @return <p>True if successfully updated</p>
|
||||
*/
|
||||
public boolean setExitLocation(@NotNull Location newLocation) {
|
||||
if (isInvalid(newLocation)) {
|
||||
return false;
|
||||
} else {
|
||||
this.exitLocation = newLocation;
|
||||
dropperArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this arena
|
||||
*
|
||||
* @param arenaName <p>The new name</p>
|
||||
* @return <p>True if successfully updated</p>
|
||||
*/
|
||||
public boolean setName(@NotNull String arenaName) {
|
||||
if (!arenaName.isBlank()) {
|
||||
String oldName = this.getArenaNameSanitized();
|
||||
this.arenaName = arenaName;
|
||||
// Update the arena lookup map to make sure the new name can be used immediately
|
||||
dropperArenaHandler.updateLookupName(oldName, this.getArenaNameSanitized());
|
||||
dropperArenaHandler.saveArenas();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the material of the win block type
|
||||
*
|
||||
* <p>The win block type is the type of block a player must hit to win in this arena</p>
|
||||
*
|
||||
* @param material <p>The material to set for the win block type</p>
|
||||
* @return <p>True if successfully updated</p>
|
||||
*/
|
||||
public boolean setWinBlockType(@NotNull Material material) {
|
||||
if (material.isAir() || !material.isBlock()) {
|
||||
return false;
|
||||
} else {
|
||||
this.winBlockType = material;
|
||||
dropperArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the horizontal velocity of this arena's players
|
||||
*
|
||||
* <p>Note: It's assumed the given value is already bound-checked! (-1 to 1)</p>
|
||||
*
|
||||
* @param horizontalVelocity <p>The horizontal velocity to use</p>
|
||||
* @return <p>True if successfully updated</p>
|
||||
*/
|
||||
public boolean setHorizontalVelocity(float horizontalVelocity) {
|
||||
if (horizontalVelocity < -1 || horizontalVelocity > 1) {
|
||||
return false;
|
||||
} else {
|
||||
this.playerHorizontalVelocity = horizontalVelocity;
|
||||
dropperArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the vertical velocity of this arena's players
|
||||
*
|
||||
* @param verticalVelocity <p>The vertical velocity to use</p>
|
||||
* @return <p>True if successfully updated</p>
|
||||
*/
|
||||
public boolean setVerticalVelocity(double verticalVelocity) {
|
||||
if (verticalVelocity <= 0 || verticalVelocity > 100) {
|
||||
return false;
|
||||
} else {
|
||||
this.playerVerticalVelocity = verticalVelocity;
|
||||
dropperArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof DropperArena otherArena)) {
|
||||
return false;
|
||||
}
|
||||
return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized());
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given location is valid
|
||||
*
|
||||
* @param location <p>The location to validate</p>
|
||||
* @return <p>False if the location is valid</p>
|
||||
*/
|
||||
private boolean isInvalid(Location location) {
|
||||
World world = location.getWorld();
|
||||
return world == null || !world.getWorldBorder().isInside(location);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
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;
|
||||
@ -15,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<SerializableUUID> 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<SerializableUUID> 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 hasCompleted(@NotNull Player player) {
|
||||
return this.playersCompleted.contains(new SerializableUUID(player.getUniqueId()));
|
||||
public boolean hasNotCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
return !this.playersCompleted.getOrDefault(arenaGameMode, new HashSet<>()).contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -50,8 +55,18 @@ public record DropperArenaData(@NotNull UUID arenaId, @NotNull DropperArenaRecor
|
||||
*
|
||||
* @param player <p>The player that completed this data's arena</p>
|
||||
*/
|
||||
public void addCompleted(@NotNull Player player) {
|
||||
this.playersCompleted.add(new SerializableUUID(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);
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@ -59,8 +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("playersCompleted", this.playersCompleted);
|
||||
data.put("recordsRegistry", this.recordRegistries);
|
||||
|
||||
// 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", serializablePlayersCompleted);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -73,9 +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> playersCompleted = (Set<SerializableUUID>) data.get("playersCompleted");
|
||||
return new DropperArenaData(serializableUUID.uuid(), recordsRegistry, playersCompleted);
|
||||
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, allPlayersCompleted);
|
||||
}
|
||||
|
||||
}
|
||||
|
253
src/main/java/net/knarcraft/dropper/arena/DropperArenaGroup.java
Normal file
253
src/main/java/net/knarcraft/dropper/arena/DropperArenaGroup.java
Normal file
@ -0,0 +1,253 @@
|
||||
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.StringSanitizer;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A sorted group of arenas that must be completed in sequence
|
||||
*/
|
||||
public class DropperArenaGroup implements ConfigurationSerializable {
|
||||
|
||||
/**
|
||||
* The unique id for this group of arenas
|
||||
*/
|
||||
private final UUID groupId;
|
||||
|
||||
/**
|
||||
* The unique name for this group of arenas
|
||||
*/
|
||||
private final String groupName;
|
||||
|
||||
/**
|
||||
* The arenas in this group, ordered from stage 1 to stage n
|
||||
*/
|
||||
private final List<UUID> arenas;
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena group
|
||||
*
|
||||
* @param groupName <p>The name of this group</p>
|
||||
*/
|
||||
public DropperArenaGroup(@NotNull String groupName) {
|
||||
this.groupId = UUID.randomUUID();
|
||||
this.groupName = groupName;
|
||||
this.arenas = new ArrayList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena group
|
||||
*
|
||||
* @param groupId <p>The unique id of this group</p>
|
||||
* @param groupName <p>The name of this group</p>
|
||||
* @param arenas <p>The arenas in this group</p>
|
||||
*/
|
||||
private DropperArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
|
||||
this.groupId = groupId;
|
||||
this.groupName = groupName;
|
||||
this.arenas = new ArrayList<>(arenas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of this dropper arena group
|
||||
*
|
||||
* @return <p>The id of this group</p>
|
||||
*/
|
||||
public @NotNull UUID getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this dropper arena group
|
||||
*
|
||||
* @return <p>The name of this group</p>
|
||||
*/
|
||||
public @NotNull String getGroupName() {
|
||||
return this.groupName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arenas contained in this group in the correct order
|
||||
*
|
||||
* @return <p>The ids of the arenas in this group</p>
|
||||
*/
|
||||
public @NotNull List<UUID> getArenas() {
|
||||
return new ArrayList<>(arenas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given dropper arena from this group
|
||||
*
|
||||
* @param arenaId <p>The id of the dropper arena to remove</p>
|
||||
*/
|
||||
public void removeArena(UUID arenaId) {
|
||||
this.arenas.remove(arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an arena to the end of this group
|
||||
*
|
||||
* @param arenaId <p>The arena to add to this group</p>
|
||||
*/
|
||||
public void addArena(UUID arenaId) {
|
||||
addArena(arenaId, this.arenas.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an arena to the end of this group
|
||||
*
|
||||
* @param arenaId <p>The arena to add to this group</p>
|
||||
* @param index <p>The index to put the arena in</p>
|
||||
*/
|
||||
public void addArena(UUID arenaId, int index) {
|
||||
// Make sure we don't have duplicates
|
||||
if (!this.arenas.contains(arenaId)) {
|
||||
this.arenas.add(index, arenaId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given player has beaten all arenas in this group on the given game-mode
|
||||
*
|
||||
* @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(ArenaGameMode gameMode, Player player) {
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
for (UUID anArenaId : this.getArenas()) {
|
||||
DropperArena dropperArena = arenaHandler.getArena(anArenaId);
|
||||
if (dropperArena == null) {
|
||||
// The arena would only be null if the arena has been deleted, but not removed from this group
|
||||
Dropper.getInstance().getLogger().log(Level.WARNING, "The dropper group " + this.getGroupName() +
|
||||
" contains the arena id " + anArenaId + " which is not a valid arena id!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (dropperArena.getData().hasNotCompleted(gameMode, player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the given player can play the given arena part of this group, on the given game-mode
|
||||
*
|
||||
* @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(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!");
|
||||
}
|
||||
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
|
||||
for (UUID anArenaId : this.getArenas()) {
|
||||
// If the target arena is reached, allow, as all previous arenas must have been cleared
|
||||
if (arenaId == anArenaId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DropperArena dropperArena = arenaHandler.getArena(anArenaId);
|
||||
if (dropperArena == null) {
|
||||
// The arena would only be null if the arena has been deleted, but not removed from this group
|
||||
Dropper.getInstance().getLogger().log(Level.WARNING, String.format("The dropper group %s contains the" +
|
||||
" arena id %s which is not a valid arena id!", this.getGroupName(), anArenaId));
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is a lower-numbered arena the player has yet to complete
|
||||
if (dropperArena.getData().hasNotCompleted(gameMode, player)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this group's name, but sanitized
|
||||
*
|
||||
* @return <p>The sanitized group name</p>
|
||||
*/
|
||||
public @NotNull String getGroupNameSanitized() {
|
||||
return StringSanitizer.sanitizeArenaName(this.getGroupName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the arenas at the given indices
|
||||
*
|
||||
* @param index1 <p>The index of the first arena to swap</p>
|
||||
* @param index2 <p>The index of the second arena to swap</p>
|
||||
*/
|
||||
public void swapArenas(int index1, int index2) {
|
||||
// Change nothing if not a valid request
|
||||
if (index1 == index2 || index1 < 0 || index2 < 0 || index1 >= this.arenas.size() ||
|
||||
index2 >= this.arenas.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Swap the two arena ids
|
||||
UUID temporaryValue = this.arenas.get(index2);
|
||||
this.arenas.set(index2, this.arenas.get(index1));
|
||||
this.arenas.set(index1, temporaryValue);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("groupId", new SerializableUUID(this.groupId));
|
||||
data.put("groupName", this.groupName);
|
||||
|
||||
List<SerializableUUID> serializableArenas = new ArrayList<>();
|
||||
for (UUID arenaId : arenas) {
|
||||
serializableArenas.add(new SerializableUUID(arenaId));
|
||||
}
|
||||
data.put("arenas", serializableArenas);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the given data
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized arena group</p>
|
||||
*/
|
||||
@SuppressWarnings({"unused", "unchecked"})
|
||||
public static @NotNull DropperArenaGroup deserialize(@NotNull Map<String, Object> data) {
|
||||
UUID id = ((SerializableUUID) data.get("groupId")).uuid();
|
||||
String name = (String) data.get("groupName");
|
||||
List<SerializableUUID> serializableArenas = (List<SerializableUUID>) data.get("arenas");
|
||||
List<UUID> arenas = new ArrayList<>();
|
||||
for (SerializableUUID arenaId : serializableArenas) {
|
||||
arenas.add(arenaId.uuid());
|
||||
}
|
||||
return new DropperArenaGroup(id, name, arenas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof DropperArenaGroup otherGroup)) {
|
||||
return false;
|
||||
}
|
||||
return this.getGroupNameSanitized().equals(otherGroup.getGroupNameSanitized());
|
||||
}
|
||||
|
||||
}
|
@ -2,13 +2,15 @@ package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.util.ArenaStorageHelper;
|
||||
import org.bukkit.entity.Player;
|
||||
import net.knarcraft.dropper.util.StringSanitizer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -18,22 +20,95 @@ import java.util.logging.Level;
|
||||
public class DropperArenaHandler {
|
||||
|
||||
private Map<UUID, DropperArena> arenas = new HashMap<>();
|
||||
private final Map<Player, Integer> stagesCleared = new HashMap<>();
|
||||
private Map<UUID, DropperArenaGroup> arenaGroups = new HashMap<>();
|
||||
private Map<String, UUID> arenaNameLookup = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Tries to register the given stage as cleared
|
||||
* Gets all arenas that are within a group
|
||||
*
|
||||
* @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>
|
||||
* @return <p>All arenas in a group</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;
|
||||
public @NotNull Set<DropperArena> getArenasInAGroup() {
|
||||
Set<DropperArena> arenas = new HashSet<>();
|
||||
for (UUID arenaId : arenaGroups.keySet()) {
|
||||
arenas.add(this.arenas.get(arenaId));
|
||||
}
|
||||
return arenas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of all dropper groups
|
||||
*
|
||||
* @return <p>All dropper groups</p>
|
||||
*/
|
||||
public Set<DropperArenaGroup> getAllGroups() {
|
||||
return new HashSet<>(arenaGroups.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the group the given arena belongs to
|
||||
*
|
||||
* @param arenaId <p>The id of the arena to get the group of</p>
|
||||
* @return <p>The group the arena belongs to, or null if not in a group</p>
|
||||
*/
|
||||
public @Nullable DropperArenaGroup getGroup(@NotNull UUID arenaId) {
|
||||
return this.arenaGroups.get(arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the group for the given arena
|
||||
*
|
||||
* @param arenaId <p>The id of the arena to change</p>
|
||||
* @param arenaGroup <p>The group to add the arena to, or null to remove the current group</p>
|
||||
*/
|
||||
public void setGroup(@NotNull UUID arenaId, @Nullable DropperArenaGroup arenaGroup) {
|
||||
if (arenaGroup == null) {
|
||||
// No need to remove something non-existing
|
||||
if (!this.arenaGroups.containsKey(arenaId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the existing group
|
||||
DropperArenaGroup oldGroup = this.arenaGroups.remove(arenaId);
|
||||
oldGroup.removeArena(arenaId);
|
||||
} else {
|
||||
return false;
|
||||
// Make sure to remove the arena from the old group's internal tracking
|
||||
if (this.arenaGroups.containsKey(arenaId)) {
|
||||
this.arenaGroups.remove(arenaId).removeArena(arenaId);
|
||||
}
|
||||
|
||||
this.arenaGroups.put(arenaId, arenaGroup);
|
||||
arenaGroup.addArena(arenaId);
|
||||
}
|
||||
saveGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dropper arena group with the given name
|
||||
*
|
||||
* @param groupName <p>The name of the group to get</p>
|
||||
* @return <p>The group, or null if not found</p>
|
||||
*/
|
||||
public @Nullable DropperArenaGroup getGroup(String groupName) {
|
||||
String sanitized = StringSanitizer.sanitizeArenaName(groupName);
|
||||
for (DropperArenaGroup arenaGroup : this.arenaGroups.values()) {
|
||||
if (arenaGroup.getGroupNameSanitized().equals(sanitized)) {
|
||||
return arenaGroup;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces an arena's lookup name
|
||||
*
|
||||
* @param oldName <p>The arena's old sanitized lookup name</p>
|
||||
* @param newName <p>The arena's new sanitized lookup name</p>
|
||||
*/
|
||||
public void updateLookupName(@NotNull String oldName, @NotNull String newName) {
|
||||
UUID arenaId = this.arenaNameLookup.remove(oldName);
|
||||
if (arenaId != null) {
|
||||
this.arenaNameLookup.put(newName, arenaId);
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,9 +119,20 @@ public class DropperArenaHandler {
|
||||
*/
|
||||
public void addArena(@NotNull DropperArena arena) {
|
||||
this.arenas.put(arena.getArenaId(), arena);
|
||||
this.arenaNameLookup.put(arena.getArenaNameSanitized(), arena.getArenaId());
|
||||
this.saveArenas();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena with the given id
|
||||
*
|
||||
* @param arenaId <p>The id of the arena to get</p>
|
||||
* @return <p>The arena, or null if no arena could be found</p>
|
||||
*/
|
||||
public @Nullable DropperArena getArena(@NotNull UUID arenaId) {
|
||||
return this.arenas.get(arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena with the given name
|
||||
*
|
||||
@ -54,13 +140,7 @@ public class DropperArenaHandler {
|
||||
* @return <p>The arena with the given name, or null if not found</p>
|
||||
*/
|
||||
public @Nullable DropperArena getArena(@NotNull String arenaName) {
|
||||
arenaName = ArenaStorageHelper.sanitizeArenaName(arenaName);
|
||||
for (DropperArena arena : arenas.values()) {
|
||||
if (ArenaStorageHelper.sanitizeArenaName(arena.getArenaName()).equals(arenaName)) {
|
||||
return arena;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return this.arenas.get(this.arenaNameLookup.get(StringSanitizer.sanitizeArenaName(arenaName)));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -80,6 +160,8 @@ public class DropperArenaHandler {
|
||||
public void removeArena(@NotNull DropperArena arena) {
|
||||
Dropper.getInstance().getPlayerRegistry().removeForArena(arena);
|
||||
this.arenas.remove(arena.getArenaId());
|
||||
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
|
||||
this.arenaGroups.remove(arena.getArenaId());
|
||||
this.saveArenas();
|
||||
}
|
||||
|
||||
@ -97,6 +179,33 @@ public class DropperArenaHandler {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all current dropper groups to disk
|
||||
*/
|
||||
public void saveGroups() {
|
||||
try {
|
||||
ArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
|
||||
} catch (IOException e) {
|
||||
Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to save current arena groups! " +
|
||||
"Data loss can occur!");
|
||||
Dropper.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all dropper groups from disk
|
||||
*/
|
||||
public void loadGroups() {
|
||||
Set<DropperArenaGroup> arenaGroups = ArenaStorageHelper.loadDropperArenaGroups();
|
||||
Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>();
|
||||
for (DropperArenaGroup arenaGroup : arenaGroups) {
|
||||
for (UUID arenaId : arenaGroup.getArenas()) {
|
||||
arenaGroupMap.put(arenaId, arenaGroup);
|
||||
}
|
||||
}
|
||||
this.arenaGroups = arenaGroupMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all current arenas to disk
|
||||
*/
|
||||
@ -115,6 +224,13 @@ public class DropperArenaHandler {
|
||||
*/
|
||||
public void loadArenas() {
|
||||
this.arenas = ArenaStorageHelper.loadArenas();
|
||||
|
||||
// Save a map from arena name to arena id for improved performance
|
||||
this.arenaNameLookup = new HashMap<>();
|
||||
for (Map.Entry<UUID, DropperArena> arena : this.arenas.entrySet()) {
|
||||
String sanitizedName = arena.getValue().getArenaNameSanitized();
|
||||
this.arenaNameLookup.put(sanitizedName, arena.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,8 +17,8 @@ import java.util.stream.Stream;
|
||||
public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
|
||||
private final UUID arenaId;
|
||||
private final Map<SerializableUUID, Integer> leastDeaths;
|
||||
private final Map<SerializableUUID, Long> shortestTimeMilliSeconds;
|
||||
private final @NotNull Map<UUID, Number> leastDeaths;
|
||||
private final @NotNull Map<UUID, Number> shortestTimeMilliSeconds;
|
||||
|
||||
/**
|
||||
* Instantiates a new empty records registry
|
||||
@ -35,8 +35,8 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
* @param leastDeaths <p>The existing least death records to use</p>
|
||||
* @param shortestTimeMilliSeconds <p>The existing leash time records to use</p>
|
||||
*/
|
||||
public DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Map<SerializableUUID, Integer> leastDeaths,
|
||||
@NotNull Map<SerializableUUID, Long> shortestTimeMilliSeconds) {
|
||||
private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Map<UUID, Integer> leastDeaths,
|
||||
@NotNull Map<UUID, Long> shortestTimeMilliSeconds) {
|
||||
this.arenaId = arenaId;
|
||||
this.leastDeaths = new HashMap<>(leastDeaths);
|
||||
this.shortestTimeMilliSeconds = new HashMap<>(shortestTimeMilliSeconds);
|
||||
@ -47,8 +47,12 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
*
|
||||
* @return <p>Existing death records</p>
|
||||
*/
|
||||
public Map<SerializableUUID, Integer> getLeastDeathsRecords() {
|
||||
return new HashMap<>(this.leastDeaths);
|
||||
public Map<UUID, Integer> getLeastDeathsRecords() {
|
||||
Map<UUID, Integer> leastDeathRecords = new HashMap<>();
|
||||
for (Map.Entry<UUID, Number> entry : this.leastDeaths.entrySet()) {
|
||||
leastDeathRecords.put(entry.getKey(), entry.getValue().intValue());
|
||||
}
|
||||
return leastDeathRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,8 +60,12 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
*
|
||||
* @return <p>Existing time records</p>
|
||||
*/
|
||||
public Map<SerializableUUID, Long> getShortestTimeMilliSecondsRecords() {
|
||||
return new HashMap<>(this.shortestTimeMilliSeconds);
|
||||
public Map<UUID, Long> getShortestTimeMilliSecondsRecords() {
|
||||
Map<UUID, Long> leastTimeRecords = new HashMap<>();
|
||||
for (Map.Entry<UUID, Number> entry : this.shortestTimeMilliSeconds.entrySet()) {
|
||||
leastTimeRecords.put(entry.getKey(), entry.getValue().longValue());
|
||||
}
|
||||
return leastTimeRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,29 +76,7 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
* @return <p>The result explaining what type of record was achieved</p>
|
||||
*/
|
||||
public @NotNull RecordResult registerDeathRecord(@NotNull UUID playerId, int deaths) {
|
||||
RecordResult result;
|
||||
Stream<Map.Entry<SerializableUUID, Integer>> records = leastDeaths.entrySet().stream();
|
||||
SerializableUUID serializableUUID = new SerializableUUID(playerId);
|
||||
|
||||
if (records.allMatch((entry) -> deaths < entry.getValue())) {
|
||||
// If the given value is less than all other values, that's a world record!
|
||||
result = RecordResult.WORLD_RECORD;
|
||||
leastDeaths.put(serializableUUID, deaths);
|
||||
save();
|
||||
} else if (leastDeaths.containsKey(serializableUUID) && deaths < leastDeaths.get(serializableUUID)) {
|
||||
// If the given value is less than the player's previous value, that's a personal best!
|
||||
result = RecordResult.PERSONAL_BEST;
|
||||
leastDeaths.put(serializableUUID, deaths);
|
||||
save();
|
||||
} else {
|
||||
// Make sure to save the record if this is the user's first attempt
|
||||
if (!leastDeaths.containsKey(serializableUUID)) {
|
||||
save();
|
||||
}
|
||||
result = RecordResult.NONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
return registerRecord(leastDeaths, playerId, deaths);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -101,30 +87,7 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
* @return <p>The result explaining what type of record was achieved</p>
|
||||
*/
|
||||
public @NotNull RecordResult registerTimeRecord(@NotNull UUID playerId, long milliseconds) {
|
||||
RecordResult result;
|
||||
Stream<Map.Entry<SerializableUUID, Long>> records = shortestTimeMilliSeconds.entrySet().stream();
|
||||
SerializableUUID serializableUUID = new SerializableUUID(playerId);
|
||||
|
||||
if (records.allMatch((entry) -> milliseconds < entry.getValue())) {
|
||||
//If the given value is less than all other values, that's a world record!
|
||||
result = RecordResult.WORLD_RECORD;
|
||||
shortestTimeMilliSeconds.put(serializableUUID, milliseconds);
|
||||
save();
|
||||
} else if (shortestTimeMilliSeconds.containsKey(serializableUUID) &&
|
||||
milliseconds < shortestTimeMilliSeconds.get(serializableUUID)) {
|
||||
//If the given value is less than the player's previous value, that's a personal best!
|
||||
result = RecordResult.PERSONAL_BEST;
|
||||
shortestTimeMilliSeconds.put(serializableUUID, milliseconds);
|
||||
save();
|
||||
} else {
|
||||
// Make sure to save the record if this is the user's first attempt
|
||||
if (!shortestTimeMilliSeconds.containsKey(serializableUUID)) {
|
||||
save();
|
||||
}
|
||||
result = RecordResult.NONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
return registerRecord(shortestTimeMilliSeconds, playerId, milliseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -134,13 +97,58 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
Dropper.getInstance().getArenaHandler().saveData(this.arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new record if applicable
|
||||
*
|
||||
* @param existingRecords <p>The map of existing records to use</p>
|
||||
* @param playerId <p>The id of the player that potentially achieved a record</p>
|
||||
* @param amount <p>The amount of whatever the player achieved</p>
|
||||
* @return <p>The result of the player's record attempt</p>
|
||||
*/
|
||||
private @NotNull RecordResult registerRecord(@NotNull Map<UUID, Number> existingRecords, @NotNull UUID playerId,
|
||||
Number amount) {
|
||||
RecordResult result;
|
||||
Stream<Map.Entry<UUID, Number>> records = existingRecords.entrySet().stream();
|
||||
long amountLong = amount.longValue();
|
||||
|
||||
if (records.allMatch((entry) -> amountLong < entry.getValue().longValue())) {
|
||||
// If the given value is less than all other values, that's a world record!
|
||||
result = RecordResult.WORLD_RECORD;
|
||||
existingRecords.put(playerId, amount);
|
||||
save();
|
||||
} else if (existingRecords.containsKey(playerId) && amountLong < existingRecords.get(playerId).longValue()) {
|
||||
// If the given value is less than the player's previous value, that's a personal best!
|
||||
result = RecordResult.PERSONAL_BEST;
|
||||
existingRecords.put(playerId, amount);
|
||||
save();
|
||||
} else {
|
||||
// Make sure to save the record if this is the user's first attempt
|
||||
if (!existingRecords.containsKey(playerId)) {
|
||||
save();
|
||||
}
|
||||
result = RecordResult.NONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("arenaId", new SerializableUUID(this.arenaId));
|
||||
data.put("leastDeaths", this.leastDeaths);
|
||||
data.put("shortestTime", this.shortestTimeMilliSeconds);
|
||||
|
||||
Map<SerializableUUID, Number> leastDeaths = new HashMap<>();
|
||||
for (Map.Entry<UUID, Number> entry : this.leastDeaths.entrySet()) {
|
||||
leastDeaths.put(new SerializableUUID(entry.getKey()), entry.getValue());
|
||||
}
|
||||
data.put("leastDeaths", leastDeaths);
|
||||
|
||||
Map<SerializableUUID, Number> shortestTimeMilliSeconds = new HashMap<>();
|
||||
for (Map.Entry<UUID, Number> entry : this.shortestTimeMilliSeconds.entrySet()) {
|
||||
shortestTimeMilliSeconds.put(new SerializableUUID(entry.getKey()), entry.getValue());
|
||||
}
|
||||
data.put("shortestTime", shortestTimeMilliSeconds);
|
||||
return data;
|
||||
}
|
||||
|
||||
@ -155,10 +163,19 @@ public class DropperArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
UUID arenaId = ((SerializableUUID) data.get("arenaId")).uuid();
|
||||
Map<SerializableUUID, Integer> leastDeathsData =
|
||||
(Map<SerializableUUID, Integer>) data.getOrDefault("leastDeaths", new HashMap<>());
|
||||
Map<SerializableUUID, Long> shortestTimeMillisecondsData =
|
||||
(Map<SerializableUUID, Long>) data.getOrDefault("shortestTime", new HashMap<>());
|
||||
Map<UUID, Integer> leastDeaths = new HashMap<>();
|
||||
for (Map.Entry<SerializableUUID, Integer> entry : leastDeathsData.entrySet()) {
|
||||
leastDeaths.put(entry.getKey().uuid(), entry.getValue());
|
||||
}
|
||||
|
||||
return new DropperArenaRecordsRegistry(arenaId, leastDeathsData, shortestTimeMillisecondsData);
|
||||
Map<SerializableUUID, Number> shortestTimeMillisecondsData =
|
||||
(Map<SerializableUUID, Number>) data.getOrDefault("shortestTime", new HashMap<>());
|
||||
Map<UUID, Long> shortestTimeMilliseconds = new HashMap<>();
|
||||
for (Map.Entry<SerializableUUID, Number> entry : shortestTimeMillisecondsData.entrySet()) {
|
||||
shortestTimeMilliseconds.put(entry.getKey().uuid(), entry.getValue().longValue());
|
||||
}
|
||||
|
||||
return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
*
|
||||
@ -61,17 +70,10 @@ public class DropperArenaSession {
|
||||
// Check for, and display, records
|
||||
registerRecord();
|
||||
|
||||
//TODO: Give reward?
|
||||
|
||||
// Register and announce any cleared stages
|
||||
Integer arenaStage = this.arena.getStage();
|
||||
if (arenaStage != null) {
|
||||
boolean clearedNewStage = Dropper.getInstance().getArenaHandler().registerStageCleared(this.player, arenaStage);
|
||||
if (clearedNewStage) {
|
||||
this.player.sendMessage("You cleared stage " + arenaStage + "!");
|
||||
}
|
||||
// Mark the arena as cleared
|
||||
if (this.arena.getData().addCompleted(this.gameMode, this.player)) {
|
||||
this.player.sendMessage("You cleared the arena!");
|
||||
}
|
||||
|
||||
this.player.sendMessage("You won!");
|
||||
|
||||
// Teleport the player out of the arena
|
||||
@ -108,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,21 +19,23 @@ 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();
|
||||
this.entryLocation = player.getLocation().clone();
|
||||
this.originalFlySpeed = player.getFlySpeed();
|
||||
this.originalIsFlying = player.isFlying();
|
||||
this.originalGameMode = player.getGameMode();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@ package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.util.StringSanitizer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -26,15 +27,20 @@ public class CreateArenaCommand implements CommandExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArena existingArena = Dropper.getInstance().getArenaHandler().getArena(arguments[0]);
|
||||
// Remove known characters that are likely to cause trouble if used in an arena name
|
||||
String arenaName = StringSanitizer.removeUnwantedCharacters(arguments[0]);
|
||||
|
||||
// An arena name is required
|
||||
if (arenaName.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArena existingArena = Dropper.getInstance().getArenaHandler().getArena(arenaName);
|
||||
if (existingArena != null) {
|
||||
commandSender.sendMessage("There already exists a dropper arena with that name!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove known characters that are likely to cause trouble if used in an arena name
|
||||
String arenaName = arguments[0].replaceAll("[§ :=&]", "");
|
||||
|
||||
DropperArena arena = new DropperArena(arenaName, player.getLocation());
|
||||
Dropper.getInstance().getArenaHandler().addArena(arena);
|
||||
commandSender.sendMessage("The arena was successfully created!");
|
||||
|
@ -2,6 +2,9 @@ package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.property.ArenaEditableProperty;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
@ -16,7 +19,7 @@ public class EditArenaCommand implements CommandExecutor {
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player)) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage("This command must be used by a player");
|
||||
return false;
|
||||
}
|
||||
@ -31,9 +34,127 @@ public class EditArenaCommand implements CommandExecutor {
|
||||
return false;
|
||||
}
|
||||
|
||||
//TODO: If an arena name and a property is given, display the current value
|
||||
//TODO: If an arena name, a property and a value is given, check if it's valid, and update the property
|
||||
return false;
|
||||
ArenaEditableProperty editableProperty = ArenaEditableProperty.getFromArgumentString(arguments[1]);
|
||||
if (editableProperty == null) {
|
||||
commandSender.sendMessage("Unknown property specified.");
|
||||
return false;
|
||||
}
|
||||
|
||||
String currentValueFormat = "Current value of %s is: %s";
|
||||
|
||||
if (arguments.length < 3) {
|
||||
// Print the current value of the property
|
||||
String value = editableProperty.getCurrentValueAsString(specifiedArena);
|
||||
commandSender.sendMessage(String.format(currentValueFormat, editableProperty.getArgumentString(), value));
|
||||
return true;
|
||||
} else {
|
||||
boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player);
|
||||
if (successful) {
|
||||
player.sendMessage(String.format("Property %s changed to: %s", editableProperty, arguments[2]));
|
||||
} else {
|
||||
player.sendMessage("Unable to change the property. Make sure your input is valid!");
|
||||
}
|
||||
return successful;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the given property to the given value
|
||||
*
|
||||
* @param arena <p>The arena to change the property for</p>
|
||||
* @param property <p>The property to change</p>
|
||||
* @param value <p>The new value of the property</p>
|
||||
* @param player <p>The player trying to change the value</p>
|
||||
* @return <p>True if the value was successfully changed</p>
|
||||
*/
|
||||
private boolean changeValue(@NotNull DropperArena arena, @NotNull ArenaEditableProperty property,
|
||||
@NotNull String value, @NotNull Player player) {
|
||||
return switch (property) {
|
||||
case WIN_BLOCK_TYPE -> arena.setWinBlockType(parseMaterial(value));
|
||||
case HORIZONTAL_VELOCITY -> arena.setHorizontalVelocity(sanitizeHorizontalVelocity(value));
|
||||
case VERTICAL_VELOCITY -> arena.setVerticalVelocity(sanitizeVerticalVelocity(value));
|
||||
case SPAWN_LOCATION -> arena.setSpawnLocation(parseLocation(player, value));
|
||||
case NAME -> arena.setName(value);
|
||||
case EXIT_LOCATION -> arena.setExitLocation(parseLocation(player, value));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the player's specified vertical velocity
|
||||
*
|
||||
* @param velocityString <p>The string to parse into a velocity</p>
|
||||
* @return <p>The parsed velocity, defaulting to 0.5 if not parse-able</p>
|
||||
*/
|
||||
private double sanitizeVerticalVelocity(@NotNull String velocityString) {
|
||||
// Vertical velocity should not be negative, as it would make the player go upwards. There is technically not a
|
||||
// max speed limit, but setting it too high makes the arena unplayable
|
||||
double velocity;
|
||||
try {
|
||||
velocity = Double.parseDouble(velocityString);
|
||||
} catch (NumberFormatException exception) {
|
||||
velocity = 3.92;
|
||||
}
|
||||
|
||||
// Require at least speed of 0.001, and at most 75 blocks/s
|
||||
return Math.min(Math.max(velocity, 0.001), 75);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes the user's specified horizontal velocity
|
||||
*
|
||||
* @param velocityString <p>The string to parse into a velocity</p>
|
||||
* @return <p>The parsed velocity, defaulting to 1 if not parse-able</p>
|
||||
*/
|
||||
private float sanitizeHorizontalVelocity(@NotNull String velocityString) {
|
||||
// Horizontal velocity is valid between -1 and 1, where negative values swaps directions
|
||||
float velocity;
|
||||
try {
|
||||
velocity = Float.parseFloat(velocityString);
|
||||
} catch (NumberFormatException exception) {
|
||||
velocity = 1;
|
||||
}
|
||||
|
||||
// Make sure the velocity isn't exactly 0
|
||||
if (velocity == 0) {
|
||||
velocity = 0.5f;
|
||||
}
|
||||
|
||||
// If outside bonds, choose the most extreme value
|
||||
return Math.min(Math.max(0.1f, velocity), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given location string
|
||||
*
|
||||
* @param player <p>The player changing a location</p>
|
||||
* @param locationString <p>The location string to parse</p>
|
||||
* @return <p>The parsed location, or the player's location if not parse-able</p>
|
||||
*/
|
||||
private @NotNull Location parseLocation(Player player, String locationString) {
|
||||
if ((locationString.trim() + ",").matches("([0-9]+.?[0-9]*,){3}")) {
|
||||
String[] parts = locationString.split(",");
|
||||
Location newLocation = player.getLocation().clone();
|
||||
newLocation.setX(Double.parseDouble(parts[0].trim()));
|
||||
newLocation.setY(Double.parseDouble(parts[1].trim()));
|
||||
newLocation.setZ(Double.parseDouble(parts[2].trim()));
|
||||
return newLocation;
|
||||
} else {
|
||||
return player.getLocation().clone();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given material name
|
||||
*
|
||||
* @param materialName <p>The material name to parse</p>
|
||||
* @return <p>The parsed material, or AIR if not valid</p>
|
||||
*/
|
||||
private @NotNull Material parseMaterial(String materialName) {
|
||||
Material material = Material.matchMaterial(materialName);
|
||||
if (material == null) {
|
||||
material = Material.AIR;
|
||||
}
|
||||
return material;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,13 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.util.TabCompleteHelper;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@ -16,9 +18,16 @@ public class EditArenaTabCompleter implements TabCompleter {
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
//TODO: Tab-complete existing arena names
|
||||
//TODO: If an arena name is given, tab-complete change-able properties
|
||||
return null;
|
||||
if (args.length == 1) {
|
||||
return TabCompleteHelper.getArenas();
|
||||
} else if (args.length == 2) {
|
||||
return TabCompleteHelper.getArenaProperties();
|
||||
} else if (args.length == 3) {
|
||||
//TODO: Tab-complete possible values for the given property
|
||||
return null;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,93 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaGroup;
|
||||
import net.knarcraft.dropper.arena.DropperArenaHandler;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The command for listing groups and the stages within
|
||||
*/
|
||||
public class GroupListCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
if (arguments.length == 0) {
|
||||
displayExistingGroups(arenaHandler, commandSender);
|
||||
return true;
|
||||
} else if (arguments.length == 1) {
|
||||
return displayOrderedArenaNames(arenaHandler, commandSender, arguments[0]);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays all currently existing dropper arena groups
|
||||
*
|
||||
* @param arenaHandler <p>The arena handler to get groups from</p>
|
||||
* @param sender <p>The command sender to display the groups to</p>
|
||||
*/
|
||||
private void displayExistingGroups(@NotNull DropperArenaHandler arenaHandler, @NotNull CommandSender sender) {
|
||||
StringBuilder builder = new StringBuilder("Dropper arena groups:").append("\n");
|
||||
arenaHandler.getAllGroups().stream().sorted().forEachOrdered((group) ->
|
||||
builder.append(group.getGroupName()).append("\n"));
|
||||
sender.sendMessage(builder.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays the ordered stages in a specified group to the specified command sender
|
||||
*
|
||||
* @param arenaHandler <p>The arena handler to get groups from</p>
|
||||
* @param sender <p>The command sender to display the stages to</p>
|
||||
* @param groupName <p>The name of the group to display stages for</p>
|
||||
* @return <p>True if the stages were successfully displayed</p>
|
||||
*/
|
||||
private boolean displayOrderedArenaNames(@NotNull DropperArenaHandler arenaHandler, @NotNull CommandSender sender,
|
||||
@NotNull String groupName) {
|
||||
DropperArenaGroup arenaGroup = arenaHandler.getGroup(groupName);
|
||||
if (arenaGroup == null) {
|
||||
sender.sendMessage("Unable to find the specified group!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a list of all stages (arenas in the group)
|
||||
StringBuilder builder = new StringBuilder(groupName).append("'s stages:").append("\n");
|
||||
int counter = 1;
|
||||
for (UUID arenaId : arenaGroup.getArenas()) {
|
||||
DropperArena arena = arenaHandler.getArena(arenaId);
|
||||
if (arena != null) {
|
||||
builder.append(counter++).append(". ").append(arena.getArenaName()).append("\n");
|
||||
}
|
||||
}
|
||||
sender.sendMessage(builder.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
List<String> groupNames = new ArrayList<>();
|
||||
for (DropperArenaGroup group : Dropper.getInstance().getArenaHandler().getAllGroups()) {
|
||||
groupNames.add(group.getGroupName());
|
||||
}
|
||||
return groupNames;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaGroup;
|
||||
import net.knarcraft.dropper.arena.DropperArenaHandler;
|
||||
import net.knarcraft.dropper.util.StringSanitizer;
|
||||
import net.knarcraft.dropper.util.TabCompleteHelper;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The command for setting the group of an arena
|
||||
*/
|
||||
public class GroupSetCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
|
||||
DropperArena specifiedArena = arenaHandler.getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage("Unable to find the specified dropper arena.");
|
||||
return false;
|
||||
}
|
||||
|
||||
String groupName = StringSanitizer.removeUnwantedCharacters(arguments[1]);
|
||||
|
||||
if (groupName.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaGroup arenaGroup;
|
||||
if (groupName.equalsIgnoreCase("null") || groupName.equalsIgnoreCase("none")) {
|
||||
arenaGroup = null;
|
||||
} else {
|
||||
arenaGroup = arenaHandler.getGroup(groupName);
|
||||
if (arenaGroup == null) {
|
||||
arenaGroup = new DropperArenaGroup(groupName);
|
||||
}
|
||||
}
|
||||
|
||||
arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup);
|
||||
|
||||
commandSender.sendMessage("The arena's group has been updated");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
return TabCompleteHelper.getArenas();
|
||||
} else if (arguments.length == 2) {
|
||||
List<String> possibleValues = new ArrayList<>();
|
||||
possibleValues.add("none");
|
||||
possibleValues.add("GroupName");
|
||||
for (DropperArenaGroup group : Dropper.getInstance().getArenaHandler().getAllGroups()) {
|
||||
possibleValues.add(group.getGroupName());
|
||||
}
|
||||
return possibleValues;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,102 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaGroup;
|
||||
import net.knarcraft.dropper.arena.DropperArenaHandler;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The command for swapping the order of two arenas in a group
|
||||
*/
|
||||
public class GroupSwapCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
|
||||
DropperArena arena1 = arenaHandler.getArena(arguments[0]);
|
||||
if (arena1 == null) {
|
||||
commandSender.sendMessage("Unable to find the first specified dropper arena.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArena arena2 = arenaHandler.getArena(arguments[1]);
|
||||
if (arena2 == null) {
|
||||
commandSender.sendMessage("Unable to find the second specified dropper arena.");
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId());
|
||||
DropperArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId());
|
||||
if (arena1Group == null || !arena1Group.equals(arena2Group)) {
|
||||
commandSender.sendMessage("You cannot swap arenas in different groups!");
|
||||
return false;
|
||||
}
|
||||
|
||||
arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()),
|
||||
arena1Group.getArenas().indexOf(arena2.getArenaId()));
|
||||
commandSender.sendMessage("The arenas have been swapped!");
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
if (arguments.length == 1) {
|
||||
List<String> arenaNames = new ArrayList<>();
|
||||
for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) {
|
||||
arenaNames.add(dropperArena.getArenaName());
|
||||
}
|
||||
return arenaNames;
|
||||
} else if (arguments.length == 2) {
|
||||
return getArenaNamesInSameGroup(arguments[0]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of all arenas in the same group as the specified arena
|
||||
*
|
||||
* @param arenaName <p>The name of the specified arena</p>
|
||||
* @return <p>The names of the arenas in the same group</p>
|
||||
*/
|
||||
private List<String> getArenaNamesInSameGroup(String arenaName) {
|
||||
DropperArenaHandler arenaHandler = Dropper.getInstance().getArenaHandler();
|
||||
DropperArena arena1 = arenaHandler.getArena(arenaName);
|
||||
if (arena1 == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// Only display other arenas in the selected group
|
||||
List<String> arenaNames = new ArrayList<>();
|
||||
DropperArenaGroup group = arenaHandler.getGroup(arena1.getArenaId());
|
||||
if (group == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (UUID arenaId : group.getArenas()) {
|
||||
DropperArena arena = arenaHandler.getArena(arenaId);
|
||||
if (arena != null && arena.getArenaId() != arena1.getArenaId()) {
|
||||
arenaNames.add(arena.getArenaName());
|
||||
}
|
||||
}
|
||||
return arenaNames;
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaGroup;
|
||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
@ -69,7 +70,18 @@ public class JoinArenaCommand implements CommandExecutor {
|
||||
gameMode = ArenaGameMode.DEFAULT;
|
||||
}
|
||||
|
||||
//TODO: Check if the arena has been beaten if the non-default game-mode has been chosen
|
||||
// Make sure the player has beaten the necessary levels
|
||||
DropperArenaGroup arenaGroup = Dropper.getInstance().getArenaHandler().getGroup(specifiedArena.getArenaId());
|
||||
if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Register the player's session
|
||||
DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode);
|
||||
@ -90,4 +102,32 @@ public class JoinArenaCommand implements CommandExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs necessary check for the given arena's group
|
||||
*
|
||||
* @param dropperArena <p>The arena the player is trying to join</p>
|
||||
* @param arenaGroup <p>The arena group the arena belongs to</p>
|
||||
* @param arenaGameMode <p>The game-mode the player selected</p>
|
||||
* @param player <p>The the player trying to join the arena</p>
|
||||
* @return <p>False if any checks failed</p>
|
||||
*/
|
||||
private boolean doGroupChecks(@NotNull DropperArena dropperArena, @NotNull DropperArenaGroup arenaGroup,
|
||||
@NotNull ArenaGameMode arenaGameMode, @NotNull Player 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<>();
|
||||
|
@ -22,8 +22,6 @@ public class ListArenaCommand implements TabExecutor {
|
||||
for (String arenaName : TabCompleteHelper.getArenas()) {
|
||||
sender.sendMessage(arenaName);
|
||||
}
|
||||
|
||||
//TODO: Allow displaying information about each arena (possibly admin-only)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,92 @@
|
||||
package net.knarcraft.dropper.property;
|
||||
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* All editable properties of a dropper arena
|
||||
*/
|
||||
public enum ArenaEditableProperty {
|
||||
|
||||
/**
|
||||
* The name of the arena
|
||||
*/
|
||||
NAME("name", DropperArena::getArenaName),
|
||||
|
||||
/**
|
||||
* The arena's spawn location
|
||||
*/
|
||||
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation())),
|
||||
|
||||
/**
|
||||
* The arena's exit location
|
||||
*/
|
||||
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation())),
|
||||
|
||||
/**
|
||||
* The arena's vertical velocity
|
||||
*/
|
||||
VERTICAL_VELOCITY("verticalVelocity", (arena) -> String.valueOf(arena.getPlayerVerticalVelocity())),
|
||||
|
||||
/**
|
||||
* The arena's horizontal velocity
|
||||
*/
|
||||
HORIZONTAL_VELOCITY("horizontalVelocity", (arena) -> String.valueOf(arena.getPlayerHorizontalVelocity())),
|
||||
|
||||
/**
|
||||
* The arena's win block type
|
||||
*/
|
||||
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString()),
|
||||
;
|
||||
|
||||
private final @NotNull String argumentString;
|
||||
private final Function<DropperArena, String> currentValueProvider;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena editable property
|
||||
*
|
||||
* @param argumentString <p>The argument string used to specify this property</p>
|
||||
*/
|
||||
ArenaEditableProperty(@NotNull String argumentString, Function<DropperArena, String> currentValueProvider) {
|
||||
this.argumentString = argumentString;
|
||||
this.currentValueProvider = currentValueProvider;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of this property's current value
|
||||
*
|
||||
* @param arena <p>The arena to check the value for</p>
|
||||
* @return <p>The current value as a string</p>
|
||||
*/
|
||||
public String getCurrentValueAsString(DropperArena arena) {
|
||||
return this.currentValueProvider.apply(arena);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument string used to specify this property
|
||||
*
|
||||
* @return <p>The argument string</p>
|
||||
*/
|
||||
public @NotNull String getArgumentString() {
|
||||
return this.argumentString;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the editable property corresponding to the given argument string
|
||||
*
|
||||
* @param argumentString <p>The argument string used to specify an editable property</p>
|
||||
* @return <p>The corresponding editable property, or null if not found</p>
|
||||
*/
|
||||
public static @Nullable ArenaEditableProperty getFromArgumentString(String argumentString) {
|
||||
for (ArenaEditableProperty property : ArenaEditableProperty.values()) {
|
||||
if (property.argumentString.equalsIgnoreCase(argumentString)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -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"));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,11 +37,6 @@ public enum ArenaStorageKey {
|
||||
*/
|
||||
PLAYER_HORIZONTAL_VELOCITY("arenaPlayerHorizontalVelocity"),
|
||||
|
||||
/**
|
||||
* The key for this arena's stage
|
||||
*/
|
||||
STAGE("arenaStage"),
|
||||
|
||||
/**
|
||||
* The key for the type of this arena's win block
|
||||
*/
|
||||
|
@ -3,9 +3,11 @@ package net.knarcraft.dropper.util;
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaData;
|
||||
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;
|
||||
@ -19,6 +21,7 @@ import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
@ -28,7 +31,9 @@ import java.util.logging.Level;
|
||||
public final class ArenaStorageHelper {
|
||||
|
||||
private final static String arenasConfigurationSection = "arenas";
|
||||
private final static String groupsConfigurationSection = "groups";
|
||||
private static final File arenaFile = new File(Dropper.getInstance().getDataFolder(), "arenas.yml");
|
||||
private static final File groupFile = new File(Dropper.getInstance().getDataFolder(), "groups.yml");
|
||||
private static final File arenaDataFolder = new File(Dropper.getInstance().getDataFolder(), "arena_data");
|
||||
|
||||
private ArenaStorageHelper() {
|
||||
@ -36,7 +41,46 @@ public final class ArenaStorageHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the given arenas to the given file
|
||||
* Saves the given dropper arena groups
|
||||
*
|
||||
* @param arenaGroups <p>The arena groups to save</p>
|
||||
* @throws IOException <p>If unable to write to the file</p>
|
||||
*/
|
||||
public static void saveDropperArenaGroups(@NotNull Set<DropperArenaGroup> arenaGroups) throws IOException {
|
||||
YamlConfiguration configuration = new YamlConfiguration();
|
||||
ConfigurationSection groupSection = configuration.createSection(groupsConfigurationSection);
|
||||
|
||||
for (DropperArenaGroup arenaGroup : arenaGroups) {
|
||||
groupSection.set(arenaGroup.getGroupId().toString(), arenaGroup);
|
||||
}
|
||||
|
||||
configuration.save(groupFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all existing dropper arena groups
|
||||
*
|
||||
* @return <p>The loaded arena groups</p>
|
||||
*/
|
||||
public static @NotNull Set<DropperArenaGroup> loadDropperArenaGroups() {
|
||||
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(groupFile);
|
||||
ConfigurationSection groupSection = configuration.getConfigurationSection(groupsConfigurationSection);
|
||||
//If no such section exists, it must be the case that there is no data to load
|
||||
if (groupSection == null) {
|
||||
return new HashSet<>();
|
||||
}
|
||||
|
||||
Set<DropperArenaGroup> arenaGroups = new HashSet<>();
|
||||
|
||||
for (String sectionName : groupSection.getKeys(false)) {
|
||||
arenaGroups.add((DropperArenaGroup) groupSection.get(sectionName));
|
||||
}
|
||||
|
||||
return arenaGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the given arenas
|
||||
*
|
||||
* @param arenas <p>The arenas to save</p>
|
||||
* @throws IOException <p>If unable to write to the file</p>
|
||||
@ -47,15 +91,13 @@ public final class ArenaStorageHelper {
|
||||
for (DropperArena arena : arenas.values()) {
|
||||
//Note: While the arena name is used as the key, as the key has to be sanitized, the un-sanitized arena name
|
||||
// must be stored as well
|
||||
@NotNull ConfigurationSection configSection = arenaSection.createSection(sanitizeArenaName(
|
||||
arena.getArenaName()));
|
||||
@NotNull ConfigurationSection configSection = arenaSection.createSection(arena.getArenaId().toString());
|
||||
configSection.set(ArenaStorageKey.ID.getKey(), new SerializableUUID(arena.getArenaId()));
|
||||
configSection.set(ArenaStorageKey.NAME.getKey(), arena.getArenaName());
|
||||
configSection.set(ArenaStorageKey.SPAWN_LOCATION.getKey(), arena.getSpawnLocation());
|
||||
configSection.set(ArenaStorageKey.EXIT_LOCATION.getKey(), arena.getExitLocation());
|
||||
configSection.set(ArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey(), arena.getPlayerVerticalVelocity());
|
||||
configSection.set(ArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity());
|
||||
configSection.set(ArenaStorageKey.STAGE.getKey(), arena.getStage());
|
||||
configSection.set(ArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
|
||||
saveArenaData(arena.getData());
|
||||
}
|
||||
@ -63,7 +105,7 @@ public final class ArenaStorageHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all arenas from the given file
|
||||
* Loads all arenas
|
||||
*
|
||||
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
|
||||
*/
|
||||
@ -108,7 +150,6 @@ public final class ArenaStorageHelper {
|
||||
double verticalVelocity = configurationSection.getDouble(ArenaStorageKey.PLAYER_VERTICAL_VELOCITY.getKey());
|
||||
float horizontalVelocity = sanitizeHorizontalVelocity((float) configurationSection.getDouble(
|
||||
ArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey()));
|
||||
Integer stage = (Integer) configurationSection.get(ArenaStorageKey.STAGE.getKey());
|
||||
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
|
||||
ArenaStorageKey.WIN_BLOCK_TYPE.getKey());
|
||||
|
||||
@ -124,21 +165,16 @@ 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,
|
||||
stage, winBlockType.material(), arenaData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes an arena name for usage as a YAML key
|
||||
*
|
||||
* @param arenaName <p>The arena name to sanitize</p>
|
||||
* @return <p>The sanitized arena name</p>
|
||||
*/
|
||||
public static @NotNull String sanitizeArenaName(@NotNull String arenaName) {
|
||||
return arenaName.toLowerCase().trim().replaceAll(" ", "_");
|
||||
winBlockType.material(), arenaData);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.dropper.util;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A helper-class for sanitizing strings
|
||||
*/
|
||||
public final class StringSanitizer {
|
||||
|
||||
private StringSanitizer() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes unwanted characters from a string
|
||||
*
|
||||
* <p>This basically removes character that have a special meaning in YML, or ones that cannot be used in the
|
||||
* chat.</p>
|
||||
*
|
||||
* @param input <p>The string to remove from</p>
|
||||
* @return <p>The string with the unwanted characters removed</p>
|
||||
*/
|
||||
public static @NotNull String removeUnwantedCharacters(@NotNull String input) {
|
||||
return input.replaceAll("[§ :=&]", "");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes an arena name for usage as a YAML key
|
||||
*
|
||||
* @param arenaName <p>The arena name to sanitize</p>
|
||||
* @return <p>The sanitized arena name</p>
|
||||
*/
|
||||
public static @NotNull String sanitizeArenaName(@NotNull String arenaName) {
|
||||
return arenaName.toLowerCase().trim().replaceAll(" ", "_");
|
||||
}
|
||||
|
||||
}
|
@ -2,6 +2,7 @@ package net.knarcraft.dropper.util;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.property.ArenaEditableProperty;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@ -29,4 +30,17 @@ public final class TabCompleteHelper {
|
||||
return arenaNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the argument strings of all arena properties
|
||||
*
|
||||
* @return <p>All arena properties</p>
|
||||
*/
|
||||
public static @NotNull List<String> getArenaProperties() {
|
||||
List<String> arenaProperties = new ArrayList<>();
|
||||
for (ArenaEditableProperty property : ArenaEditableProperty.values()) {
|
||||
arenaProperties.add(property.getArgumentString());
|
||||
}
|
||||
return arenaProperties;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,48 +4,77 @@ main: net.knarcraft.dropper.Dropper
|
||||
api-version: 1.19
|
||||
description: A plugin for dropper mini-games
|
||||
|
||||
# Note to self: Aliases must be lowercase!
|
||||
commands:
|
||||
dropperreload:
|
||||
dropperGroupSet:
|
||||
aliases:
|
||||
- dgset
|
||||
permission: dropper.edit
|
||||
usage: |
|
||||
/<command> <arena> <group>
|
||||
- The group will be created if it doesn't already exist
|
||||
- Use "none" or "null" as the group to release the arena from its group
|
||||
description: Sets the group of the given arena
|
||||
dropperGroupSwap:
|
||||
aliases:
|
||||
- dgswap
|
||||
permission: dropper.edit
|
||||
usage: |
|
||||
/<command> <arena1> <arena2>
|
||||
- The two arenas must be in the same group
|
||||
description: Swaps the order of two arenas in the same group
|
||||
dropperGroupList:
|
||||
aliases:
|
||||
- dglist
|
||||
permission: dropper.edit
|
||||
usage: |
|
||||
/<command> [group]
|
||||
- Existing groups will be listed if used without an argument
|
||||
- Supplying a group shows the group's arenas
|
||||
description: Lists existing groups and their arenas
|
||||
dropperReload:
|
||||
aliases:
|
||||
- dreload
|
||||
permission: dropper.admin
|
||||
usage: /<command>
|
||||
description: Reloads all data from disk
|
||||
dropperlist:
|
||||
dropperList:
|
||||
aliases:
|
||||
- dlist
|
||||
permission: dropper.join
|
||||
usage: /<command>
|
||||
description: Used to list all current dropper arenas
|
||||
dropperjoin:
|
||||
dropperJoin:
|
||||
aliases:
|
||||
- djoin
|
||||
permission: dropper.join
|
||||
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
|
||||
- Mode can be used to select challenge modes which can be played after beating the arena.
|
||||
- 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:
|
||||
dropperLeave:
|
||||
aliases:
|
||||
- dleave
|
||||
permission: dropper.join
|
||||
usage: /<command>
|
||||
description: Used to leave the current dropper arena
|
||||
droppercreate:
|
||||
dropperCreate:
|
||||
aliases:
|
||||
- dcreate
|
||||
permission: dropper.create
|
||||
usage: /<command> (Details not finalized)
|
||||
usage: |
|
||||
/<command> <arena> <property> [new value]
|
||||
- Valid properties: name, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity, winBlockType
|
||||
description: Used to create a new dropper arena
|
||||
dropperedit:
|
||||
dropperEdit:
|
||||
aliases:
|
||||
- dedit
|
||||
permission: dropper.edit
|
||||
usage: /<command> (Details not finalized)
|
||||
description: Used to edit an existing dropper arena
|
||||
dropperremove:
|
||||
dropperRemove:
|
||||
aliases:
|
||||
- dremove
|
||||
permission: dropper.remove
|
||||
|
@ -0,0 +1,50 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Tests for arena dropper groups
|
||||
*/
|
||||
public class DropperArenaGroupTest {
|
||||
|
||||
@Test
|
||||
public void swapTest() {
|
||||
/*
|
||||
This test makes sure the order of arenas is as expected when the arenas are added to a group. It also makes
|
||||
sure that swapping two items works as expected.
|
||||
*/
|
||||
|
||||
DropperArenaGroup arenaGroup = new DropperArenaGroup("test");
|
||||
UUID arena1Id = UUID.randomUUID();
|
||||
UUID arena2Id = UUID.randomUUID();
|
||||
UUID arena3Id = UUID.randomUUID();
|
||||
UUID arena4Id = UUID.randomUUID();
|
||||
|
||||
arenaGroup.addArena(arena1Id);
|
||||
arenaGroup.addArena(arena2Id);
|
||||
arenaGroup.addArena(arena3Id);
|
||||
arenaGroup.addArena(arena4Id);
|
||||
|
||||
List<UUID> initialOrder = new ArrayList<>();
|
||||
initialOrder.add(arena1Id);
|
||||
initialOrder.add(arena2Id);
|
||||
initialOrder.add(arena3Id);
|
||||
initialOrder.add(arena4Id);
|
||||
Assertions.assertEquals(initialOrder, arenaGroup.getArenas());
|
||||
|
||||
arenaGroup.swapArenas(1, 3);
|
||||
|
||||
List<UUID> swapped = new ArrayList<>();
|
||||
swapped.add(arena1Id);
|
||||
swapped.add(arena4Id);
|
||||
swapped.add(arena3Id);
|
||||
swapped.add(arena2Id);
|
||||
Assertions.assertEquals(swapped, arenaGroup.getArenas());
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user