mirror of
https://github.com/SunNetservers/MiniGames.git
synced 2025-07-06 16:14:45 +02:00
Compare commits
77 Commits
v0.0.1
...
v1.0.4-bet
Author | SHA1 | Date | |
---|---|---|---|
701cdd81eb | |||
078a8ed007 | |||
8d1b841619 | |||
4a3329459e | |||
206a85b23a | |||
401490df58 | |||
b2fbaf0e68 | |||
7c04e91024 | |||
e6bb324da1 | |||
58e25bcb30 | |||
cf0962ef70 | |||
01a3d0b73c | |||
ce0a0dbaa7 | |||
ac70b4bb0d | |||
bcddd214f6 | |||
0344060214 | |||
76e11ec6b8 | |||
a84e164edf | |||
b0a72561f9 | |||
c68cafb925 | |||
8e4737a267 | |||
e21e872e89 | |||
6950246134 | |||
257fc48912 | |||
31b22c7e56 | |||
904761ba4e | |||
12789980c0 | |||
8f77fc5910 | |||
1acaebb3bc | |||
9a3f9841ab | |||
b1e86a928b | |||
4de5ae469b | |||
2f4d4ff4c6 | |||
50978d8baf | |||
3bbf41206c | |||
6a41664fef | |||
d1964e9d7b | |||
cd7d8eded0 | |||
b95cc294ab | |||
458dbc2beb | |||
efaca03434 | |||
58b5b422f0 | |||
483a0a16dc | |||
e1c4a6a97c | |||
5be6f0d00e | |||
f6a272b0c0 | |||
46e52812af | |||
fe016fd620 | |||
8e9b274fc0 | |||
2b9cfeebb1 | |||
096f23d468 | |||
3ebf5fa924 | |||
579c1ea0f9 | |||
d41154281b | |||
f852de7309 | |||
fb6550afe7 | |||
e5a3f9206d | |||
dfdf04a0ee | |||
888b20bb93 | |||
c29fcdc166 | |||
f9008ca050 | |||
572bb980c1 | |||
c01e1c3509 | |||
ca5b0fcbca | |||
34989e6673 | |||
45bdb3f2a6 | |||
21425f73a1 | |||
51237cb11a | |||
3626d997b8 | |||
9a7d3ca360 | |||
9a56f58f2f | |||
592f53ec9e | |||
49eb0ac82c | |||
0c58860026 | |||
6385b4c5e8 | |||
14572de102 | |||
fba75d2c3f |
15
HEADER
Normal file
15
HEADER
Normal file
@ -0,0 +1,15 @@
|
||||
MiniGames - A mini-games plugin for spigot
|
||||
Copyright (C) 2023 Kristian Knarvik (EpicKnarvik97)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
271
README.md
271
README.md
@ -1,53 +1,256 @@
|
||||
# Dropper
|
||||
# MiniGames
|
||||
|
||||
This is a plugin for a dropper mini-game (try to reach the bottom without hitting any obstacles).
|
||||
To create an arena, simply use `/droppercreate <name>`, where \<name> is simply the name used to differentiate and
|
||||
recognize the arena. Your location will be used as the spawn location for anyone joining the dropper arena. To start
|
||||
This plugin adds several mini-games.
|
||||
|
||||
To create a dropper arena, simply use `/droppercreate <name>`, where \<name> is simply the name used to differentiate
|
||||
and recognize the arena. Your location will be used as the spawn location for anyone joining the dropper arena. To start
|
||||
playing, simply use `/dropperjoin <name>`, where \<name> is the same as you specified upon creation.
|
||||
To modify
|
||||
To modify the arena, use `/dropperedit <name> <property> <value>`.
|
||||
|
||||
To create a parkour arena, simply use `/parkourcreate <name>`, where \<name> is simply the name used to differentiate
|
||||
and recognize the arena. Your location will be used as the spawn location for anyone joining the dropper arena. To start
|
||||
playing, simply use `/parkourjoin <name>`, where \<name> is the same as you specified upon creation.
|
||||
To modify the arena, use `/parkouredit <name> <property> <value>`. Use `/parkouredit checkpointAdd here` to add a
|
||||
checkpoint at your current location.
|
||||
|
||||
## 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 |
|
||||
The only permission normal players will need is `minigames.join` which is set to true by default.
|
||||
|
||||
| Node | Description |
|
||||
|--------------------------|------------------------------------------------------|
|
||||
| minigames.admin | Gives all permissions. |
|
||||
| minigames.dropper | Gives all dropper-related permissions. |
|
||||
| minigames.parkour | Gives all parkour-related permissions. |
|
||||
| minigames.join | Allows a player to participate in mini-game arenas. |
|
||||
| minigames.join.dropper | Allows a player to participate in dropper arenas. |
|
||||
| minigames.join.parkour | Allows a player to participate in parkour arenas. |
|
||||
| minigames.create | Allows a player to create a new mini-game arena. |
|
||||
| minigames.create.dropper | Allows a player to create a new dropper arena. |
|
||||
| minigames.create.parkour | Allows a player to create a new parkour arena. |
|
||||
| minigames.edit | Allows a player to edit an existing mini-game arena. |
|
||||
| minigames.edit.dropper | Allows a player to edit an existing dropper arena. |
|
||||
| minigames.edit.parkour | Allows a player to edit an existing parkour arena. |
|
||||
| minigames.remove | Allows a player to remove a mini-game arena. |
|
||||
| minigames.remove.dropper | Allows a player to remove a dropper arena. |
|
||||
| minigames.remove.parkour | Allows a player to remove a parkour 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 |
|
||||
| Command | Alias | Arguments | Description |
|
||||
|----------------------------------------|----------|-----------------------------|-------------------------------------------------------------------------------------|
|
||||
| /miniGamesReload | /mreload | | Reloads all data from disk. |
|
||||
| /miniGamesLeave | /mleave | | Leaves the current mini-game. |
|
||||
| /dropperList | /dlist | | Lists available dropper arenas. |
|
||||
| [/dropperJoin](#dropperjoin) | /djoin | \<arena> \[mode] | Joins the selected 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. |
|
||||
| [/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. |
|
||||
| /parkourList | /plist | | Lists available parkour arenas. |
|
||||
| /parkourJoin | /pjoin | \<arena> | Joins the selected arena. |
|
||||
| /parkourCreate | /pcreate | \<name> | Creates a new parkour arena with the given name. The spawn is set to your location. |
|
||||
| /parkourRemove | /premove | \<arena> | Removes the specified parkour arena. |
|
||||
| [/parkourEdit](#parkouredit) | /pedit | \<arena> \<option> \[value] | Gets or sets a parkour arena option. |
|
||||
| /parkourGroupSet | /pgset | \<arena> \<group> | Puts the given arena in the given group. Use "none" to remove an existing group. |
|
||||
| /parkourGroupList | /pglist | \[group] | Lists groups, or the stages of a group if a group is specified. |
|
||||
| [/parkourGroupSwap](#droppergroupswap) | /pgswap | \<arena1> \<arena2> | Swaps the two arenas in the group's ordered list. |
|
||||
|
||||
## Command explanation
|
||||
### Command explanation dropper
|
||||
|
||||
### /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
|
||||
|
||||
### Command explanation parkour
|
||||
|
||||
#### /parkourEdit
|
||||
|
||||
This command allows editing the specified property for the specified parkour arena.
|
||||
|
||||
`/parkouredit <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. |
|
||||
|
||||
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. |
|
||||
| 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. |
|
||||
| winLocation | The location players must reach to win the arena (see spawnLocation for valid values). If set, this overrides, and is used instead of, the win block type. |
|
||||
| checkpointAdd | Adds a new checkpoint to the arena's checkpoints (see spawnLocation for valid values). |
|
||||
| checkpointClear | Clears all current checkpoints. Give any value to execute. If not given a value, current checkpoints are shown. |
|
||||
| killPlaneBlocks | A comma-separated list of materials which will force a loss on hit. +WOOL and other [material tags](#notes-about-material-tags) are supported as well. |
|
||||
|
||||
## Configuration options
|
||||
|
||||
### Shared
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|-----------------------------------|---------------------|-------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| liquidHitBoxDepth | -1 < decimal < 0 | -0.8 | This decides how far inside a non-solid block the player must go before detection triggers (-1, 0). The closer to -1 it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit increases. |
|
||||
| solidHitBoxDistance | 0 < decimal < 1 | 0.2 | This decides the distance the player must be from a block below them before a hit triggers (0, 1). If too low, the likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near. |
|
||||
|
||||
### Dropper
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|-----------------------------------|---------------------|-------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| blockSneaking | true/false | true | Whether to block using the shift key to drop faster than the intended drop speed |
|
||||
| blockSprinting | true/false | true | Whether to block using the sprint key for slightly improved air speed |
|
||||
| verticalVelocity | 0 < decimal <= 75 | 1.0 | The vertical velocity used as default for all arenas. Must be greater than 0. 3.92 is the max speed of a falling player. |
|
||||
| horizontalVelocity | 0 < decimal <= 1 | 1.0 | The horizontal velocity used as default for all arenas (technically fly-speed). Must be between 0 (exclusive) and 1 (inclusive). |
|
||||
| randomlyInvertedTimer | 0 < integer <= 3600 | 7 | The number of seconds before the randomly inverted game-mode switches between normal and inverted movement (0, 3600] |
|
||||
| mustDoGroupedInSequence | true/false | true | Whether grouped dropper arenas must be played in the correct sequence |
|
||||
| ignoreRecordsUntilGroupBeatenOnce | true/false | false | Whether records won't be registered unless the player has already beaten all arenas in a group. That means players are required to do a second play-through to register a record for a grouped arena. |
|
||||
| mustDoNormalModeFirst | true/false | true | Whether a player must do the normal/default game-mode before playing any other game-modes |
|
||||
| makePlayersInvisible | true/false | false | Whether players should be made invisible while playing in a dropper arena |
|
||||
| disableHitCollision | true/false | true | Whether players should have their entity hit collision disabled while in an arena. This prevents players from pushing each-other if in the same arena. |
|
||||
| liquidHitBoxDepth | -1 < decimal < 0 | -0.8 | This decides how far inside a non-solid block the player must go before detection triggers (-1, 0). The closer to -1 it is, the more accurate it will seem to the player, but the likelihood of not detecting the hit increases. |
|
||||
| solidHitBoxDistance | 0 < decimal < 1 | 0.2 | This decides the distance the player must be from a block below them before a hit triggers (0, 1). If too low, the likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near. |
|
||||
| blockWhitelist | list | [see this](#blockwhitelist-default) | A whitelist for which blocks won't trigger a loss when hit/passed through. The win block check happens before the loss check, so even blocks on the whitelist can be used as the win-block. "+" denotes a [material tag](#notes-about-material-tags). |
|
||||
|
||||
### Parkour
|
||||
|
||||
| Name | Type | Default | Description |
|
||||
|-----------------------------------|------------|--------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| enforceCheckpointOrder | true/false | false | Whether to enforce the order in which a player must reach checkpoints. Enabling this ensures that a player cannot trigger a previous checkpoint by accident. It also ensures players cannot skip a checkpoint, even if the arena layout makes it possible. |
|
||||
| mustDoGroupedInSequence | true/false | true | Whether grouped dropper arenas must be played in the correct sequence |
|
||||
| ignoreRecordsUntilGroupBeatenOnce | true/false | false | Whether records won't be registered unless the player has already beaten all arenas in a group. That means players are required to do a second play-through to register a record for a grouped arena. |
|
||||
| makePlayersInvisible | true/false | false | Whether players should be made invisible while playing in a dropper arena |
|
||||
| killPlaneBlocks | list | [see this](#killplaneblocks-default) | The types of blocks compromising parkour arenas' kill planes. Add any materials you want to use for the "bottom" of your parkour arenas. +WOOL and other [material tags](#notes-about-material-tags) are supported. |
|
||||
|
||||
#### blockWhitelist default:
|
||||
|
||||
- WATER
|
||||
- LAVA
|
||||
- +WALL_SIGNS
|
||||
- +STANDING_SIGNS
|
||||
- STRUCTURE_VOID
|
||||
- WALL_TORCH
|
||||
- SOUL_WALL_TORCH
|
||||
- REDSTONE_WALL_TORCH
|
||||
- +BANNERS
|
||||
- +BUTTONS
|
||||
- +CORALS
|
||||
- +WALL_CORALS
|
||||
|
||||
#### killPlaneBlocks default:
|
||||
|
||||
- LAVA
|
||||
- MAGMA_BLOCK
|
||||
|
||||
## Record placeholders
|
||||
|
||||
Player records can be displayed on a leaderboard by using PlaceholderAPI. If you want to display a sign-based
|
||||
leaderboard, you can use the [Placeholder Signs](https://git.knarcraft.net/EpicKnarvik97/PlaceholderSigns) plugin. The
|
||||
format for the built-in placeholders is as follows:
|
||||
|
||||
`%gameMode_record_recordType_gameModeType_identifierType_identifier_recordPlacing_infoType%`
|
||||
|
||||
| Variable | Values | Description |
|
||||
|----------------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| gameMode | dropper / parkour | A selection of which game-mode you are getting a record for |
|
||||
| record | | This must be as-is. It's a selector in case placeholders are added for more than records. |
|
||||
| recordType | deaths / time | Selects the type of record to get (deaths or time). |
|
||||
| gameModeType | default / inverted / random | Selects the game-mode to get the record for. |
|
||||
| identifierType | arena / group | The type of thing the following identifier points to (an arena or an arena group). |
|
||||
| identifier | ? | An identifier (the name or UUID) for an arena or a group (whichever was chosen as identifierType). |
|
||||
| recordPlacing | 1 / 2 / 3 / ... | The position of the record to get (1 = first place, 2 = second place, etc.). |
|
||||
| infoType | player / value / combined | The type of info to get. Player gets the player name, Value gets the value of the achieved record. Combined gets "Player: Record". |
|
||||
|
||||
## Notes about material tags
|
||||
|
||||
Where a list of material is allowed, this plugin supports using material tags that specify a set of blocks. This makes
|
||||
it much easier to add a lot of blocks without ending up with hundreds of individual materials. To specify
|
||||
such a tag, use a `+` character, and then the tag name.
|
||||
See <a href="https://hub.spigotmc.org/javadocs/spigot/org/bukkit/Tag.html">the documentation</a> for a complete overview
|
||||
of all tags. Only those with type `Tag<Material>` can be used.
|
||||
|
||||
Example tags:
|
||||
|
||||
- +WOOL
|
||||
- +WALL_SIGNS
|
||||
- +ACACIA_LOGS
|
||||
- +ALL_SIGNS
|
||||
- +DIAMOND_ORES
|
||||
- +DIRT
|
||||
- +DOORS
|
||||
- +DRAGON_IMMUNE
|
||||
- +FENCE_GATES
|
||||
- +FENCES
|
||||
|
||||
## License
|
||||
|
||||
MiniGames is licensed under the GNU Public License Version 3.0. This includes every source and resource file. See the
|
||||
HEADER file for a more detailed license description.
|
22
pom.xml
22
pom.xml
@ -5,13 +5,13 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>net.knarcraft</groupId>
|
||||
<artifactId>Dropper</artifactId>
|
||||
<artifactId>MiniGames</artifactId>
|
||||
<version>1.0-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>Dropper</name>
|
||||
<name>MiniGames</name>
|
||||
|
||||
<description>A plugin for dropper mini-games</description>
|
||||
<description>A plugin which adds various mini-games</description>
|
||||
<properties>
|
||||
<java.version>16</java.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
@ -58,6 +58,10 @@
|
||||
<id>spigot-repo</id>
|
||||
<url>https://hub.spigotmc.org/nexus/content/repositories/snapshots/</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>placeholderapi</id>
|
||||
<url>https://repo.extendedclip.com/content/repositories/placeholderapi/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<dependencies>
|
||||
@ -73,5 +77,17 @@
|
||||
<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>
|
||||
<dependency>
|
||||
<groupId>me.clip</groupId>
|
||||
<artifactId>placeholderapi</artifactId>
|
||||
<version>2.10.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@ -1,119 +0,0 @@
|
||||
package net.knarcraft.dropper;
|
||||
|
||||
import net.knarcraft.dropper.arena.DropperArenaHandler;
|
||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.dropper.command.CreateArenaCommand;
|
||||
import net.knarcraft.dropper.command.EditArenaCommand;
|
||||
import net.knarcraft.dropper.command.EditArenaTabCompleter;
|
||||
import net.knarcraft.dropper.command.JoinArenaCommand;
|
||||
import net.knarcraft.dropper.command.JoinArenaTabCompleter;
|
||||
import net.knarcraft.dropper.command.LeaveArenaCommand;
|
||||
import net.knarcraft.dropper.command.ListArenaCommand;
|
||||
import net.knarcraft.dropper.command.RemoveArenaCommand;
|
||||
import net.knarcraft.dropper.listener.DamageListener;
|
||||
import net.knarcraft.dropper.listener.MoveListener;
|
||||
import net.knarcraft.dropper.listener.PlayerLeaveListener;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* The dropper plugin's main class
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class Dropper extends JavaPlugin {
|
||||
|
||||
private static Dropper instance;
|
||||
private DropperArenaHandler arenaHandler;
|
||||
private DropperArenaPlayerRegistry playerRegistry;
|
||||
|
||||
/**
|
||||
* Gets an instance of this plugin
|
||||
*
|
||||
* @return <p>An instance of this plugin, or null if not initialized yet.</p>
|
||||
*/
|
||||
public static Dropper getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena handler for this instance
|
||||
*
|
||||
* @return <p>A dropper arena handler</p>
|
||||
*/
|
||||
public DropperArenaHandler getArenaHandler() {
|
||||
return this.arenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena player registry for this instance
|
||||
*
|
||||
* @return <p>A dropper arena player registry</p>
|
||||
*/
|
||||
public DropperArenaPlayerRegistry getPlayerRegistry() {
|
||||
return this.playerRegistry;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
instance = this;
|
||||
this.playerRegistry = new DropperArenaPlayerRegistry();
|
||||
this.arenaHandler = new DropperArenaHandler();
|
||||
this.arenaHandler.loadArenas();
|
||||
|
||||
//TODO: Add a command for joining a specific arena. Only teleport if the stage check succeeds (The server can
|
||||
// use something like https://www.spigotmc.org/resources/commandblocks.62720/ for immersion)
|
||||
//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
|
||||
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
pluginManager.registerEvents(new DamageListener(), this);
|
||||
pluginManager.registerEvents(new MoveListener(), this);
|
||||
pluginManager.registerEvents(new PlayerLeaveListener(), this);
|
||||
|
||||
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(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command
|
||||
*
|
||||
* @param commandName <p>The name of the command to register (defined in plugin.yml)</p>
|
||||
* @param commandExecutor <p>The executor for the command</p>
|
||||
* @param tabCompleter <p>The tab-completer to use, or null</p>
|
||||
*/
|
||||
private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor commandExecutor,
|
||||
@Nullable TabCompleter tabCompleter) {
|
||||
PluginCommand command = this.getCommand(commandName);
|
||||
if (command != null) {
|
||||
command.setExecutor(commandExecutor);
|
||||
if (tabCompleter != null) {
|
||||
command.setTabCompleter(tabCompleter);
|
||||
}
|
||||
} else {
|
||||
getLogger().log(Level.SEVERE, "Unable to register the command " + commandName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,148 +0,0 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A representation of one dropper arena
|
||||
*/
|
||||
public class DropperArena {
|
||||
|
||||
/**
|
||||
* A name used when listing and storing this arena.
|
||||
*/
|
||||
private final @NotNull String arenaName;
|
||||
|
||||
/**
|
||||
* The location players are teleported to when joining this arena.
|
||||
*/
|
||||
private final @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;
|
||||
|
||||
/**
|
||||
* The velocity in the y-direction to apply to all players in this arena.
|
||||
*/
|
||||
private final double playerVelocity;
|
||||
|
||||
/**
|
||||
* The stage number of this arena. If not null, the previous stage number must be cleared before access.
|
||||
*/
|
||||
private final @Nullable Integer stage;
|
||||
|
||||
/**
|
||||
* The registry used to save this arena's records
|
||||
*/
|
||||
private final @NotNull DropperArenaRecordsRegistry recordsRegistry;
|
||||
|
||||
//TODO: Store records for this arena (maps with player->deaths/time). It should be possible to get those in sorted
|
||||
// order (smallest to largest)
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena
|
||||
*
|
||||
* @param arenaName <p>The name of the arena</p>
|
||||
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
|
||||
* @param exitLocation <p>The location the players are teleported to when exiting the arena, or null</p>
|
||||
* @param playerVelocity <p>The velocity multiplier to use for players' velocity</p>
|
||||
* @param stage <p>The stage number of this stage, or null if not limited to stages</p>
|
||||
* @param recordsRegistry <p>The registry keeping track of all of this arena's records</p>
|
||||
*/
|
||||
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation, @Nullable Location exitLocation,
|
||||
double playerVelocity, @Nullable Integer stage, @NotNull DropperArenaRecordsRegistry recordsRegistry) {
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = exitLocation;
|
||||
this.playerVelocity = playerVelocity;
|
||||
this.stage = stage;
|
||||
this.recordsRegistry = recordsRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena
|
||||
*
|
||||
* <p>Note that this minimal constructor can be used to quickly create a new dropper arena at the player's given
|
||||
* location, simply by them giving an arena name.</p>
|
||||
*
|
||||
* @param arenaName <p>The name of the arena</p>
|
||||
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
|
||||
*/
|
||||
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation) {
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = null;
|
||||
this.playerVelocity = 1;
|
||||
this.stage = null;
|
||||
this.recordsRegistry = new DropperArenaRecordsRegistry();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registry keeping track of this arena's records
|
||||
*
|
||||
* @return <p>This arena's record registry</p>
|
||||
*/
|
||||
public @NotNull DropperArenaRecordsRegistry getRecordsRegistry() {
|
||||
return this.recordsRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this arena
|
||||
*
|
||||
* @return <p>The name of this arena.</p>
|
||||
*/
|
||||
public @NotNull String getArenaName() {
|
||||
return this.arenaName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this arena's spawn location
|
||||
*
|
||||
* <p>The spawn location is the location every player starts from when entering the dropper.</p>
|
||||
*
|
||||
* @return <p>This arena's spawn location.</p>
|
||||
*/
|
||||
public @NotNull Location getSpawnLocation() {
|
||||
return this.spawnLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this arena's exit location
|
||||
*
|
||||
* @return <p>This arena's exit location, or null if no such location is set.</p>
|
||||
*/
|
||||
public @Nullable Location getExitLocation() {
|
||||
return this.exitLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the velocity for players in this arena
|
||||
*
|
||||
* <p>The velocity is the multiplier used to define players' dropping speed in this dropper arena. 1.0 is the normal
|
||||
* falling speed. 0.5 is half speed. 2 is double speed etc.</p>
|
||||
*
|
||||
* @return <p>Players' velocity in this arena</p>
|
||||
*/
|
||||
public double getPlayerVelocity() {
|
||||
return this.playerVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
//TODO: Add the appropriate getters/setters and other methods
|
||||
|
||||
}
|
@ -1,91 +0,0 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.util.ArenaStorageHelper;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A handler that keeps track of all dropper arenas
|
||||
*/
|
||||
public class DropperArenaHandler {
|
||||
|
||||
private static final File arenaFile = new File(Dropper.getInstance().getDataFolder(), "arenas.yml");
|
||||
|
||||
private List<DropperArena> arenas = new ArrayList<>();
|
||||
private final Map<Player, Integer> stagesCleared = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Tries to register the given stage as cleared
|
||||
*
|
||||
* @param player <p>The player that cleared a stage</p>
|
||||
* @param stage <p>The stage the player cleared</p>
|
||||
* @return <p>True if the player cleared a new stage</p>
|
||||
*/
|
||||
public boolean registerStageCleared(@NotNull Player player, int stage) {
|
||||
if ((!stagesCleared.containsKey(player) && stage == 1) || (stagesCleared.containsKey(player) &&
|
||||
stage == stagesCleared.get(player) + 1)) {
|
||||
stagesCleared.put(player, stage);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new arena
|
||||
*
|
||||
* @param arena <p>The arena to add</p>
|
||||
*/
|
||||
public void addArena(@NotNull DropperArena arena) {
|
||||
this.arenas.add(arena);
|
||||
this.saveArenas();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all known arenas
|
||||
*
|
||||
* @return <p>All known arenas</p>
|
||||
*/
|
||||
public @NotNull List<DropperArena> getArenas() {
|
||||
return new ArrayList<>(this.arenas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given arena
|
||||
*
|
||||
* @param arena <p>The arena to remove</p>
|
||||
*/
|
||||
public void removeArena(@NotNull DropperArena arena) {
|
||||
this.arenas.remove(arena);
|
||||
this.saveArenas();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all current arenas to disk
|
||||
*/
|
||||
public void saveArenas() {
|
||||
try {
|
||||
ArenaStorageHelper.saveArenas(this.arenas, arenaFile);
|
||||
} catch (IOException e) {
|
||||
Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to save current arenas! " +
|
||||
"Data loss can occur!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all arenas from disk
|
||||
*/
|
||||
public void loadArenas() {
|
||||
this.arenas = ArenaStorageHelper.loadArenas(arenaFile);
|
||||
}
|
||||
|
||||
}
|
@ -1,46 +0,0 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A registry to keep track of which players are playing in which arenas
|
||||
*/
|
||||
public class DropperArenaPlayerRegistry {
|
||||
|
||||
private final Map<UUID, DropperArenaSession> arenaPlayers = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Registers that the given player has started playing the given dropper arena session
|
||||
*
|
||||
* @param playerId <p>The id of the player that started playing</p>
|
||||
* @param arena <p>The arena session to register</p>
|
||||
*/
|
||||
public void registerPlayer(@NotNull UUID playerId, @NotNull DropperArenaSession arena) {
|
||||
this.arenaPlayers.put(playerId, arena);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this player from players currently playing
|
||||
*
|
||||
* @param playerId <p>The id of the player to remove</p>
|
||||
*/
|
||||
public boolean removePlayer(@NotNull UUID playerId) {
|
||||
return this.arenaPlayers.remove(playerId) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player's active dropper arena session
|
||||
*
|
||||
* @param playerId <p>The id of the player to get arena for</p>
|
||||
* @return <p>The player's active arena session, or null if not currently playing</p>
|
||||
*/
|
||||
public @Nullable DropperArenaSession getArenaSession(@NotNull UUID playerId) {
|
||||
return this.arenaPlayers.getOrDefault(playerId, null);
|
||||
}
|
||||
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.property.RecordResult;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A registry keeping track of all records
|
||||
*/
|
||||
public class DropperArenaRecordsRegistry {
|
||||
|
||||
private final Map<Player, Integer> leastDeaths;
|
||||
private final Map<Player, Long> shortestTimeMilliSeconds;
|
||||
|
||||
/**
|
||||
* Instantiates a new empty records registry
|
||||
*/
|
||||
public DropperArenaRecordsRegistry() {
|
||||
this.leastDeaths = new HashMap<>();
|
||||
this.shortestTimeMilliSeconds = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new records registry
|
||||
*
|
||||
* @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 Map<Player, Integer> leastDeaths,
|
||||
@NotNull Map<Player, Long> shortestTimeMilliSeconds) {
|
||||
this.leastDeaths = new HashMap<>(leastDeaths);
|
||||
this.shortestTimeMilliSeconds = new HashMap<>(shortestTimeMilliSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all existing death records
|
||||
*
|
||||
* @return <p>Existing death records</p>
|
||||
*/
|
||||
public Map<Player, Integer> getLeastDeathsRecords() {
|
||||
return new HashMap<>(this.leastDeaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all existing time records
|
||||
*
|
||||
* @return <p>Existing time records</p>
|
||||
*/
|
||||
public Map<Player, Long> getShortestTimeMilliSecondsRecords() {
|
||||
return new HashMap<>(this.shortestTimeMilliSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new deaths-record
|
||||
*
|
||||
* @param player <p>The player that performed the records</p>
|
||||
* @param deaths <p>The number of deaths suffered before the player finished the arena</p>
|
||||
* @return <p>The result explaining what type of record was achieved</p>
|
||||
*/
|
||||
public @NotNull RecordResult registerDeathRecord(@NotNull Player player, int deaths) {
|
||||
RecordResult result;
|
||||
Stream<Map.Entry<Player, Integer>> records = leastDeaths.entrySet().stream();
|
||||
|
||||
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(player, deaths);
|
||||
} else if (leastDeaths.containsKey(player) && deaths < leastDeaths.get(player)) {
|
||||
//If the given value is less than the player's previous value, that's a personal best!
|
||||
result = RecordResult.PERSONAL_BEST;
|
||||
leastDeaths.put(player, deaths);
|
||||
} else {
|
||||
result = RecordResult.NONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new time-record
|
||||
*
|
||||
* @param player <p>The player that performed the records</p>
|
||||
* @param milliseconds <p>The number of milliseconds it took the player to finish the dropper arena</p>
|
||||
* @return <p>The result explaining what type of record was achieved</p>
|
||||
*/
|
||||
public @NotNull RecordResult registerTimeRecord(@NotNull Player player, long milliseconds) {
|
||||
RecordResult result;
|
||||
Stream<Map.Entry<Player, Long>> records = shortestTimeMilliSeconds.entrySet().stream();
|
||||
|
||||
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(player, milliseconds);
|
||||
} else if (shortestTimeMilliSeconds.containsKey(player) && milliseconds < shortestTimeMilliSeconds.get(player)) {
|
||||
//If the given value is less than the player's previous value, that's a personal best!
|
||||
result = RecordResult.PERSONAL_BEST;
|
||||
shortestTimeMilliSeconds.put(player, milliseconds);
|
||||
} else {
|
||||
result = RecordResult.NONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
@ -1,161 +0,0 @@
|
||||
package net.knarcraft.dropper.arena;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import net.knarcraft.dropper.property.RecordResult;
|
||||
import net.knarcraft.dropper.util.PlayerTeleporter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A representation of a player's current session in a dropper arena
|
||||
*/
|
||||
public class DropperArenaSession {
|
||||
|
||||
private final @NotNull DropperArena arena;
|
||||
private final @NotNull Player player;
|
||||
private final @NotNull ArenaGameMode gameMode;
|
||||
private final @NotNull Location entryLocation;
|
||||
private int deaths;
|
||||
private final long startTime;
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena session
|
||||
*
|
||||
* @param dropperArena <p>The arena that's being played in</p>
|
||||
* @param player <p>The player playing the arena</p>
|
||||
* @param gameMode <p>The game-mode</p>
|
||||
*/
|
||||
public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player,
|
||||
@NotNull ArenaGameMode gameMode) {
|
||||
this.arena = dropperArena;
|
||||
this.player = player;
|
||||
this.gameMode = gameMode;
|
||||
this.deaths = 0;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
this.entryLocation = player.getLocation();
|
||||
// Prevent Spigot interference when traveling at high velocities
|
||||
player.setAllowFlight(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a win for the player playing in this session
|
||||
*/
|
||||
public void triggerWin() {
|
||||
// Remove this session from game sessions to stop listeners from fiddling more with the player
|
||||
removeSession();
|
||||
|
||||
// No longer allow the player to avoid fly checks
|
||||
player.setAllowFlight(false);
|
||||
|
||||
// Check for, and display, records
|
||||
registerRecord();
|
||||
|
||||
//TODO: Give reward?
|
||||
|
||||
// Register and announce any cleared stages
|
||||
Integer arenaStage = arena.getStage();
|
||||
if (arenaStage != null) {
|
||||
boolean clearedNewStage = Dropper.getInstance().getArenaHandler().registerStageCleared(player, arenaStage);
|
||||
if (clearedNewStage) {
|
||||
player.sendMessage("You cleared stage " + arenaStage + "!");
|
||||
}
|
||||
}
|
||||
|
||||
player.sendMessage("You won!");
|
||||
|
||||
// Teleport the player out of the arena
|
||||
teleportToExit();
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports the playing player out of the arena
|
||||
*/
|
||||
private void teleportToExit() {
|
||||
// Teleport the player out of the arena
|
||||
Location exitLocation;
|
||||
if (arena.getExitLocation() != null) {
|
||||
exitLocation = arena.getExitLocation();
|
||||
} else {
|
||||
exitLocation = entryLocation;
|
||||
}
|
||||
PlayerTeleporter.teleportPlayer(player, exitLocation, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes this session from current sessions
|
||||
*/
|
||||
private void removeSession() {
|
||||
// Remove this session for game sessions to stop listeners from fiddling more with the player
|
||||
boolean removedSession = Dropper.getInstance().getPlayerRegistry().removePlayer(player.getUniqueId());
|
||||
if (!removedSession) {
|
||||
Dropper.getInstance().getLogger().log(Level.SEVERE, "Unable to remove dropper arena session for " +
|
||||
player.getName() + ". This will have unintended consequences.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the player's record if necessary, and prints record information to the player
|
||||
*/
|
||||
private void registerRecord() {
|
||||
DropperArenaRecordsRegistry recordsRegistry = arena.getRecordsRegistry();
|
||||
RecordResult recordResult = switch (gameMode) {
|
||||
case LEAST_TIME -> recordsRegistry.registerTimeRecord(player,
|
||||
System.currentTimeMillis() - startTime);
|
||||
case LEAST_DEATHS -> recordsRegistry.registerDeathRecord(player, deaths);
|
||||
case DEFAULT -> RecordResult.NONE;
|
||||
};
|
||||
switch (recordResult) {
|
||||
case WORLD_RECORD -> player.sendMessage("You just set a new record for this arena!");
|
||||
case PERSONAL_BEST -> player.sendMessage("You just got a new personal record!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a loss for the player playing in this session
|
||||
*/
|
||||
public void triggerLoss() {
|
||||
// Add to the death count if playing the least-deaths game-mode
|
||||
if (gameMode == ArenaGameMode.LEAST_DEATHS) {
|
||||
deaths++;
|
||||
}
|
||||
//Teleport the player back to the top
|
||||
PlayerTeleporter.teleportPlayer(player, arena.getSpawnLocation(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a quit for the player playing in this session
|
||||
*/
|
||||
public void triggerQuit() {
|
||||
// Remove this session from game sessions to stop listeners from fiddling more with the player
|
||||
removeSession();
|
||||
// No longer allow the player to avoid fly checks
|
||||
player.setAllowFlight(false);
|
||||
// Teleport the player out of the arena
|
||||
teleportToExit();
|
||||
|
||||
player.sendMessage("You quit the arena!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena this session is being played in
|
||||
*
|
||||
* @return <p>The session's arena</p>
|
||||
*/
|
||||
public @NotNull DropperArena getArena() {
|
||||
return this.arena;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player playing in this session
|
||||
*
|
||||
* @return <p>This session's player</p>
|
||||
*/
|
||||
public @NotNull Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.util.ArenaStorageHelper;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command for creating a new dropper arena
|
||||
*/
|
||||
public class CreateArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage("This command must be used by a player");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Abort if no name was specified
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String arenaName = arguments[0];
|
||||
String sanitized = ArenaStorageHelper.sanitizeArenaName(arenaName);
|
||||
|
||||
for (DropperArena arena : Dropper.getInstance().getArenaHandler().getArenas()) {
|
||||
if (sanitized.equals(ArenaStorageHelper.sanitizeArenaName(arena.getArenaName()))) {
|
||||
commandSender.sendMessage("There already exists a dropper arena with that name!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Make sure the arena name doesn't contain any unwanted characters
|
||||
|
||||
DropperArena arena = new DropperArena(arenaName, player.getLocation());
|
||||
Dropper.getInstance().getArenaHandler().addArena(arena);
|
||||
commandSender.sendMessage("The arena was successfully created!");
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command for editing an existing dropper arena
|
||||
*/
|
||||
public class EditArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] strings) {
|
||||
//TODO: Make sure the console cannot run this
|
||||
//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;
|
||||
}
|
||||
|
||||
}
|
@ -1,24 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
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.List;
|
||||
|
||||
/**
|
||||
* The tab-completer for the edit arena command
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import net.knarcraft.dropper.property.ArenaGameMode;
|
||||
import net.knarcraft.dropper.util.ArenaStorageHelper;
|
||||
import net.knarcraft.dropper.util.PlayerTeleporter;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command used to join a dropper arena
|
||||
*/
|
||||
public class JoinArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage("This command must be used by a player");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (player.isFlying() || player.getGameMode() == GameMode.CREATIVE ||
|
||||
player.getGameMode() == GameMode.SPECTATOR) {
|
||||
commandSender.sendMessage("You cannot join a dropper arena while able to fly!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow joining if the player is already in a dropper arena
|
||||
DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId());
|
||||
if (existingSession != null) {
|
||||
commandSender.sendMessage("You are already in a dropper arena!");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the arena exists
|
||||
String arenaName = ArenaStorageHelper.sanitizeArenaName(arguments[0]);
|
||||
DropperArena specifiedArena = null;
|
||||
for (DropperArena arena : Dropper.getInstance().getArenaHandler().getArenas()) {
|
||||
if (ArenaStorageHelper.sanitizeArenaName(arena.getArenaName()).equals(arenaName)) {
|
||||
specifiedArena = arena;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage("Unable to find the specified dropper arena.");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Find the specified game-mode
|
||||
ArenaGameMode gameMode;
|
||||
if (arguments.length > 1) {
|
||||
gameMode = ArenaGameMode.matchGamemode(arguments[1]);
|
||||
} else {
|
||||
gameMode = ArenaGameMode.DEFAULT;
|
||||
}
|
||||
|
||||
//TODO: Check if the arena has been beaten if the non-default game-mode has been chosen
|
||||
|
||||
// Register the player's session
|
||||
DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode);
|
||||
DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry();
|
||||
playerRegistry.registerPlayer(player.getUniqueId(), newSession);
|
||||
|
||||
// Try to teleport the player to the arena
|
||||
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false);
|
||||
if (!teleported) {
|
||||
commandSender.sendMessage("Unable to teleport you to the dropper arena. Make sure you're not in a vehicle," +
|
||||
"and is not carrying a passenger!");
|
||||
newSession.triggerQuit();
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
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;
|
||||
|
||||
/**
|
||||
* The tab-completer for the join command
|
||||
*/
|
||||
public class JoinArenaTabCompleter implements TabCompleter {
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] args) {
|
||||
if (args.length == 1) {
|
||||
List<String> arenaNames = new ArrayList<>();
|
||||
for (DropperArena dropperArena : Dropper.getInstance().getArenaHandler().getArenas()) {
|
||||
arenaNames.add(dropperArena.getArenaName());
|
||||
}
|
||||
return arenaNames;
|
||||
} else if (args.length == 2) {
|
||||
List<String> gameModes = new ArrayList<>();
|
||||
gameModes.add("default");
|
||||
gameModes.add("deaths");
|
||||
gameModes.add("time");
|
||||
return gameModes;
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command used to leave the current dropper arena
|
||||
*/
|
||||
public class LeaveArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] strings) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage("This command must be used by a player");
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaSession existingSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId());
|
||||
if (existingSession == null) {
|
||||
commandSender.sendMessage("You are not in a dropper arena!");
|
||||
return false;
|
||||
}
|
||||
|
||||
existingSession.triggerQuit();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -1,20 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A command for listing existing dropper arenas
|
||||
*/
|
||||
public class ListArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
@NotNull String[] args) {
|
||||
//TODO: List all existing arenas, and possibly information about a specified arena
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package net.knarcraft.dropper.command;
|
||||
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The method used for removing an existing arena
|
||||
*/
|
||||
public class RemoveArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] strings) {
|
||||
//TODO: Make sure to kick any playing players if the arena is currently in use, by triggering their sessions'
|
||||
// triggerQuit() method
|
||||
//TODO: Remove the arena from DropperArenaHandler
|
||||
//TODO: Notify the user of success
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
@ -1,39 +0,0 @@
|
||||
package net.knarcraft.dropper.listener;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
|
||||
/**
|
||||
* A listener for checking if a player takes damage within a dropper arena
|
||||
*/
|
||||
public class DamageListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDamage(EntityDamageEvent event) {
|
||||
// Only player damage matters
|
||||
if (event.getEntityType() != EntityType.PLAYER) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) event.getEntity();
|
||||
|
||||
// We don't care about damage outside arenas
|
||||
DropperArenaSession arenaSession = Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId());
|
||||
if (arenaSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
// Only trigger a loss when a player suffers fall damage
|
||||
if (event.getCause() == EntityDamageEvent.DamageCause.FALL) {
|
||||
arenaSession.triggerLoss();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,65 +0,0 @@
|
||||
package net.knarcraft.dropper.listener;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
|
||||
/**
|
||||
* A listener for players moving inside a dropper arena
|
||||
*/
|
||||
public class MoveListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerMove(PlayerMoveEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
DropperArenaPlayerRegistry playerRegistry = Dropper.getInstance().getPlayerRegistry();
|
||||
DropperArenaSession arenaSession = playerRegistry.getArenaSession(player.getUniqueId());
|
||||
if (arenaSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Block targetBlock = event.getTo().getBlock();
|
||||
Material targetBlockType = targetBlock.getType();
|
||||
|
||||
// Hitting water is the trigger for winning
|
||||
if (targetBlockType == Material.WATER) {
|
||||
arenaSession.triggerWin();
|
||||
return;
|
||||
}
|
||||
|
||||
Location targetLocation = targetBlock.getLocation();
|
||||
Material beneathPlayerType = targetLocation.getWorld().getBlockAt(targetLocation.add(0, -0.1, 0)).getType();
|
||||
|
||||
// If hitting something which is not air or water, it must be a solid block, and would end in a loss
|
||||
if (!targetBlockType.isAir() || (beneathPlayerType != Material.WATER &&
|
||||
!beneathPlayerType.isAir())) {
|
||||
arenaSession.triggerLoss();
|
||||
return;
|
||||
}
|
||||
|
||||
//Updates the player's velocity to the one set by the arena
|
||||
updatePlayerVelocity(arenaSession);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the velocity of the player in the given session
|
||||
*
|
||||
* @param session <p>The session to update the velocity for</p>
|
||||
*/
|
||||
private void updatePlayerVelocity(DropperArenaSession session) {
|
||||
Player player = session.getPlayer();
|
||||
Vector playerVelocity = player.getVelocity();
|
||||
double arenaVelocity = session.getArena().getPlayerVelocity();
|
||||
Vector newVelocity = new Vector(playerVelocity.getX(), -arenaVelocity, playerVelocity.getZ());
|
||||
player.setVelocity(newVelocity);
|
||||
}
|
||||
|
||||
}
|
@ -1,64 +0,0 @@
|
||||
package net.knarcraft.dropper.listener;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArenaSession;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerQuitEvent;
|
||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A listener for players leaving the server or the arena
|
||||
*/
|
||||
public class PlayerLeaveListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerLeave(PlayerQuitEvent event) {
|
||||
triggerQuit(event.getPlayer());
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerTeleport(PlayerTeleportEvent event) {
|
||||
DropperArenaSession arenaSession = getSession(event.getPlayer());
|
||||
if (arenaSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getTo().equals(arenaSession.getArena().getSpawnLocation())) {
|
||||
return;
|
||||
}
|
||||
|
||||
triggerQuit(event.getPlayer());
|
||||
}
|
||||
|
||||
/**
|
||||
* Forces the given player to quit their current arena
|
||||
*
|
||||
* @param player <p>The player to trigger a quit for</p>
|
||||
*/
|
||||
private void triggerQuit(Player player) {
|
||||
DropperArenaSession arenaSession = getSession(player);
|
||||
if (arenaSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
arenaSession.triggerQuit();
|
||||
|
||||
//TODO: It might not be possible to alter a leaving player's location here. It might be necessary to move them once
|
||||
// they join again
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena session for the given player
|
||||
*
|
||||
* @param player <p>The player to get the arena session for</p>
|
||||
* @return <p>The player's session, or null if not in a session</p>
|
||||
*/
|
||||
private @Nullable DropperArenaSession getSession(@NotNull Player player) {
|
||||
return Dropper.getInstance().getPlayerRegistry().getArenaSession(player.getUniqueId());
|
||||
}
|
||||
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
package net.knarcraft.dropper.property;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A representation of possible arena game-modes
|
||||
*/
|
||||
public enum ArenaGameMode {
|
||||
|
||||
/**
|
||||
* The default game-mode. Failing once throws the player out.
|
||||
* //TODO: Verify if we want the default game-mode to lock the player in the arena until they beat it
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* The least-deaths game-mode. Player plays until they manage to win. The number of deaths is recorded.
|
||||
*/
|
||||
LEAST_DEATHS,
|
||||
|
||||
/**
|
||||
* The least-time game-mode. Player plays until they manage to win. The total time of the session is recorded.
|
||||
*/
|
||||
LEAST_TIME,
|
||||
;
|
||||
|
||||
/**
|
||||
* Tries to match the correct game-mode according to the given string
|
||||
*
|
||||
* @param gameMode <p>The game-mode string to match</p>
|
||||
* @return <p>The specified arena game-mode</p>
|
||||
*/
|
||||
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;
|
||||
} else {
|
||||
return ArenaGameMode.DEFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,37 +0,0 @@
|
||||
package net.knarcraft.dropper.property;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A representation of each key used for storing arena data
|
||||
*/
|
||||
public enum ArenaStorageKey {
|
||||
|
||||
NAME("arenaName"),
|
||||
SPAWN_LOCATION("arenaSpawnLocation"),
|
||||
EXIT_LOCATION("arenaExitLocation"),
|
||||
PLAYER_VELOCITY("arenaPlayerVelocity"),
|
||||
STAGE("arenaStage"),
|
||||
;
|
||||
|
||||
private final @NotNull String key;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena storage key
|
||||
*
|
||||
* @param key <p>The string path of the configuration key this value represents.</p>
|
||||
*/
|
||||
ArenaStorageKey(@NotNull String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration key this enum represents
|
||||
*
|
||||
* @return <p>The string key representation.</p>
|
||||
*/
|
||||
public @NotNull String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
}
|
@ -1,119 +0,0 @@
|
||||
package net.knarcraft.dropper.util;
|
||||
|
||||
import net.knarcraft.dropper.Dropper;
|
||||
import net.knarcraft.dropper.arena.DropperArena;
|
||||
import net.knarcraft.dropper.arena.DropperArenaRecordsRegistry;
|
||||
import net.knarcraft.dropper.property.ArenaStorageKey;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.bukkit.configuration.file.YamlConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A helper class for saving and loading arenas
|
||||
*/
|
||||
public final class ArenaStorageHelper {
|
||||
|
||||
private final static String arenasConfigurationSection = "arenas";
|
||||
|
||||
private ArenaStorageHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the given arenas to the given file
|
||||
*
|
||||
* @param arenas <p>The arenas to save</p>
|
||||
* @param arenaFile <p>The file to save the arenas to</p>
|
||||
* @throws IOException <p>If unable to write to the file</p>
|
||||
*/
|
||||
public static void saveArenas(@NotNull List<DropperArena> arenas, @NotNull File arenaFile) throws IOException {
|
||||
YamlConfiguration configuration = new YamlConfiguration();
|
||||
ConfigurationSection arenaSection = configuration.createSection(arenasConfigurationSection);
|
||||
for (DropperArena arena : arenas) {
|
||||
//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()));
|
||||
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_VELOCITY.getKey(), arena.getPlayerVelocity());
|
||||
configSection.set(ArenaStorageKey.STAGE.getKey(), arena.getStage());
|
||||
}
|
||||
//TODO: Save records belonging to the arena
|
||||
configuration.save(arenaFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all arenas from the given file
|
||||
*
|
||||
* @param arenaFile <p>The file used to store the arenas</p>
|
||||
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
|
||||
*/
|
||||
public static @NotNull List<DropperArena> loadArenas(@NotNull File arenaFile) {
|
||||
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaFile);
|
||||
ConfigurationSection arenaSection = configuration.getConfigurationSection(arenasConfigurationSection);
|
||||
//If no such section exists, it must be the case that there is no data to load
|
||||
if (arenaSection == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<DropperArena> loadedArenas = new ArrayList<>();
|
||||
|
||||
for (String sectionName : arenaSection.getKeys(false)) {
|
||||
ConfigurationSection configurationSection = arenaSection.getConfigurationSection(sectionName);
|
||||
//I'm not sure whether this could actually happen
|
||||
if (configurationSection == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
DropperArena arena = loadArena(configurationSection);
|
||||
if (arena != null) {
|
||||
loadedArenas.add(arena);
|
||||
}
|
||||
}
|
||||
|
||||
return loadedArenas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads an arena from the given configuration section
|
||||
*
|
||||
* @param configurationSection <p>The configuration section containing arena data</p>
|
||||
* @return <p>The loaded arena, or null if invalid</p>
|
||||
*/
|
||||
private static @Nullable DropperArena loadArena(@NotNull ConfigurationSection configurationSection) {
|
||||
String arenaName = configurationSection.getString(ArenaStorageKey.NAME.getKey());
|
||||
Location spawnLocation = (Location) configurationSection.get(ArenaStorageKey.SPAWN_LOCATION.getKey());
|
||||
Location exitLocation = (Location) configurationSection.get(ArenaStorageKey.EXIT_LOCATION.getKey());
|
||||
double playerVelocity = configurationSection.getDouble(ArenaStorageKey.PLAYER_VELOCITY.getKey());
|
||||
Integer stage = (Integer) configurationSection.get(ArenaStorageKey.STAGE.getKey());
|
||||
if (arenaName == null || spawnLocation == null) {
|
||||
Dropper.getInstance().getLogger().log(Level.SEVERE, "Could not load the arena at configuration " +
|
||||
"section " + configurationSection.getName() + ". Please check the arenas storage file for issues.");
|
||||
return null;
|
||||
}
|
||||
//TODO: Load records for this arena
|
||||
return new DropperArena(arenaName, spawnLocation, exitLocation, playerVelocity, stage,
|
||||
new DropperArenaRecordsRegistry());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(" ", "_");
|
||||
}
|
||||
|
||||
}
|
314
src/main/java/net/knarcraft/minigames/MiniGames.java
Normal file
314
src/main/java/net/knarcraft/minigames/MiniGames.java
Normal file
@ -0,0 +1,314 @@
|
||||
package net.knarcraft.minigames;
|
||||
|
||||
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.arena.ArenaSession;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaData;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperPlayerEntryState;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaData;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourPlayerEntryState;
|
||||
import net.knarcraft.minigames.arena.record.IntegerRecord;
|
||||
import net.knarcraft.minigames.arena.record.LongRecord;
|
||||
import net.knarcraft.minigames.command.LeaveArenaCommand;
|
||||
import net.knarcraft.minigames.command.ReloadCommand;
|
||||
import net.knarcraft.minigames.command.dropper.CreateDropperArenaCommand;
|
||||
import net.knarcraft.minigames.command.dropper.DropperGroupListCommand;
|
||||
import net.knarcraft.minigames.command.dropper.DropperGroupSetCommand;
|
||||
import net.knarcraft.minigames.command.dropper.DropperGroupSwapCommand;
|
||||
import net.knarcraft.minigames.command.dropper.EditDropperArenaCommand;
|
||||
import net.knarcraft.minigames.command.dropper.EditDropperArenaTabCompleter;
|
||||
import net.knarcraft.minigames.command.dropper.JoinDropperArenaCommand;
|
||||
import net.knarcraft.minigames.command.dropper.JoinDropperArenaTabCompleter;
|
||||
import net.knarcraft.minigames.command.dropper.ListDropperArenaCommand;
|
||||
import net.knarcraft.minigames.command.dropper.RemoveDropperArenaCommand;
|
||||
import net.knarcraft.minigames.command.dropper.RemoveDropperArenaTabCompleter;
|
||||
import net.knarcraft.minigames.command.parkour.CreateParkourArenaCommand;
|
||||
import net.knarcraft.minigames.command.parkour.EditParkourArenaCommand;
|
||||
import net.knarcraft.minigames.command.parkour.EditParkourArenaTabCompleter;
|
||||
import net.knarcraft.minigames.command.parkour.JoinParkourArenaCommand;
|
||||
import net.knarcraft.minigames.command.parkour.JoinParkourArenaTabCompleter;
|
||||
import net.knarcraft.minigames.command.parkour.ListParkourArenaCommand;
|
||||
import net.knarcraft.minigames.command.parkour.ParkourGroupListCommand;
|
||||
import net.knarcraft.minigames.command.parkour.ParkourGroupSetCommand;
|
||||
import net.knarcraft.minigames.command.parkour.ParkourGroupSwapCommand;
|
||||
import net.knarcraft.minigames.command.parkour.RemoveParkourArenaCommand;
|
||||
import net.knarcraft.minigames.command.parkour.RemoveParkourArenaTabCompleter;
|
||||
import net.knarcraft.minigames.config.DropperConfiguration;
|
||||
import net.knarcraft.minigames.config.ParkourConfiguration;
|
||||
import net.knarcraft.minigames.config.SharedConfiguration;
|
||||
import net.knarcraft.minigames.container.SerializableMaterial;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import net.knarcraft.minigames.listener.CommandListener;
|
||||
import net.knarcraft.minigames.listener.DamageListener;
|
||||
import net.knarcraft.minigames.listener.MoveListener;
|
||||
import net.knarcraft.minigames.listener.PlayerStateChangeListener;
|
||||
import net.knarcraft.minigames.placeholder.DropperRecordExpansion;
|
||||
import net.knarcraft.minigames.placeholder.ParkourRecordExpansion;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.PluginCommand;
|
||||
import org.bukkit.command.TabCompleter;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerialization;
|
||||
import org.bukkit.plugin.PluginManager;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* The dropper plugin's main class
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class MiniGames extends JavaPlugin {
|
||||
|
||||
private static MiniGames instance;
|
||||
private SharedConfiguration sharedConfiguration;
|
||||
private DropperConfiguration dropperConfiguration;
|
||||
private ParkourConfiguration parkourConfiguration;
|
||||
private DropperArenaHandler dropperArenaHandler;
|
||||
private ArenaPlayerRegistry<DropperArena> dropperArenaPlayerRegistry;
|
||||
private DropperRecordExpansion dropperRecordExpansion;
|
||||
private ParkourRecordExpansion parkourRecordExpansion;
|
||||
private ParkourArenaHandler parkourArenaHandler;
|
||||
private ArenaPlayerRegistry<ParkourArena> parkourArenaPlayerRegistry;
|
||||
|
||||
/**
|
||||
* Gets an instance of this plugin
|
||||
*
|
||||
* @return <p>An instance of this plugin, or null if not initialized yet.</p>
|
||||
*/
|
||||
public static MiniGames getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dropper arena handler for this instance
|
||||
*
|
||||
* @return <p>A dropper arena handler</p>
|
||||
*/
|
||||
public DropperArenaHandler getDropperArenaHandler() {
|
||||
return this.dropperArenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parkour arena handler for this instance
|
||||
*
|
||||
* @return <p>A parkour arena handler</p>
|
||||
*/
|
||||
public ParkourArenaHandler getParkourArenaHandler() {
|
||||
return this.parkourArenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dropper arena player registry for this instance
|
||||
*
|
||||
* @return <p>A dropper arena player registry</p>
|
||||
*/
|
||||
public ArenaPlayerRegistry<DropperArena> getDropperArenaPlayerRegistry() {
|
||||
return this.dropperArenaPlayerRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parkour arena player registry for this instance
|
||||
*
|
||||
* @return <p>A parkour arena player registry</p>
|
||||
*/
|
||||
public ArenaPlayerRegistry<ParkourArena> getParkourArenaPlayerRegistry() {
|
||||
return this.parkourArenaPlayerRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the shared configuration
|
||||
*
|
||||
* <p>The configuration for options which don't affect specific types of mini-games.</p>
|
||||
*
|
||||
* @return <p>The shared configuration</p>
|
||||
*/
|
||||
public SharedConfiguration getSharedConfiguration() {
|
||||
return this.sharedConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dropper configuration
|
||||
*
|
||||
* @return <p>The dropper configuration</p>
|
||||
*/
|
||||
public DropperConfiguration getDropperConfiguration() {
|
||||
return this.dropperConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parkour configuration
|
||||
*
|
||||
* @return <p>The parkour configuration</p>
|
||||
*/
|
||||
public ParkourConfiguration getParkourConfiguration() {
|
||||
return this.parkourConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current session of the given player
|
||||
*
|
||||
* @param playerId <p>The id of the player to get a session for</p>
|
||||
* @return <p>The player's current session, or null if not found</p>
|
||||
*/
|
||||
public @Nullable ArenaSession getSession(@NotNull UUID playerId) {
|
||||
ArenaSession dropperArenaSession = dropperArenaPlayerRegistry.getArenaSession(playerId);
|
||||
if (dropperArenaSession != null) {
|
||||
return dropperArenaSession;
|
||||
}
|
||||
|
||||
return parkourArenaPlayerRegistry.getArenaSession(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs a message
|
||||
*
|
||||
* @param level <p>The message level to log at</p>
|
||||
* @param message <p>The message to log</p>
|
||||
*/
|
||||
public static void log(Level level, String message) {
|
||||
MiniGames.getInstance().getLogger().log(level, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads all configurations and data from disk
|
||||
*/
|
||||
public void reload() {
|
||||
// Load all arenas again
|
||||
this.dropperArenaHandler.load();
|
||||
this.parkourArenaHandler.load();
|
||||
|
||||
// Reload configuration
|
||||
this.reloadConfig();
|
||||
this.sharedConfiguration.load(this.getConfig());
|
||||
this.dropperConfiguration.load(this.getConfig());
|
||||
this.parkourConfiguration.load(this.getConfig());
|
||||
|
||||
// Clear record caches
|
||||
this.dropperRecordExpansion.clearCaches();
|
||||
this.parkourRecordExpansion.clearCaches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad() {
|
||||
super.onLoad();
|
||||
// Register serialization classes
|
||||
ConfigurationSerialization.registerClass(SerializableMaterial.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaRecordsRegistry.class);
|
||||
ConfigurationSerialization.registerClass(SerializableUUID.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaData.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaGroup.class);
|
||||
ConfigurationSerialization.registerClass(DropperArenaGameMode.class);
|
||||
ConfigurationSerialization.registerClass(LongRecord.class);
|
||||
ConfigurationSerialization.registerClass(IntegerRecord.class);
|
||||
ConfigurationSerialization.registerClass(ParkourArenaRecordsRegistry.class);
|
||||
ConfigurationSerialization.registerClass(ParkourArenaData.class);
|
||||
ConfigurationSerialization.registerClass(ParkourArenaGroup.class);
|
||||
ConfigurationSerialization.registerClass(ParkourArenaGameMode.class);
|
||||
ConfigurationSerialization.registerClass(DropperPlayerEntryState.class);
|
||||
ConfigurationSerialization.registerClass(ParkourPlayerEntryState.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEnable() {
|
||||
// Plugin startup logic
|
||||
instance = this;
|
||||
this.saveDefaultConfig();
|
||||
getConfig().options().copyDefaults(true);
|
||||
saveConfig();
|
||||
reloadConfig();
|
||||
this.sharedConfiguration = new SharedConfiguration(this.getConfig());
|
||||
this.dropperConfiguration = new DropperConfiguration(this.getConfig());
|
||||
this.parkourConfiguration = new ParkourConfiguration(this.getConfig());
|
||||
this.dropperArenaPlayerRegistry = new DropperArenaPlayerRegistry();
|
||||
this.dropperArenaHandler = new DropperArenaHandler(this.dropperArenaPlayerRegistry);
|
||||
this.dropperArenaHandler.load();
|
||||
this.parkourArenaPlayerRegistry = new ParkourArenaPlayerRegistry();
|
||||
this.parkourArenaHandler = new ParkourArenaHandler(this.parkourArenaPlayerRegistry);
|
||||
this.parkourArenaHandler.load();
|
||||
|
||||
PluginManager pluginManager = getServer().getPluginManager();
|
||||
pluginManager.registerEvents(new DamageListener(), this);
|
||||
pluginManager.registerEvents(new MoveListener(this.dropperConfiguration, this.parkourConfiguration), this);
|
||||
pluginManager.registerEvents(new PlayerStateChangeListener(), this);
|
||||
pluginManager.registerEvents(new CommandListener(), this);
|
||||
|
||||
registerCommand("miniGamesReload", new ReloadCommand(), null);
|
||||
registerCommand("miniGamesLeave", new LeaveArenaCommand(), null);
|
||||
|
||||
registerCommand("dropperCreate", new CreateDropperArenaCommand(), null);
|
||||
registerCommand("dropperList", new ListDropperArenaCommand(), null);
|
||||
registerCommand("dropperJoin", new JoinDropperArenaCommand(), new JoinDropperArenaTabCompleter());
|
||||
registerCommand("dropperEdit", new EditDropperArenaCommand(this.dropperConfiguration), new EditDropperArenaTabCompleter());
|
||||
registerCommand("dropperRemove", new RemoveDropperArenaCommand(), new RemoveDropperArenaTabCompleter());
|
||||
registerCommand("dropperGroupSet", new DropperGroupSetCommand(), null);
|
||||
registerCommand("dropperGroupSwap", new DropperGroupSwapCommand(), null);
|
||||
registerCommand("dropperGroupList", new DropperGroupListCommand(), null);
|
||||
|
||||
registerCommand("parkourCreate", new CreateParkourArenaCommand(), null);
|
||||
registerCommand("parkourList", new ListParkourArenaCommand(), null);
|
||||
registerCommand("parkourJoin", new JoinParkourArenaCommand(), new JoinParkourArenaTabCompleter());
|
||||
registerCommand("parkourEdit", new EditParkourArenaCommand(), new EditParkourArenaTabCompleter());
|
||||
registerCommand("parkourRemove", new RemoveParkourArenaCommand(), new RemoveParkourArenaTabCompleter());
|
||||
registerCommand("parkourGroupSet", new ParkourGroupSetCommand(), null);
|
||||
registerCommand("parkourGroupSwap", new ParkourGroupSwapCommand(), null);
|
||||
registerCommand("parkourGroupList", new ParkourGroupListCommand(), null);
|
||||
|
||||
if (Bukkit.getPluginManager().getPlugin("PlaceholderAPI") != null) {
|
||||
this.dropperRecordExpansion = new DropperRecordExpansion(this);
|
||||
if (!this.dropperRecordExpansion.register()) {
|
||||
log(Level.WARNING, "Unable to register PlaceholderAPI dropper expansion!");
|
||||
}
|
||||
this.parkourRecordExpansion = new ParkourRecordExpansion(this);
|
||||
if (!this.parkourRecordExpansion.register()) {
|
||||
log(Level.WARNING, "Unable to register PlaceholderAPI parkour expansion!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
// Kill all sessions before exiting
|
||||
for (DropperArena arena : dropperArenaHandler.getArenas().values()) {
|
||||
dropperArenaPlayerRegistry.removeForArena(arena, true);
|
||||
}
|
||||
for (ParkourArena arena : parkourArenaHandler.getArenas().values()) {
|
||||
parkourArenaPlayerRegistry.removeForArena(arena, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a command
|
||||
*
|
||||
* @param commandName <p>The name of the command to register (defined in plugin.yml)</p>
|
||||
* @param commandExecutor <p>The executor for the command</p>
|
||||
* @param tabCompleter <p>The tab-completer to use, or null</p>
|
||||
*/
|
||||
private void registerCommand(@NotNull String commandName, @NotNull CommandExecutor commandExecutor,
|
||||
@Nullable TabCompleter tabCompleter) {
|
||||
PluginCommand command = this.getCommand(commandName);
|
||||
if (command != null) {
|
||||
command.setExecutor(commandExecutor);
|
||||
if (tabCompleter != null) {
|
||||
command.setTabCompleter(tabCompleter);
|
||||
}
|
||||
} else {
|
||||
log(Level.SEVERE, "Unable to register the command " + commandName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.util.ArenaStorageHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* A player registry to keep track of currently playing players
|
||||
*
|
||||
* @param <K> <p>The type of arena stored</p>
|
||||
*/
|
||||
public abstract class AbstractArenaPlayerRegistry<K extends Arena> implements ArenaPlayerRegistry<K> {
|
||||
|
||||
private final Map<UUID, ArenaSession> arenaPlayers = new HashMap<>();
|
||||
private final Map<UUID, PlayerEntryState> entryStates = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Instantiates a new arena player registry
|
||||
*/
|
||||
public AbstractArenaPlayerRegistry() {
|
||||
loadEntryStates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PlayerEntryState getEntryState(@NotNull UUID playerId) {
|
||||
return this.entryStates.get(playerId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerPlayer(@NotNull UUID playerId, @NotNull ArenaSession arenaSession) {
|
||||
this.arenaPlayers.put(playerId, arenaSession);
|
||||
this.entryStates.put(playerId, arenaSession.getEntryState());
|
||||
this.saveEntryStates();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePlayer(@NotNull UUID playerId, boolean restoreState) {
|
||||
// Try and restore the state. If it cannot be restored, retain the entry state
|
||||
PlayerEntryState entryState = this.entryStates.remove(playerId);
|
||||
if (restoreState) {
|
||||
if (entryState.restore()) {
|
||||
this.saveEntryStates();
|
||||
} else {
|
||||
this.entryStates.put(playerId, entryState);
|
||||
}
|
||||
} else {
|
||||
this.saveEntryStates();
|
||||
}
|
||||
|
||||
return this.arenaPlayers.remove(playerId) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ArenaSession getArenaSession(@NotNull UUID playerId) {
|
||||
return this.arenaPlayers.getOrDefault(playerId, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeForArena(K arena, boolean immediately) {
|
||||
for (Map.Entry<UUID, ArenaSession> entry : this.arenaPlayers.entrySet()) {
|
||||
if (entry.getValue().getArena() == arena) {
|
||||
// Kick the player gracefully
|
||||
entry.getValue().triggerQuit(immediately);
|
||||
this.arenaPlayers.remove(entry.getKey());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a string key unique to this type of player registry
|
||||
*
|
||||
* @return <p>A unique key used for entry state storage</p>
|
||||
*/
|
||||
protected abstract String getEntryStateStorageKey();
|
||||
|
||||
/**
|
||||
* Saves all entry states to disk
|
||||
*/
|
||||
private void saveEntryStates() {
|
||||
ArenaStorageHelper.storeArenaPlayerEntryStates(getEntryStateStorageKey(), new HashSet<>(entryStates.values()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all entry states from disk
|
||||
*/
|
||||
private void loadEntryStates() {
|
||||
this.entryStates.clear();
|
||||
Set<PlayerEntryState> entryStates = ArenaStorageHelper.getArenaPlayerEntryStates(getEntryStateStorageKey());
|
||||
for (PlayerEntryState entryState : entryStates) {
|
||||
this.entryStates.put(entryState.getPlayerId(), entryState);
|
||||
}
|
||||
if (this.entryStates.size() > 0) {
|
||||
MiniGames.log(Level.WARNING, entryStates.size() + " un-exited sessions found. This happens if " +
|
||||
"players leave in the middle of a game, or if the server crashes. MiniGames will do its best " +
|
||||
"to fix the players' states.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,107 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.container.PlaceholderContainer;
|
||||
import net.knarcraft.minigames.property.RecordResult;
|
||||
import net.knarcraft.minigames.util.PlayerTeleporter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public abstract class AbstractArenaSession implements ArenaSession {
|
||||
|
||||
private final @NotNull Arena arena;
|
||||
private final @NotNull ArenaGameMode gameMode;
|
||||
private final @NotNull Player player;
|
||||
protected int deaths;
|
||||
protected final long startTime;
|
||||
protected PlayerEntryState entryState;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract arena session
|
||||
*
|
||||
* @param arena <p>The arena that's being played in</p>
|
||||
* @param player <p>The player playing the arena</p>
|
||||
* @param gameMode <p>The game-mode</p>
|
||||
*/
|
||||
public AbstractArenaSession(@NotNull Arena arena, @NotNull Player player, @NotNull ArenaGameMode gameMode) {
|
||||
this.arena = arena;
|
||||
this.player = player;
|
||||
this.gameMode = gameMode;
|
||||
this.deaths = 0;
|
||||
this.startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerQuit(boolean immediately) {
|
||||
// Stop this session
|
||||
removeSession();
|
||||
// Teleport the player out of the arena
|
||||
teleportToExit(immediately);
|
||||
|
||||
player.sendMessage(Message.SUCCESS_ARENA_QUIT.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
*/
|
||||
protected void announceRecord(@NotNull RecordResult recordResult, @NotNull String type) {
|
||||
if (recordResult == RecordResult.NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Gets a string representation of the played game-mode
|
||||
String gameModeString = getGameModeString();
|
||||
|
||||
Message recordInfoMessage = switch (recordResult) {
|
||||
case WORLD_RECORD -> Message.RECORD_ACHIEVED_GLOBAL;
|
||||
case PERSONAL_BEST -> Message.RECORD_ACHIEVED_PERSONAL;
|
||||
default -> throw new IllegalStateException("Unexpected value: " + recordResult);
|
||||
};
|
||||
String recordInfo = recordInfoMessage.getPartialMessage("{recordType}", type);
|
||||
|
||||
PlaceholderContainer placeholderContainer = new PlaceholderContainer().add("{gameMode}", gameModeString);
|
||||
placeholderContainer.add("{recordInfo}", recordInfo);
|
||||
player.sendMessage(Message.SUCCESS_RECORD_ACHIEVED.getMessage(placeholderContainer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the player's record if necessary, and prints record information to the player
|
||||
*/
|
||||
protected void registerRecord() {
|
||||
ArenaRecordsRegistry recordsRegistry = this.arena.getData().getRecordRegistries().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");
|
||||
}
|
||||
|
||||
/**
|
||||
* Teleports the playing player out of the arena
|
||||
*/
|
||||
protected void teleportToExit(boolean immediately) {
|
||||
// Teleport the player out of the arena
|
||||
Location exitLocation;
|
||||
if (this.arena.getExitLocation() != null) {
|
||||
exitLocation = this.arena.getExitLocation();
|
||||
} else {
|
||||
exitLocation = this.entryState.getEntryLocation();
|
||||
}
|
||||
PlayerTeleporter.teleportPlayer(this.player, exitLocation, true, immediately);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the string representation of the session's game-mode
|
||||
*
|
||||
* @return <p>The string representation</p>
|
||||
*/
|
||||
protected abstract String getGameModeString();
|
||||
|
||||
/**
|
||||
* Removes this session from current sessions
|
||||
*/
|
||||
protected abstract void removeSession();
|
||||
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.potion.PotionEffect;
|
||||
import org.bukkit.potion.PotionEffectType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* An abstract representation of a player's entry state
|
||||
*/
|
||||
public abstract class AbstractPlayerEntryState implements PlayerEntryState {
|
||||
|
||||
protected final UUID playerId;
|
||||
private final boolean makePlayerInvisible;
|
||||
private final Location entryLocation;
|
||||
private final boolean originalIsFlying;
|
||||
private final GameMode originalGameMode;
|
||||
private final boolean originalAllowFlight;
|
||||
private final boolean originalInvulnerable;
|
||||
private final boolean originalIsSwimming;
|
||||
private final boolean originalCollideAble;
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract player entry state
|
||||
*
|
||||
* @param player <p>The player whose state this should keep track of</p>
|
||||
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
|
||||
*/
|
||||
public AbstractPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
|
||||
this.playerId = player.getUniqueId();
|
||||
this.makePlayerInvisible = makePlayerInvisible;
|
||||
this.entryLocation = player.getLocation().clone();
|
||||
this.originalIsFlying = player.isFlying();
|
||||
this.originalGameMode = player.getGameMode();
|
||||
this.originalAllowFlight = player.getAllowFlight();
|
||||
this.originalInvulnerable = player.isInvulnerable();
|
||||
this.originalIsSwimming = player.isSwimming();
|
||||
this.originalCollideAble = player.isCollidable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new abstract player entry state
|
||||
*
|
||||
* @param playerId <p>The id of the player whose state this should keep track of</p>
|
||||
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
|
||||
* @param entryLocation <p>The location the player entered from</p>
|
||||
* @param originalIsFlying <p>Whether the player was flying before entering the arena</p>
|
||||
* @param originalGameMode <p>The game-mode of the player before entering the arena</p>
|
||||
* @param originalAllowFlight <p>Whether the player was allowed flight before entering the arena</p>
|
||||
* @param originalInvulnerable <p>Whether the player was invulnerable before entering the arena</p>
|
||||
* @param originalIsSwimming <p>Whether the player was swimming before entering the arena</p>
|
||||
* @param originalCollideAble <p>Whether the player was collide-able before entering the arena</p>
|
||||
*/
|
||||
public AbstractPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation,
|
||||
boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight,
|
||||
boolean originalInvulnerable, boolean originalIsSwimming,
|
||||
boolean originalCollideAble) {
|
||||
this.playerId = playerId;
|
||||
this.makePlayerInvisible = makePlayerInvisible;
|
||||
this.entryLocation = entryLocation;
|
||||
this.originalIsFlying = originalIsFlying;
|
||||
this.originalGameMode = originalGameMode;
|
||||
this.originalAllowFlight = originalAllowFlight;
|
||||
this.originalInvulnerable = originalInvulnerable;
|
||||
this.originalIsSwimming = originalIsSwimming;
|
||||
this.originalCollideAble = originalCollideAble;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getPlayerId() {
|
||||
return this.playerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArenaState() {
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
if (this.makePlayerInvisible) {
|
||||
player.addPotionEffect(new PotionEffect(PotionEffectType.INVISIBILITY,
|
||||
PotionEffect.INFINITE_DURATION, 3));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore() {
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
restore(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restore(@NotNull Player player) {
|
||||
player.setFlying(this.originalIsFlying);
|
||||
player.setGameMode(this.originalGameMode);
|
||||
player.setAllowFlight(this.originalAllowFlight);
|
||||
player.setInvulnerable(this.originalInvulnerable);
|
||||
player.setSwimming(this.originalIsSwimming);
|
||||
player.setCollidable(this.originalCollideAble);
|
||||
if (this.makePlayerInvisible) {
|
||||
player.removePotionEffect(PotionEffectType.INVISIBILITY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Location getEntryLocation() {
|
||||
return this.entryLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player this entry state belongs to
|
||||
*
|
||||
* @return <p>The player, or null if not currently online</p>
|
||||
*/
|
||||
protected Player getPlayer() {
|
||||
Player player = Bukkit.getOfflinePlayer(this.playerId).getPlayer();
|
||||
if (player == null) {
|
||||
MiniGames.log(Level.WARNING, "Unable to change state for player with id " + this.playerId +
|
||||
" because the player was not found on the server.");
|
||||
}
|
||||
return player;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("playerId", new SerializableUUID(this.playerId));
|
||||
data.put("makePlayerInvisible", this.makePlayerInvisible);
|
||||
data.put("entryLocation", this.entryLocation);
|
||||
data.put("originalIsFlying", this.originalIsFlying);
|
||||
data.put("originalGameMode", this.originalGameMode.name());
|
||||
data.put("originalAllowFlight", this.originalAllowFlight);
|
||||
data.put("originalInvulnerable", this.originalInvulnerable);
|
||||
data.put("originalIsSwimming", this.originalIsSwimming);
|
||||
data.put("originalCollideAble", this.originalCollideAble);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
94
src/main/java/net/knarcraft/minigames/arena/Arena.java
Normal file
94
src/main/java/net/knarcraft/minigames/arena/Arena.java
Normal file
@ -0,0 +1,94 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* An interface describing an arena
|
||||
*/
|
||||
public interface Arena {
|
||||
|
||||
/**
|
||||
* Gets the name of this arena
|
||||
*
|
||||
* @return <p>The name of this arena</p>
|
||||
*/
|
||||
@NotNull String getArenaName();
|
||||
|
||||
/**
|
||||
* Gets the data stored for this arena
|
||||
*
|
||||
* @return <p>The stored data</p>
|
||||
*/
|
||||
@NotNull ArenaData getData();
|
||||
|
||||
/**
|
||||
* Gets the id of this arena
|
||||
*
|
||||
* @return <p>This arena's identifier</p>
|
||||
*/
|
||||
@NotNull UUID getArenaId();
|
||||
|
||||
/**
|
||||
* Gets this arena's sanitized name
|
||||
*
|
||||
* @return <p>This arena's sanitized name</p>
|
||||
*/
|
||||
@NotNull String getArenaNameSanitized();
|
||||
|
||||
/**
|
||||
* Removes the data file belonging to this arena
|
||||
*
|
||||
* @return <p>True if successfully removed</p>
|
||||
*/
|
||||
boolean removeData();
|
||||
|
||||
/**
|
||||
* Saves this arena's data
|
||||
*
|
||||
* @return <p>True if successfully saved</p>
|
||||
*/
|
||||
boolean saveData();
|
||||
|
||||
/**
|
||||
* Gets whether standing on the given block should cause a win
|
||||
*
|
||||
* @param block <p>The block to check</p>
|
||||
* @return <p>True if standing on the block will cause a win</p>
|
||||
*/
|
||||
boolean willCauseWin(Block block);
|
||||
|
||||
/**
|
||||
* Gets whether standing on the given block should cause a loss
|
||||
*
|
||||
* @param block <p>The block to check</p>
|
||||
* @return <p>True if standing on the block will cause a loss</p>
|
||||
*/
|
||||
boolean willCauseLoss(Block block);
|
||||
|
||||
/**
|
||||
* Gets whether the win location is a solid block
|
||||
*
|
||||
* @return <p>True if the location is a solid block</p>
|
||||
*/
|
||||
boolean winLocationIsSolid();
|
||||
|
||||
/**
|
||||
* Gets the location of this arena's spawn
|
||||
*
|
||||
* @return <p>This arena's spawn location</p>
|
||||
*/
|
||||
@NotNull Location getSpawnLocation();
|
||||
|
||||
/**
|
||||
* Gets this arena's exit location
|
||||
*
|
||||
* @return <p>This arena's exit location, or null if no such location is set.</p>
|
||||
*/
|
||||
@Nullable Location getExitLocation();
|
||||
|
||||
}
|
108
src/main/java/net/knarcraft/minigames/arena/ArenaData.java
Normal file
108
src/main/java/net/knarcraft/minigames/arena/ArenaData.java
Normal file
@ -0,0 +1,108 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.container.SerializableContainer;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.knarcraft.minigames.util.SerializableConverter.makeSerializable;
|
||||
|
||||
/**
|
||||
* An interface describing generic arena data
|
||||
*/
|
||||
public abstract class ArenaData implements ConfigurationSerializable {
|
||||
|
||||
protected final @NotNull UUID arenaId;
|
||||
private final @NotNull Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries;
|
||||
private final @NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted;
|
||||
|
||||
/**
|
||||
* Instantiates arena data
|
||||
*
|
||||
* @param arenaId <p>The id of the arena this data belongs to</p>
|
||||
* @param recordRegistries <p>The registry storing records for this arena</p>
|
||||
* @param playersCompleted <p>The players that have completed this arena</p>
|
||||
*/
|
||||
public ArenaData(@NotNull UUID arenaId, @NotNull Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries,
|
||||
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) {
|
||||
this.arenaId = arenaId;
|
||||
this.recordRegistries = recordRegistries;
|
||||
this.playersCompleted = playersCompleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of this arena
|
||||
*
|
||||
* @return <p>The id of this arena</p>
|
||||
*/
|
||||
public @NotNull UUID getArenaId() {
|
||||
return this.arenaId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all record registries
|
||||
*
|
||||
* @return <p>All record registries</p>
|
||||
*/
|
||||
public @NotNull Map<ArenaGameMode, ArenaRecordsRegistry> getRecordRegistries() {
|
||||
return new HashMap<>(this.recordRegistries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the given player has cleared this arena
|
||||
*
|
||||
* @param arenaGameMode <p>The game-mode to check for</p>
|
||||
* @param player <p>The player to check</p>
|
||||
* @return <p>True if the player has cleared the arena this data belongs to</p>
|
||||
*/
|
||||
public boolean hasNotCompleted(@NotNull ArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
return !this.playersCompleted.getOrDefault(arenaGameMode, new HashSet<>()).contains(player.getUniqueId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the given player as having completed this arena
|
||||
*
|
||||
* @param arenaGameMode <p>The game-mode the player completed</p>
|
||||
* @param player <p>The player that completed this data's arena</p>
|
||||
*/
|
||||
public boolean setCompleted(@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) {
|
||||
saveData();
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves this data to disk
|
||||
*/
|
||||
public abstract void saveData();
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("arenaId", new SerializableUUID(this.arenaId));
|
||||
data.put("recordsRegistry", this.recordRegistries);
|
||||
|
||||
// Convert normal UUIDs to serializable UUIDs
|
||||
Map<ArenaGameMode, Set<SerializableContainer<UUID>>> serializablePlayersCompleted = new HashMap<>();
|
||||
makeSerializable(this.playersCompleted, serializablePlayersCompleted, new SerializableUUID(null));
|
||||
data.put("playersCompleted", serializablePlayersCompleted);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An interface describing any arena game-mode
|
||||
*/
|
||||
public interface ArenaGameMode {
|
||||
|
||||
/**
|
||||
* Gets the name of this game-mode
|
||||
*
|
||||
* @return <p>The name of this game-mode</p>
|
||||
*/
|
||||
@NotNull String name();
|
||||
|
||||
/**
|
||||
* Gets a set of all available arena game-modes in the type definition of this game-mode
|
||||
*
|
||||
* @return <p>All game-modes in this game-mode's class</p>
|
||||
*/
|
||||
@NotNull ArenaGameMode[] getValues();
|
||||
|
||||
}
|
244
src/main/java/net/knarcraft/minigames/arena/ArenaGroup.java
Normal file
244
src/main/java/net/knarcraft/minigames/arena/ArenaGroup.java
Normal file
@ -0,0 +1,244 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import net.knarcraft.minigames.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 group containing a list of arenas
|
||||
*
|
||||
* @param <K> <p>The type of arena stored</p>
|
||||
* @param <S> <p>The type of arena group stored in the given arena handler</p>
|
||||
*/
|
||||
public abstract class ArenaGroup<K extends Arena, S extends ArenaGroup<K, S>> 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 arena handler used to convert uuids to arenas
|
||||
*/
|
||||
private final ArenaHandler<K, S> arenaHandler;
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* @param arenaHandler <p>The arena handler used to convert uuids to arenas</p>
|
||||
*/
|
||||
protected ArenaGroup(@NotNull String groupName, @NotNull ArenaHandler<K, S> arenaHandler) {
|
||||
this.groupId = UUID.randomUUID();
|
||||
this.groupName = groupName;
|
||||
this.arenas = new ArrayList<>();
|
||||
this.arenaHandler = arenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new 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>
|
||||
* @param arenaHandler <p>The arena handler used to convert uuids to arenas</p>
|
||||
*/
|
||||
protected ArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas,
|
||||
@NotNull ArenaHandler<K, S> arenaHandler) {
|
||||
this.groupId = groupId;
|
||||
this.groupName = groupName;
|
||||
this.arenas = new ArrayList<>(arenas);
|
||||
this.arenaHandler = arenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of this arena group
|
||||
*
|
||||
* @return <p>The id of this group</p>
|
||||
*/
|
||||
public @NotNull UUID getGroupId() {
|
||||
return this.groupId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the name of this 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 arena from this group
|
||||
*
|
||||
* @param arenaId <p>The id of the 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this group's name, but sanitized
|
||||
*
|
||||
* @return <p>The sanitized group name</p>
|
||||
*/
|
||||
public @NotNull String getGroupNameSanitized() {
|
||||
return StringSanitizer.sanitizeArenaName(this.getGroupName());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
for (UUID anArenaId : this.getArenas()) {
|
||||
K arena = this.arenaHandler.getArena(anArenaId);
|
||||
if (arena == null) {
|
||||
// The arena would only be null if the arena has been deleted, but not removed from this group
|
||||
MiniGames.log(Level.WARNING, "The dropper group " + this.getGroupName() +
|
||||
" contains the arena id " + anArenaId + " which is not a valid arena id!");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arena.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 cannotPlay(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!");
|
||||
}
|
||||
|
||||
for (UUID anArenaId : this.getArenas()) {
|
||||
// If the target arena is reached, allow, as all previous arenas must have been cleared
|
||||
if (arenaId.equals(anArenaId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
K arena = this.arenaHandler.getArena(anArenaId);
|
||||
if (arena == null) {
|
||||
// The arena would only be null if the arena has been deleted, but not removed from this group
|
||||
MiniGames.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 (arena.getData().hasNotCompleted(gameMode, player)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ArenaGroup<?, ?> otherGroup)) {
|
||||
return false;
|
||||
}
|
||||
return this.getGroupNameSanitized().equals(otherGroup.getGroupNameSanitized());
|
||||
}
|
||||
|
||||
}
|
230
src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java
Normal file
230
src/main/java/net/knarcraft/minigames/arena/ArenaHandler.java
Normal file
@ -0,0 +1,230 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* An interface describing a generic arena handler
|
||||
*
|
||||
* @param <K> <p>The type of arena stored</p>
|
||||
* @param <S> <p>The type of arena group stored</p>
|
||||
*/
|
||||
public abstract class ArenaHandler<K extends Arena, S extends ArenaGroup<K, S>> {
|
||||
|
||||
protected Map<UUID, K> arenas = new HashMap<>();
|
||||
protected Map<UUID, S> arenaGroups = new HashMap<>();
|
||||
protected Map<String, UUID> arenaNameLookup = new HashMap<>();
|
||||
private final ArenaPlayerRegistry<K> playerRegistry;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena handler
|
||||
*
|
||||
* @param playerRegistry <p>The registry keeping track of player sessions</p>
|
||||
*/
|
||||
public ArenaHandler(ArenaPlayerRegistry<K> playerRegistry) {
|
||||
this.playerRegistry = playerRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all arenas that are within a group
|
||||
*
|
||||
* @return <p>All arenas in a group</p>
|
||||
*/
|
||||
public @NotNull Set<K> getArenasInAGroup() {
|
||||
Set<K> arenas = new HashSet<>();
|
||||
for (UUID arenaId : arenaGroups.keySet()) {
|
||||
arenas.add(this.arenas.get(arenaId));
|
||||
}
|
||||
return arenas;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a copy of all arena groups
|
||||
*
|
||||
* @return <p>All arena groups</p>
|
||||
*/
|
||||
public Set<S> 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 S 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 S arenaGroup) {
|
||||
if (arenaGroup == null) {
|
||||
// No need to remove something non-existing
|
||||
if (!this.arenaGroups.containsKey(arenaId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove the existing group
|
||||
S oldGroup = this.arenaGroups.remove(arenaId);
|
||||
oldGroup.removeArena(arenaId);
|
||||
} else {
|
||||
// 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 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 S getGroup(String groupName) {
|
||||
String sanitized = StringSanitizer.sanitizeArenaName(groupName);
|
||||
for (S 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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new arena
|
||||
*
|
||||
* @param arena <p>The arena to add</p>
|
||||
*/
|
||||
public void addArena(@NotNull K 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 K getArena(@NotNull UUID arenaId) {
|
||||
return this.arenas.get(arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the arena with the given name
|
||||
*
|
||||
* @param arenaName <p>The arena to get</p>
|
||||
* @return <p>The arena with the given name, or null if not found</p>
|
||||
*/
|
||||
public @Nullable K getArena(@NotNull String arenaName) {
|
||||
return this.arenas.get(this.arenaNameLookup.get(StringSanitizer.sanitizeArenaName(arenaName)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all known arenas
|
||||
*
|
||||
* @return <p>All known arenas</p>
|
||||
*/
|
||||
public @NotNull Map<UUID, K> getArenas() {
|
||||
return new HashMap<>(this.arenas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given arena
|
||||
*
|
||||
* @param arena <p>The arena to remove</p>
|
||||
*/
|
||||
public void removeArena(@NotNull K arena) {
|
||||
UUID arenaId = arena.getArenaId();
|
||||
this.playerRegistry.removeForArena(arena, false);
|
||||
this.arenas.remove(arenaId);
|
||||
this.arenaNameLookup.remove(arena.getArenaNameSanitized());
|
||||
this.arenaGroups.remove(arenaId);
|
||||
if (!arena.removeData()) {
|
||||
MiniGames.log(Level.WARNING, "Unable to remove arena data file " + arenaId + ".yml. " +
|
||||
"You must remove it manually!");
|
||||
}
|
||||
this.saveArenas();
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the data for the given arena
|
||||
*
|
||||
* @param arenaId <p>The id of the arena whose data should be saved</p>
|
||||
*/
|
||||
public void saveData(UUID arenaId) {
|
||||
K arena = getArena(arenaId);
|
||||
if (arena != null) {
|
||||
if (!arena.saveData()) {
|
||||
MiniGames.log(Level.WARNING, "Unable to save data for arena with id " + arenaId +
|
||||
" because of a write exception!");
|
||||
}
|
||||
} else {
|
||||
MiniGames.log(Level.WARNING, "Unable to save data for arena with id " + arenaId +
|
||||
" because the arena could not be found!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all current groups to disk
|
||||
*/
|
||||
public abstract void saveGroups();
|
||||
|
||||
/**
|
||||
* Loads all groups from disk
|
||||
*/
|
||||
protected abstract void loadGroups();
|
||||
|
||||
/**
|
||||
* Loads all arenas and groups from disk
|
||||
*/
|
||||
public void load() {
|
||||
loadArenas();
|
||||
loadGroups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves all current arenas to disk
|
||||
*/
|
||||
public abstract void saveArenas();
|
||||
|
||||
/**
|
||||
* Loads all arenas from disk
|
||||
*/
|
||||
protected abstract void loadArenas();
|
||||
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A registry keeping track of all player sessions for some arenas
|
||||
*
|
||||
* @param <K> <p>The type of arena this registry stores</p>
|
||||
*/
|
||||
public interface ArenaPlayerRegistry<K extends Arena> {
|
||||
|
||||
/**
|
||||
* Gets the current entry state for the given player
|
||||
*
|
||||
* @param playerId <p>The id of the player to get an entry state for</p>
|
||||
* @return <p>The entry state of the player, or null if not found</p>
|
||||
*/
|
||||
@Nullable PlayerEntryState getEntryState(@NotNull UUID playerId);
|
||||
|
||||
/**
|
||||
* Registers that the given player has started playing the given dropper arena session
|
||||
*
|
||||
* @param playerId <p>The id of the player that started playing</p>
|
||||
* @param arenaSession <p>The arena session to register</p>
|
||||
*/
|
||||
void registerPlayer(@NotNull UUID playerId, @NotNull ArenaSession arenaSession);
|
||||
|
||||
/**
|
||||
* Removes this player from players currently playing
|
||||
*
|
||||
* @param playerId <p>The id of the player to remove</p>
|
||||
* @param restoreState <p>Whether to restore the state of the player as part of the removal</p>
|
||||
*/
|
||||
boolean removePlayer(@NotNull UUID playerId, boolean restoreState);
|
||||
|
||||
/**
|
||||
* Gets the player's active dropper arena session
|
||||
*
|
||||
* @param playerId <p>The id of the player to get arena for</p>
|
||||
* @return <p>The player's active arena session, or null if not currently playing</p>
|
||||
*/
|
||||
@Nullable ArenaSession getArenaSession(@NotNull UUID playerId);
|
||||
|
||||
/**
|
||||
* Removes all active sessions for the given arena
|
||||
*
|
||||
* @param arena <p>The arena to remove sessions for</p>
|
||||
* @param immediately <p>Whether to immediately teleport the player</p>
|
||||
*/
|
||||
void removeForArena(K arena, boolean immediately);
|
||||
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import net.knarcraft.minigames.arena.record.ArenaRecord;
|
||||
import net.knarcraft.minigames.arena.record.IntegerRecord;
|
||||
import net.knarcraft.minigames.arena.record.LongRecord;
|
||||
import net.knarcraft.minigames.arena.record.SummableArenaRecord;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import net.knarcraft.minigames.property.RecordResult;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* A records registry storing records for an arena
|
||||
*/
|
||||
public abstract class ArenaRecordsRegistry implements ConfigurationSerializable {
|
||||
|
||||
protected final UUID arenaId;
|
||||
private final @NotNull Set<IntegerRecord> leastDeaths;
|
||||
private final @NotNull Set<LongRecord> shortestTimeMilliSeconds;
|
||||
|
||||
/**
|
||||
* Instantiates a new empty records registry
|
||||
*/
|
||||
public ArenaRecordsRegistry(@NotNull UUID arenaId) {
|
||||
this.arenaId = arenaId;
|
||||
this.leastDeaths = new HashSet<>();
|
||||
this.shortestTimeMilliSeconds = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new records registry
|
||||
*
|
||||
* @param leastDeaths <p>The existing least death records to use</p>
|
||||
* @param shortestTimeMilliSeconds <p>The existing leash time records to use</p>
|
||||
*/
|
||||
protected ArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set<IntegerRecord> leastDeaths,
|
||||
@NotNull Set<LongRecord> shortestTimeMilliSeconds) {
|
||||
this.arenaId = arenaId;
|
||||
this.leastDeaths = new HashSet<>(leastDeaths);
|
||||
this.shortestTimeMilliSeconds = new HashSet<>(shortestTimeMilliSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all existing death records
|
||||
*
|
||||
* @return <p>Existing death records</p>
|
||||
*/
|
||||
public Set<SummableArenaRecord<Integer>> getLeastDeathsRecords() {
|
||||
return new HashSet<>(this.leastDeaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all existing time records
|
||||
*
|
||||
* @return <p>Existing time records</p>
|
||||
*/
|
||||
public Set<SummableArenaRecord<Long>> getShortestTimeMilliSecondsRecords() {
|
||||
return new HashSet<>(this.shortestTimeMilliSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new deaths-record
|
||||
*
|
||||
* @param playerId <p>The id of the player that performed the records</p>
|
||||
* @param deaths <p>The number of deaths suffered before the player finished the arena</p>
|
||||
* @return <p>The result explaining what type of record was achieved</p>
|
||||
*/
|
||||
public @NotNull RecordResult registerDeathRecord(@NotNull UUID playerId, int deaths) {
|
||||
Consumer<Integer> consumer = (value) -> {
|
||||
leastDeaths.removeIf((item) -> item.getUserId().equals(playerId));
|
||||
leastDeaths.add(new IntegerRecord(playerId, value));
|
||||
};
|
||||
return registerRecord(new HashSet<>(leastDeaths), consumer, playerId, deaths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new time-record
|
||||
*
|
||||
* @param playerId <p>The id of the player that performed the records</p>
|
||||
* @param milliseconds <p>The number of milliseconds it took the player to finish the dropper arena</p>
|
||||
* @return <p>The result explaining what type of record was achieved</p>
|
||||
*/
|
||||
public @NotNull RecordResult registerTimeRecord(@NotNull UUID playerId, long milliseconds) {
|
||||
Consumer<Long> consumer = (value) -> {
|
||||
shortestTimeMilliSeconds.removeIf((item) -> item.getUserId().equals(playerId));
|
||||
shortestTimeMilliSeconds.add(new LongRecord(playerId, value));
|
||||
};
|
||||
return registerRecord(new HashSet<>(shortestTimeMilliSeconds), consumer, playerId, milliseconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changed records
|
||||
*/
|
||||
protected abstract void save();
|
||||
|
||||
/**
|
||||
* Registers a new record if applicable
|
||||
*
|
||||
* @param existingRecords <p>The map of existing records to use</p>
|
||||
* @param recordSetter <p>The consumer used to set a new record</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 <T extends Comparable<T>> @NotNull RecordResult registerRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
|
||||
@NotNull Consumer<T> recordSetter,
|
||||
@NotNull UUID playerId, T amount) {
|
||||
RecordResult result;
|
||||
if (existingRecords.stream().allMatch((entry) -> amount.compareTo(entry.getRecord()) < 0)) {
|
||||
// If the given value is less than all other values, that's a world record!
|
||||
result = RecordResult.WORLD_RECORD;
|
||||
recordSetter.accept(amount);
|
||||
save();
|
||||
return result;
|
||||
}
|
||||
|
||||
ArenaRecord<T> playerRecord = getRecord(existingRecords, playerId);
|
||||
if (playerRecord != null && amount.compareTo(playerRecord.getRecord()) < 0) {
|
||||
// If the given value is less than the player's previous value, that's a personal best!
|
||||
result = RecordResult.PERSONAL_BEST;
|
||||
recordSetter.accept(amount);
|
||||
save();
|
||||
} else {
|
||||
// Make sure to save the record if this is the user's first attempt
|
||||
if (playerRecord == null) {
|
||||
recordSetter.accept(amount);
|
||||
save();
|
||||
}
|
||||
result = RecordResult.NONE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the record stored for the given player
|
||||
*
|
||||
* @param existingRecords <p>The existing records to look through</p>
|
||||
* @param playerId <p>The id of the player to look for</p>
|
||||
* @param <T> <p>The type of the stored record</p>
|
||||
* @return <p>The record, or null if not found</p>
|
||||
*/
|
||||
private <T extends Comparable<T>> @Nullable ArenaRecord<T> getRecord(@NotNull Set<ArenaRecord<T>> existingRecords,
|
||||
@NotNull UUID playerId) {
|
||||
AtomicReference<ArenaRecord<T>> record = new AtomicReference<>();
|
||||
existingRecords.forEach((item) -> {
|
||||
if (item.getUserId().equals(playerId)) {
|
||||
record.set(item);
|
||||
}
|
||||
});
|
||||
return record.get();
|
||||
}
|
||||
|
||||
@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);
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A player's session while in an arena
|
||||
*/
|
||||
public interface ArenaSession {
|
||||
|
||||
/**
|
||||
* Gets the state of the player when they joined the session
|
||||
*
|
||||
* @return <p>The player's entry state</p>
|
||||
*/
|
||||
@NotNull PlayerEntryState getEntryState();
|
||||
|
||||
/**
|
||||
* Triggers a win for the player playing in this session
|
||||
*/
|
||||
void triggerWin();
|
||||
|
||||
/**
|
||||
* Triggers a loss for the player playing in this session
|
||||
*/
|
||||
void triggerLoss();
|
||||
|
||||
/**
|
||||
* Triggers a quit for the player playing in this session
|
||||
*
|
||||
* @param immediately <p>Whether to to the teleportation immediately, not using any timers</p>
|
||||
*/
|
||||
void triggerQuit(boolean immediately);
|
||||
|
||||
/**
|
||||
* Gets the arena this session is being played in
|
||||
*
|
||||
* @return <p>The session's arena</p>
|
||||
*/
|
||||
@NotNull Arena getArena();
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
/**
|
||||
* The type of one editable property
|
||||
*/
|
||||
public enum EditablePropertyType {
|
||||
|
||||
/**
|
||||
* The property is a location
|
||||
*/
|
||||
LOCATION,
|
||||
|
||||
/**
|
||||
* The property is an arena name
|
||||
*/
|
||||
ARENA_NAME,
|
||||
|
||||
/**
|
||||
* The property is a horizontal velocity
|
||||
*/
|
||||
HORIZONTAL_VELOCITY,
|
||||
|
||||
/**
|
||||
* The property is a vertical velocity (fly speed)
|
||||
*/
|
||||
VERTICAL_VELOCITY,
|
||||
|
||||
/**
|
||||
* The property is a material that specifies a block
|
||||
*/
|
||||
BLOCK_TYPE,
|
||||
|
||||
/**
|
||||
* The property clears a checkpoint
|
||||
*/
|
||||
CHECKPOINT_CLEAR,
|
||||
|
||||
/**
|
||||
* The property is a comma-separated list of materials
|
||||
*/
|
||||
MATERIAL_LIST
|
||||
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package net.knarcraft.minigames.arena;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The stored state of a player
|
||||
*/
|
||||
public interface PlayerEntryState extends ConfigurationSerializable {
|
||||
|
||||
/**
|
||||
* Sets the state of the stored player to the state used by the arena
|
||||
*/
|
||||
void setArenaState();
|
||||
|
||||
/**
|
||||
* Restores the stored state for the stored player
|
||||
*/
|
||||
boolean restore();
|
||||
|
||||
/**
|
||||
* Restores the stored state for the given player
|
||||
*
|
||||
* @param player <p>A player object that's refers to the same player as the stored player</p>
|
||||
*/
|
||||
void restore(Player player);
|
||||
|
||||
/**
|
||||
* Gets the id of the player this state belongs to
|
||||
*
|
||||
* @return <p>The player the state belongs to</p>
|
||||
*/
|
||||
UUID getPlayerId();
|
||||
|
||||
/**
|
||||
* Gets the location the player entered from
|
||||
*
|
||||
* @return <p>The location the player entered from</p>
|
||||
*/
|
||||
Location getEntryLocation();
|
||||
|
||||
}
|
@ -0,0 +1,340 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.Arena;
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.config.DropperConfiguration;
|
||||
import net.knarcraft.minigames.util.DropperArenaStorageHelper;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
|
||||
|
||||
/**
|
||||
* A representation of one dropper arena
|
||||
*/
|
||||
public class DropperArena implements Arena {
|
||||
|
||||
/**
|
||||
* An unique and persistent identifier for this arena
|
||||
*/
|
||||
private final UUID arenaId;
|
||||
|
||||
/**
|
||||
* A name used when listing and storing this arena.
|
||||
*/
|
||||
private @NotNull String arenaName;
|
||||
|
||||
/**
|
||||
* The location players are teleported to when joining this arena.
|
||||
*/
|
||||
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 @Nullable Location exitLocation;
|
||||
|
||||
/**
|
||||
* The velocity in the y-direction to apply to all players in this arena.
|
||||
*/
|
||||
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 float playerHorizontalVelocity;
|
||||
|
||||
/**
|
||||
* The material of the block players have to hit to win this dropper arena
|
||||
*/
|
||||
private @NotNull Material winBlockType;
|
||||
|
||||
/**
|
||||
* The arena data for this arena
|
||||
*/
|
||||
private final DropperArenaData dropperArenaData;
|
||||
|
||||
private final DropperArenaHandler dropperArenaHandler;
|
||||
|
||||
private static final DropperConfiguration dropperConfiguration = MiniGames.getInstance().getDropperConfiguration();
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena
|
||||
*
|
||||
* @param arenaId <p>The id of the arena</p>
|
||||
* @param arenaName <p>The name of the arena</p>
|
||||
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
|
||||
* @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 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>
|
||||
* @param arenaHandler <p>The arena handler used for saving any changes</p>
|
||||
*/
|
||||
public DropperArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
|
||||
@Nullable Location exitLocation, double playerVerticalVelocity, float playerHorizontalVelocity,
|
||||
@NotNull Material winBlockType, @NotNull DropperArenaData dropperArenaData,
|
||||
@NotNull DropperArenaHandler arenaHandler) {
|
||||
this.arenaId = arenaId;
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = exitLocation;
|
||||
this.playerVerticalVelocity = playerVerticalVelocity;
|
||||
this.playerHorizontalVelocity = playerHorizontalVelocity;
|
||||
this.winBlockType = winBlockType;
|
||||
this.dropperArenaData = dropperArenaData;
|
||||
this.dropperArenaHandler = arenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena
|
||||
*
|
||||
* <p>Note that this minimal constructor can be used to quickly create a new dropper arena at the player's given
|
||||
* location, simply by them giving an arena name.</p>
|
||||
*
|
||||
* @param arenaName <p>The name of the arena</p>
|
||||
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
|
||||
* @param arenaHandler <p>The arena handler used for saving any changes</p>
|
||||
*/
|
||||
public DropperArena(@NotNull String arenaName, @NotNull Location spawnLocation,
|
||||
@NotNull DropperArenaHandler arenaHandler) {
|
||||
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
|
||||
this.arenaId = UUID.randomUUID();
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = null;
|
||||
this.playerVerticalVelocity = configuration.getVerticalVelocity();
|
||||
this.playerHorizontalVelocity = configuration.getHorizontalVelocity();
|
||||
|
||||
Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
|
||||
for (ArenaGameMode arenaGameMode : DropperArenaGameMode.values()) {
|
||||
recordRegistries.put(arenaGameMode, new DropperArenaRecordsRegistry(this.arenaId));
|
||||
}
|
||||
|
||||
this.dropperArenaData = new DropperArenaData(this.arenaId, recordRegistries, new HashMap<>());
|
||||
this.winBlockType = Material.WATER;
|
||||
this.dropperArenaHandler = arenaHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull DropperArenaData getData() {
|
||||
return this.dropperArenaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getArenaId() {
|
||||
return this.arenaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getArenaName() {
|
||||
return this.arenaName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Location getSpawnLocation() {
|
||||
return this.spawnLocation.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getExitLocation() {
|
||||
return this.exitLocation != null ? this.exitLocation.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the vertical velocity for players in this arena
|
||||
*
|
||||
* <p>This velocity will be set on the negative y-axis, for all players in this arena.</p>
|
||||
*
|
||||
* @return <p>Players' velocity in this arena</p>
|
||||
*/
|
||||
public double getPlayerVerticalVelocity() {
|
||||
return this.playerVerticalVelocity;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Gets the horizontal for players in this arena
|
||||
*
|
||||
* <p>This will be used for players' fly-speed in this arena</p>
|
||||
*
|
||||
* @return <p>Players' velocity in this arena</p>
|
||||
*/
|
||||
public float getPlayerHorizontalVelocity() {
|
||||
return this.playerHorizontalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of block a player has to hit to win this arena
|
||||
*
|
||||
* @return <p>The kind of block players must hit</p>
|
||||
*/
|
||||
public @NotNull Material getWinBlockType() {
|
||||
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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeData() {
|
||||
return DropperArenaStorageHelper.removeDropperArenaData(getArenaId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData() {
|
||||
try {
|
||||
DropperArenaStorageHelper.saveDropperArenaData(getData());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean willCauseWin(Block block) {
|
||||
return block.getType() == winBlockType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean willCauseLoss(Block block) {
|
||||
return !dropperConfiguration.getBlockWhitelist().contains(block.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean winLocationIsSolid() {
|
||||
return winBlockType.isSolid();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaData;
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.container.SerializableContainer;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import net.knarcraft.minigames.util.SerializableConverter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Data stored for an arena
|
||||
*/
|
||||
public class DropperArenaData extends ArenaData {
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena data object
|
||||
*
|
||||
* @param arenaId <p>The id of the arena this data belongs to</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 Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries,
|
||||
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) {
|
||||
super(arenaId, recordRegistries, playersCompleted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a dropper arena data from the given data
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized dropper arena data</p>
|
||||
*/
|
||||
@SuppressWarnings({"unused", "unchecked"})
|
||||
public static @NotNull DropperArenaData deserialize(@NotNull Map<String, Object> data) {
|
||||
SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
|
||||
Map<ArenaGameMode, ArenaRecordsRegistry> recordsRegistry =
|
||||
(Map<ArenaGameMode, ArenaRecordsRegistry>) data.get("recordsRegistry");
|
||||
Map<ArenaGameMode, Set<SerializableContainer<UUID>>> playersCompletedData =
|
||||
(Map<ArenaGameMode, Set<SerializableContainer<UUID>>>) data.get("playersCompleted");
|
||||
|
||||
if (recordsRegistry == null) {
|
||||
recordsRegistry = new HashMap<>();
|
||||
} else if (playersCompletedData == null) {
|
||||
playersCompletedData = new HashMap<>();
|
||||
}
|
||||
|
||||
// Convert the serializable UUIDs to normal UUIDs
|
||||
Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
|
||||
SerializableConverter.getRawValue(playersCompletedData, allPlayersCompleted);
|
||||
|
||||
for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
|
||||
if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
|
||||
recordsRegistry.put(arenaGameMode, new DropperArenaRecordsRegistry(serializableUUID.getRawValue()));
|
||||
}
|
||||
}
|
||||
return new DropperArenaData(serializableUUID.getRawValue(), recordsRegistry, allPlayersCompleted);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.arena.EditablePropertyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* All editable properties of a dropper arena
|
||||
*/
|
||||
public enum DropperArenaEditableProperty {
|
||||
|
||||
/**
|
||||
* The name of the arena
|
||||
*/
|
||||
NAME("name", DropperArena::getArenaName, EditablePropertyType.ARENA_NAME),
|
||||
|
||||
/**
|
||||
* The arena's spawn location
|
||||
*/
|
||||
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation()),
|
||||
EditablePropertyType.LOCATION),
|
||||
|
||||
/**
|
||||
* The arena's exit location
|
||||
*/
|
||||
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation()),
|
||||
EditablePropertyType.LOCATION),
|
||||
|
||||
/**
|
||||
* The arena's vertical velocity
|
||||
*/
|
||||
VERTICAL_VELOCITY("verticalVelocity", (arena) -> String.valueOf(arena.getPlayerVerticalVelocity()),
|
||||
EditablePropertyType.VERTICAL_VELOCITY),
|
||||
|
||||
/**
|
||||
* The arena's horizontal velocity
|
||||
*/
|
||||
HORIZONTAL_VELOCITY("horizontalVelocity", (arena) -> String.valueOf(arena.getPlayerHorizontalVelocity()),
|
||||
EditablePropertyType.HORIZONTAL_VELOCITY),
|
||||
|
||||
/**
|
||||
* The arena's win block type
|
||||
*/
|
||||
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString(),
|
||||
EditablePropertyType.BLOCK_TYPE),
|
||||
;
|
||||
|
||||
private final @NotNull String argumentString;
|
||||
private final Function<DropperArena, String> currentValueProvider;
|
||||
private final EditablePropertyType propertyType;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena editable property
|
||||
*
|
||||
* @param argumentString <p>The argument string used to specify this property</p>
|
||||
*/
|
||||
DropperArenaEditableProperty(@NotNull String argumentString, Function<DropperArena, String> currentValueProvider,
|
||||
EditablePropertyType propertyType) {
|
||||
this.argumentString = argumentString;
|
||||
this.currentValueProvider = currentValueProvider;
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of property this editable property represents
|
||||
*
|
||||
* @return <p>The type of this property</p>
|
||||
*/
|
||||
public EditablePropertyType getPropertyType() {
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 DropperArenaEditableProperty getFromArgumentString(String argumentString) {
|
||||
for (DropperArenaEditableProperty property : DropperArenaEditableProperty.values()) {
|
||||
if (property.argumentString.equalsIgnoreCase(argumentString)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
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 DropperArenaGameMode implements ConfigurationSerializable, ArenaGameMode {
|
||||
|
||||
/**
|
||||
* The default game-mode. Failing once throws the player out.
|
||||
*/
|
||||
DEFAULT,
|
||||
|
||||
/**
|
||||
* A game-mode where the player's directional buttons are inverted
|
||||
*/
|
||||
INVERTED,
|
||||
|
||||
/**
|
||||
* A game-mode which swaps between normal and inverted controls on a set schedule of a few seconds
|
||||
*/
|
||||
RANDOM_INVERTED,
|
||||
;
|
||||
|
||||
/**
|
||||
* Tries to match the correct game-mode according to the given string
|
||||
*
|
||||
* @param gameMode <p>The game-mode string to match</p>
|
||||
* @return <p>The specified arena game-mode</p>
|
||||
*/
|
||||
public static @NotNull DropperArenaGameMode matchGamemode(@NotNull String gameMode) {
|
||||
String sanitized = gameMode.trim().toLowerCase();
|
||||
if (sanitized.matches("(invert(ed)?|inverse)")) {
|
||||
return DropperArenaGameMode.INVERTED;
|
||||
} else if (sanitized.matches("rand(om)?")) {
|
||||
return DropperArenaGameMode.RANDOM_INVERTED;
|
||||
} else {
|
||||
return DropperArenaGameMode.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 DropperArenaGameMode deserialize(Map<String, Object> data) {
|
||||
return DropperArenaGameMode.valueOf((String) data.get("name"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull DropperArenaGameMode[] getValues() {
|
||||
return DropperArenaGameMode.values();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaGroup;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import net.knarcraft.minigames.util.SerializableConverter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A sorted group of arenas that must be completed in sequence
|
||||
*/
|
||||
public class DropperArenaGroup extends ArenaGroup<DropperArena, DropperArenaGroup> {
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena group
|
||||
*
|
||||
* @param groupName <p>The name of this group</p>
|
||||
*/
|
||||
public DropperArenaGroup(@NotNull String groupName) {
|
||||
super(groupName, MiniGames.getInstance().getDropperArenaHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
super(groupId, groupName, arenas, MiniGames.getInstance().getDropperArenaHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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")).getRawValue();
|
||||
String name = (String) data.get("groupName");
|
||||
List<SerializableUUID> serializableArenas = (List<SerializableUUID>) data.get("arenas");
|
||||
List<UUID> arenas = new ArrayList<>();
|
||||
|
||||
SerializableConverter.getRawValue(new ArrayList<>(serializableArenas), arenas);
|
||||
return new DropperArenaGroup(id, name, arenas);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaHandler;
|
||||
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.DropperArenaStorageHelper;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* A handler that keeps track of all dropper arenas
|
||||
*/
|
||||
public class DropperArenaHandler extends ArenaHandler<DropperArena, DropperArenaGroup> {
|
||||
|
||||
/**
|
||||
* Instantiates a new arena handler
|
||||
*
|
||||
* @param playerRegistry <p>The registry keeping track of player sessions</p>
|
||||
*/
|
||||
public DropperArenaHandler(ArenaPlayerRegistry<DropperArena> playerRegistry) {
|
||||
super(playerRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveGroups() {
|
||||
try {
|
||||
DropperArenaStorageHelper.saveDropperArenaGroups(new HashSet<>(this.arenaGroups.values()));
|
||||
} catch (IOException e) {
|
||||
MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage());
|
||||
MiniGames.log(Level.SEVERE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadGroups() {
|
||||
Set<DropperArenaGroup> arenaGroups = DropperArenaStorageHelper.loadDropperArenaGroups();
|
||||
Map<UUID, DropperArenaGroup> arenaGroupMap = new HashMap<>();
|
||||
for (DropperArenaGroup arenaGroup : arenaGroups) {
|
||||
for (UUID arenaId : arenaGroup.getArenas()) {
|
||||
arenaGroupMap.put(arenaId, arenaGroup);
|
||||
}
|
||||
}
|
||||
this.arenaGroups = arenaGroupMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveArenas() {
|
||||
try {
|
||||
DropperArenaStorageHelper.saveDropperArenas(this.arenas);
|
||||
} catch (IOException e) {
|
||||
MiniGames.log(Level.SEVERE, "Unable to save current arenas! " +
|
||||
"Data loss can occur!");
|
||||
MiniGames.log(Level.SEVERE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadArenas() {
|
||||
this.arenas = DropperArenaStorageHelper.loadDropperArenas();
|
||||
|
||||
// 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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
|
||||
|
||||
/**
|
||||
* A registry to keep track of which players are playing in which arenas
|
||||
*/
|
||||
public class DropperArenaPlayerRegistry extends AbstractArenaPlayerRegistry<DropperArena> {
|
||||
|
||||
@Override
|
||||
protected String getEntryStateStorageKey() {
|
||||
return "dropper";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.arena.record.IntegerRecord;
|
||||
import net.knarcraft.minigames.arena.record.LongRecord;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A registry keeping track of all records
|
||||
*/
|
||||
public class DropperArenaRecordsRegistry extends ArenaRecordsRegistry {
|
||||
|
||||
/**
|
||||
* Instantiates a new empty records registry
|
||||
*/
|
||||
public DropperArenaRecordsRegistry(@NotNull UUID arenaId) {
|
||||
super(arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new records registry
|
||||
*
|
||||
* @param leastDeaths <p>The existing least death records to use</p>
|
||||
* @param shortestTimeMilliSeconds <p>The existing leash time records to use</p>
|
||||
*/
|
||||
private DropperArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set<IntegerRecord> leastDeaths,
|
||||
@NotNull Set<LongRecord> shortestTimeMilliSeconds) {
|
||||
super(arenaId, leastDeaths, shortestTimeMilliSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changed records
|
||||
*/
|
||||
protected void save() {
|
||||
MiniGames.getInstance().getDropperArenaHandler().saveData(this.arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the given data
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized records registry</p>
|
||||
*/
|
||||
@SuppressWarnings({"unused", "unchecked"})
|
||||
public static DropperArenaRecordsRegistry deserialize(Map<String, Object> data) {
|
||||
UUID arenaId = ((SerializableUUID) data.get("arenaId")).getRawValue();
|
||||
Set<IntegerRecord> leastDeaths =
|
||||
(Set<IntegerRecord>) data.getOrDefault("leastDeaths", new HashMap<>());
|
||||
Set<LongRecord> shortestTimeMilliseconds =
|
||||
(Set<LongRecord>) data.getOrDefault("shortestTime", new HashMap<>());
|
||||
|
||||
leastDeaths.removeIf(Objects::isNull);
|
||||
shortestTimeMilliseconds.removeIf(Objects::isNull);
|
||||
return new DropperArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.AbstractArenaSession;
|
||||
import net.knarcraft.minigames.arena.PlayerEntryState;
|
||||
import net.knarcraft.minigames.config.DropperConfiguration;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.PlayerTeleporter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A representation of a player's current session in a dropper arena
|
||||
*/
|
||||
public class DropperArenaSession extends AbstractArenaSession {
|
||||
|
||||
private final @NotNull DropperArena arena;
|
||||
private final @NotNull Player player;
|
||||
private final @NotNull DropperArenaGameMode gameMode;
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper arena session
|
||||
*
|
||||
* @param dropperArena <p>The arena that's being played in</p>
|
||||
* @param player <p>The player playing the arena</p>
|
||||
* @param gameMode <p>The game-mode</p>
|
||||
*/
|
||||
public DropperArenaSession(@NotNull DropperArena dropperArena, @NotNull Player player,
|
||||
@NotNull DropperArenaGameMode gameMode) {
|
||||
super(dropperArena, player, gameMode);
|
||||
this.arena = dropperArena;
|
||||
this.player = player;
|
||||
this.gameMode = gameMode;
|
||||
|
||||
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
|
||||
boolean makeInvisible = configuration.makePlayersInvisible();
|
||||
boolean disableCollision = configuration.disableHitCollision();
|
||||
this.entryState = new DropperPlayerEntryState(player, gameMode, makeInvisible, disableCollision,
|
||||
dropperArena.getPlayerHorizontalVelocity());
|
||||
this.entryState.setArenaState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the player playing in this session
|
||||
*
|
||||
* @return <p>This session's player</p>
|
||||
*/
|
||||
public @NotNull Player getPlayer() {
|
||||
return this.player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the game-mode the player is playing in this session
|
||||
*
|
||||
* @return <p>The game-mode for this session</p>
|
||||
*/
|
||||
public @NotNull DropperArenaGameMode getGameMode() {
|
||||
return this.gameMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerEntryState getEntryState() {
|
||||
return this.entryState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerWin() {
|
||||
// Stop this session
|
||||
removeSession();
|
||||
|
||||
// Check for, and display, records
|
||||
MiniGames miniGames = MiniGames.getInstance();
|
||||
boolean ignore = miniGames.getDropperConfiguration().ignoreRecordsUntilGroupBeatenOnce();
|
||||
DropperArenaGroup group = miniGames.getDropperArenaHandler().getGroup(this.arena.getArenaId());
|
||||
if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) {
|
||||
registerRecord();
|
||||
}
|
||||
|
||||
// Mark the arena as cleared
|
||||
if (this.arena.getData().setCompleted(this.gameMode, this.player)) {
|
||||
this.player.sendMessage(Message.SUCCESS_ARENA_FIRST_CLEAR.getMessage());
|
||||
}
|
||||
this.player.sendMessage(Message.SUCCESS_ARENA_WIN.getMessage());
|
||||
|
||||
// Teleport the player out of the arena
|
||||
teleportToExit(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerLoss() {
|
||||
this.deaths++;
|
||||
//Teleport the player back to the top
|
||||
PlayerTeleporter.teleportPlayer(this.player, this.arena.getSpawnLocation(), true, false);
|
||||
this.entryState.setArenaState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull DropperArena getArena() {
|
||||
return this.arena;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeSession() {
|
||||
// Remove this session for game sessions to stop listeners from fiddling more with the player
|
||||
boolean removedSession = MiniGames.getInstance().getDropperArenaPlayerRegistry().removePlayer(
|
||||
player.getUniqueId(), true);
|
||||
if (!removedSession) {
|
||||
MiniGames.log(Level.SEVERE, "Unable to remove dropper arena session for " + player.getName() + ". " +
|
||||
"This will have unintended consequences.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getGameModeString() {
|
||||
return switch (this.gameMode) {
|
||||
case DEFAULT -> "default";
|
||||
case INVERTED -> "inverted";
|
||||
case RANDOM_INVERTED -> "random";
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A representation of each key used for storing arena data
|
||||
*/
|
||||
public enum DropperArenaStorageKey {
|
||||
|
||||
/**
|
||||
* The key for an arena's id
|
||||
*/
|
||||
ID("arenaId"),
|
||||
|
||||
/**
|
||||
* The key for an arena's name
|
||||
*/
|
||||
NAME("arenaName"),
|
||||
|
||||
/**
|
||||
* The key for an arena's spawn location
|
||||
*/
|
||||
SPAWN_LOCATION("arenaSpawnLocation"),
|
||||
|
||||
/**
|
||||
* The key for an arena's exit location
|
||||
*/
|
||||
EXIT_LOCATION("arenaExitLocation"),
|
||||
|
||||
/**
|
||||
* The key for a player in this arena's vertical velocity
|
||||
*/
|
||||
PLAYER_VERTICAL_VELOCITY("arenaPlayerVerticalVelocity"),
|
||||
|
||||
/**
|
||||
* The key for a player in this arena's horizontal velocity
|
||||
*/
|
||||
PLAYER_HORIZONTAL_VELOCITY("arenaPlayerHorizontalVelocity"),
|
||||
|
||||
/**
|
||||
* The key for the type of this arena's win block
|
||||
*/
|
||||
WIN_BLOCK_TYPE("winBlockType"),
|
||||
|
||||
/**
|
||||
* The hey for this arena's data
|
||||
*/
|
||||
DATA("arenaData"),
|
||||
;
|
||||
|
||||
private final @NotNull String key;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena storage key
|
||||
*
|
||||
* @param key <p>The string path of the configuration key this value represents.</p>
|
||||
*/
|
||||
DropperArenaStorageKey(@NotNull String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration key this enum represents
|
||||
*
|
||||
* @return <p>The string key representation.</p>
|
||||
*/
|
||||
public @NotNull String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
package net.knarcraft.minigames.arena.dropper;
|
||||
|
||||
import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The state of a player before entering a dropper arena
|
||||
*/
|
||||
public class DropperPlayerEntryState extends AbstractPlayerEntryState {
|
||||
|
||||
private final float originalFlySpeed;
|
||||
private final boolean disableHitCollision;
|
||||
private final float horizontalVelocity;
|
||||
private final DropperArenaGameMode arenaGameMode;
|
||||
|
||||
/**
|
||||
* Instantiates a new player state
|
||||
*
|
||||
* @param player <p>The player whose state should be stored</p>
|
||||
*/
|
||||
public DropperPlayerEntryState(@NotNull Player player, @NotNull DropperArenaGameMode arenaGameMode,
|
||||
boolean makePlayerInvisible, boolean disableHitCollision, float horizontalVelocity) {
|
||||
super(player, makePlayerInvisible);
|
||||
this.originalFlySpeed = player.getFlySpeed();
|
||||
this.arenaGameMode = arenaGameMode;
|
||||
this.disableHitCollision = disableHitCollision;
|
||||
this.horizontalVelocity = horizontalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour player entry state
|
||||
*
|
||||
* @param playerId <p>The id of the player whose state this should keep track of</p>
|
||||
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
|
||||
* @param entryLocation <p>The location the player entered from</p>
|
||||
* @param originalIsFlying <p>Whether the player was flying before entering the arena</p>
|
||||
* @param originalGameMode <p>The game-mode of the player before entering the arena</p>
|
||||
* @param originalAllowFlight <p>Whether the player was allowed flight before entering the arena</p>
|
||||
* @param originalInvulnerable <p>Whether the player was invulnerable before entering the arena</p>
|
||||
* @param originalIsSwimming <p>Whether the player was swimming before entering the arena</p>
|
||||
* @param originalCollideAble <p>Whether the player was collide-able before entering the arena</p>
|
||||
*/
|
||||
public DropperPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation,
|
||||
boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight,
|
||||
boolean originalInvulnerable, boolean originalIsSwimming,
|
||||
boolean originalCollideAble, float originalFlySpeed, boolean disableHitCollision,
|
||||
float horizontalVelocity, DropperArenaGameMode arenaGameMode) {
|
||||
super(playerId, makePlayerInvisible, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight,
|
||||
originalInvulnerable, originalIsSwimming, originalCollideAble);
|
||||
this.originalFlySpeed = originalFlySpeed;
|
||||
this.disableHitCollision = disableHitCollision;
|
||||
this.horizontalVelocity = horizontalVelocity;
|
||||
this.arenaGameMode = arenaGameMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArenaState() {
|
||||
super.setArenaState();
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
player.setAllowFlight(true);
|
||||
player.setFlying(true);
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
player.setSwimming(false);
|
||||
if (this.disableHitCollision) {
|
||||
player.setCollidable(false);
|
||||
}
|
||||
|
||||
// If playing on the inverted game-mode, negate the horizontal velocity to swap the controls
|
||||
if (this.arenaGameMode == DropperArenaGameMode.INVERTED) {
|
||||
player.setFlySpeed(-this.horizontalVelocity);
|
||||
} else {
|
||||
player.setFlySpeed(this.horizontalVelocity);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean restore() {
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
this.restore(player);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void restore(@NotNull Player player) {
|
||||
super.restore(player);
|
||||
player.setFlySpeed(this.originalFlySpeed);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = super.serialize();
|
||||
data.put("originalFlySpeed", this.originalFlySpeed);
|
||||
data.put("disableHitCollision", this.disableHitCollision);
|
||||
data.put("horizontalVelocity", this.horizontalVelocity);
|
||||
data.put("arenaGameMode", this.arenaGameMode);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a ParkourPlayerEntryState from the given data
|
||||
*
|
||||
* @return <p>The data to deserialize</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static DropperPlayerEntryState deserialize(Map<String, Object> data) {
|
||||
UUID playerId = ((SerializableUUID) data.get("playerId")).getRawValue();
|
||||
boolean makePlayerInvisible = (boolean) data.get("makePlayerInvisible");
|
||||
Location entryLocation = (Location) data.get("entryLocation");
|
||||
boolean originalIsFlying = (boolean) data.get("originalIsFlying");
|
||||
GameMode originalGameMode = GameMode.valueOf((String) data.get("originalGameMode"));
|
||||
boolean originalAllowFlight = (boolean) data.get("originalAllowFlight");
|
||||
boolean originalInvulnerable = (boolean) data.get("originalInvulnerable");
|
||||
boolean originalIsSwimming = (boolean) data.get("originalIsSwimming");
|
||||
boolean originalCollideAble = (boolean) data.get("originalCollideAble");
|
||||
float originalFlySpeed = ((Number) data.get("originalFlySpeed")).floatValue();
|
||||
boolean disableHitCollision = (boolean) data.get("disableHitCollision");
|
||||
float horizontalVelocity = ((Number) data.get("horizontalVelocity")).floatValue();
|
||||
DropperArenaGameMode arenaGameMode = (DropperArenaGameMode) data.get("arenaGameMode");
|
||||
|
||||
return new DropperPlayerEntryState(playerId, makePlayerInvisible, entryLocation, originalIsFlying,
|
||||
originalGameMode, originalAllowFlight, originalInvulnerable, originalIsSwimming, originalCollideAble,
|
||||
originalFlySpeed, disableHitCollision, horizontalVelocity, arenaGameMode);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,409 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.Arena;
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.util.MaterialHelper;
|
||||
import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.knarcraft.minigames.util.InputValidationHelper.isInvalid;
|
||||
|
||||
/**
|
||||
* A representation of one parkour arena
|
||||
*/
|
||||
public class ParkourArena implements Arena {
|
||||
|
||||
/**
|
||||
* An unique and persistent identifier for this arena
|
||||
*/
|
||||
private final @NotNull UUID arenaId;
|
||||
|
||||
/**
|
||||
* A name used when listing and storing this arena.
|
||||
*/
|
||||
private @NotNull String arenaName;
|
||||
|
||||
/**
|
||||
* The location players are teleported to when joining this arena.
|
||||
*/
|
||||
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 @Nullable Location exitLocation;
|
||||
|
||||
/**
|
||||
* The material of the block players have to hit to win this parkour arena
|
||||
*/
|
||||
private @NotNull Material winBlockType;
|
||||
|
||||
/**
|
||||
* The location the player has to reach to win. If not set, winBlockType is used instead
|
||||
*/
|
||||
private @Nullable Location winLocation;
|
||||
|
||||
/**
|
||||
* The names of the block types constituting this arena's kill plane
|
||||
*/
|
||||
private @Nullable Set<String> killPlaneBlockNames;
|
||||
|
||||
/**
|
||||
* The block types constituting this arena's kill plane
|
||||
*/
|
||||
private @Nullable Set<Material> killPlaneBlocks;
|
||||
|
||||
/**
|
||||
* The checkpoints for this arena. Entering a checkpoint overrides the player's spawn location.
|
||||
*/
|
||||
private final @NotNull List<Location> checkpoints;
|
||||
|
||||
/**
|
||||
* The arena data for this arena
|
||||
*/
|
||||
private final @NotNull ParkourArenaData parkourArenaData;
|
||||
|
||||
private final @NotNull ParkourArenaHandler parkourArenaHandler;
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour arena
|
||||
*
|
||||
* @param arenaId <p>The id of the arena</p>
|
||||
* @param arenaName <p>The name of the arena</p>
|
||||
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
|
||||
* @param exitLocation <p>The location the players are teleported to when exiting the arena, or null</p>
|
||||
* @param winBlockType <p>The material of the block players have to hit to win this parkour arena</p>
|
||||
* @param winLocation <p>The location a player has to reach to win this arena</p>
|
||||
* @param parkourArenaData <p>The arena data keeping track of which players have done what in this arena</p>
|
||||
* @param arenaHandler <p>The arena handler used for saving any changes</p>
|
||||
*/
|
||||
public ParkourArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
|
||||
@Nullable Location exitLocation, @NotNull Material winBlockType, @Nullable Location winLocation,
|
||||
@Nullable Set<String> killPlaneBlockNames, @NotNull List<Location> checkpoints,
|
||||
@NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) {
|
||||
this.arenaId = arenaId;
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = exitLocation;
|
||||
this.winBlockType = winBlockType;
|
||||
this.winLocation = winLocation;
|
||||
this.killPlaneBlockNames = killPlaneBlockNames;
|
||||
this.killPlaneBlocks = this.killPlaneBlockNames == null ? null : MaterialHelper.loadMaterialList(
|
||||
new ArrayList<>(killPlaneBlockNames));
|
||||
this.checkpoints = checkpoints;
|
||||
this.parkourArenaData = parkourArenaData;
|
||||
this.parkourArenaHandler = arenaHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour arena
|
||||
*
|
||||
* <p>Note that this minimal constructor can be used to quickly create a new parkour arena at the player's given
|
||||
* location, simply by them giving an arena name.</p>
|
||||
*
|
||||
* @param arenaName <p>The name of the arena</p>
|
||||
* @param spawnLocation <p>The location players spawn in when entering the arena</p>
|
||||
* @param arenaHandler <p>The arena handler used for saving any changes</p>
|
||||
*/
|
||||
public ParkourArena(@NotNull String arenaName, @NotNull Location spawnLocation,
|
||||
@NotNull ParkourArenaHandler arenaHandler) {
|
||||
this.arenaId = UUID.randomUUID();
|
||||
this.arenaName = arenaName;
|
||||
this.spawnLocation = spawnLocation;
|
||||
this.exitLocation = null;
|
||||
this.winLocation = null;
|
||||
|
||||
Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
|
||||
for (ParkourArenaGameMode arenaGameMode : ParkourArenaGameMode.values()) {
|
||||
recordRegistries.put(arenaGameMode, new ParkourArenaRecordsRegistry(this.arenaId));
|
||||
}
|
||||
|
||||
this.parkourArenaData = new ParkourArenaData(this.arenaId, recordRegistries, new HashMap<>());
|
||||
this.winBlockType = Material.EMERALD_BLOCK;
|
||||
this.killPlaneBlocks = null;
|
||||
this.checkpoints = new ArrayList<>();
|
||||
this.parkourArenaHandler = arenaHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ParkourArenaData getData() {
|
||||
return this.parkourArenaData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull UUID getArenaId() {
|
||||
return this.arenaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String getArenaName() {
|
||||
return this.arenaName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Location getSpawnLocation() {
|
||||
return this.spawnLocation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Location getExitLocation() {
|
||||
return this.exitLocation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of block a player has to hit to win this arena
|
||||
*
|
||||
* @return <p>The kind of block players must hit</p>
|
||||
*/
|
||||
public @NotNull Material getWinBlockType() {
|
||||
return this.winBlockType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The location a player has to reach to win this arena
|
||||
*
|
||||
* <p></p>
|
||||
*
|
||||
* @return <p>The win trigger's location</p>
|
||||
*/
|
||||
public @Nullable Location getWinLocation() {
|
||||
return this.winLocation != null ? this.winLocation.clone() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the block types used for this parkour arena's kill plane
|
||||
*
|
||||
* @return <p>The types of blocks that cause a loss</p>
|
||||
*/
|
||||
public @NotNull Set<Material> getKillPlaneBlocks() {
|
||||
if (this.killPlaneBlocks != null) {
|
||||
return new HashSet<>(this.killPlaneBlocks);
|
||||
} else {
|
||||
return MiniGames.getInstance().getParkourConfiguration().getKillPlaneBlocks();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the names of the block types used for this parkour arena's kill plane
|
||||
*
|
||||
* @return <p>The names of the types of blocks that cause a loss</p>
|
||||
*/
|
||||
public @Nullable Set<String> getKillPlaneBlockNames() {
|
||||
return this.killPlaneBlockNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all checkpoint locations for this arena
|
||||
*
|
||||
* @return <p>All checkpoint locations for this arena</p>
|
||||
*/
|
||||
public List<Location> getCheckpoints() {
|
||||
List<Location> copy = new ArrayList<>(this.checkpoints.size());
|
||||
for (Location location : this.checkpoints) {
|
||||
copy.add(location.clone());
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this arena's sanitized name
|
||||
*
|
||||
* @return <p>This arena's sanitized name</p>
|
||||
*/
|
||||
public @NotNull String getArenaNameSanitized() {
|
||||
return StringSanitizer.sanitizeArenaName(this.getArenaName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeData() {
|
||||
return ParkourArenaStorageHelper.removeParkourArenaData(getArenaId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean saveData() {
|
||||
try {
|
||||
ParkourArenaStorageHelper.saveParkourArenaData(getData());
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean willCauseWin(Block block) {
|
||||
return (this.winLocation != null && this.winLocation.getBlock().equals(block)) ||
|
||||
(this.winLocation == null && this.winBlockType == block.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean willCauseLoss(Block block) {
|
||||
return this.getKillPlaneBlocks().contains(block.getType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean winLocationIsSolid() {
|
||||
return (this.winLocation != null && this.winLocation.getBlock().getType().isSolid()) ||
|
||||
this.winBlockType.isSolid();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
this.parkourArenaHandler.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;
|
||||
this.parkourArenaHandler.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
|
||||
this.parkourArenaHandler.updateLookupName(oldName, this.getArenaNameSanitized());
|
||||
this.parkourArenaHandler.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;
|
||||
this.parkourArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the location players need to reach to win this arena
|
||||
*
|
||||
* @param newLocation <p>The location players have to reach</p>
|
||||
* @return <p>True if successfully changed</p>
|
||||
*/
|
||||
public boolean setWinLocation(@NotNull Location newLocation) {
|
||||
if (isInvalid(newLocation)) {
|
||||
return false;
|
||||
} else {
|
||||
this.winLocation = newLocation.clone();
|
||||
this.parkourArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the type of blocks constituting this arena's kill plane
|
||||
*
|
||||
* @param killPlaneBlockNames <p>The names of the blocks that will cause players to lose</p>
|
||||
*/
|
||||
public boolean setKillPlaneBlocks(@NotNull Set<String> killPlaneBlockNames) {
|
||||
if (killPlaneBlockNames.isEmpty()) {
|
||||
this.killPlaneBlocks = null;
|
||||
} else {
|
||||
Set<Material> parsed = MaterialHelper.loadMaterialList(new ArrayList<>(killPlaneBlockNames));
|
||||
if (parsed.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
this.killPlaneBlocks = parsed;
|
||||
}
|
||||
this.parkourArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a checkpoint to this arena
|
||||
*
|
||||
* @param checkpoint <p>The checkpoint to add</p>
|
||||
* @return <p>True if successfully added</p>
|
||||
*/
|
||||
public boolean addCheckpoint(@NotNull Location checkpoint) {
|
||||
if (isInvalid(checkpoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.checkpoints.add(checkpoint.clone());
|
||||
this.parkourArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all checkpoints from this arena
|
||||
*
|
||||
* @return <p>True if successfully cleared</p>
|
||||
*/
|
||||
public boolean clearCheckpoints() {
|
||||
if (checkpoints.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
this.checkpoints.clear();
|
||||
this.parkourArenaHandler.saveArenas();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ParkourArena otherArena)) {
|
||||
return false;
|
||||
}
|
||||
return this.getArenaNameSanitized().equals(otherArena.getArenaNameSanitized());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,72 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaData;
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.container.SerializableContainer;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import net.knarcraft.minigames.util.SerializableConverter;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Data stored for an arena
|
||||
*/
|
||||
public class ParkourArenaData extends ArenaData {
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour arena data object
|
||||
*
|
||||
* @param arenaId <p>The id of the arena this data belongs to</p>
|
||||
* @param recordRegistries <p>The registry of this arena's records</p>
|
||||
* @param playersCompleted <p>The set of the players that have cleared this arena</p>
|
||||
*/
|
||||
public ParkourArenaData(@NotNull UUID arenaId,
|
||||
@NotNull Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries,
|
||||
@NotNull Map<ArenaGameMode, Set<UUID>> playersCompleted) {
|
||||
super(arenaId, recordRegistries, playersCompleted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData() {
|
||||
MiniGames.getInstance().getParkourArenaHandler().saveData(this.arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a parkour arena data from the given data
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized parkour arena data</p>
|
||||
*/
|
||||
@SuppressWarnings({"unused", "unchecked"})
|
||||
public static @NotNull ParkourArenaData deserialize(@NotNull Map<String, Object> data) {
|
||||
SerializableUUID serializableUUID = (SerializableUUID) data.get("arenaId");
|
||||
Map<ArenaGameMode, ArenaRecordsRegistry> recordsRegistry =
|
||||
(Map<ArenaGameMode, ArenaRecordsRegistry>) data.get("recordsRegistry");
|
||||
Map<ArenaGameMode, Set<SerializableContainer<UUID>>> playersCompletedData =
|
||||
(Map<ArenaGameMode, Set<SerializableContainer<UUID>>>) data.get("playersCompleted");
|
||||
|
||||
if (recordsRegistry == null) {
|
||||
recordsRegistry = new HashMap<>();
|
||||
} else if (playersCompletedData == null) {
|
||||
playersCompletedData = new HashMap<>();
|
||||
}
|
||||
|
||||
// Convert the serializable UUIDs to normal UUIDs
|
||||
Map<ArenaGameMode, Set<UUID>> allPlayersCompleted = new HashMap<>();
|
||||
SerializableConverter.getRawValue(playersCompletedData, allPlayersCompleted);
|
||||
|
||||
for (ArenaGameMode arenaGameMode : playersCompletedData.keySet()) {
|
||||
if (!recordsRegistry.containsKey(arenaGameMode) || recordsRegistry.get(arenaGameMode) == null) {
|
||||
recordsRegistry.put(arenaGameMode, new ParkourArenaRecordsRegistry(serializableUUID.getRawValue()));
|
||||
}
|
||||
}
|
||||
return new ParkourArenaData(serializableUUID.getRawValue(), recordsRegistry, allPlayersCompleted);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.arena.EditablePropertyType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* All editable properties of a parkour arena
|
||||
*/
|
||||
public enum ParkourArenaEditableProperty {
|
||||
|
||||
/**
|
||||
* The name of the arena
|
||||
*/
|
||||
NAME("name", ParkourArena::getArenaName, EditablePropertyType.ARENA_NAME),
|
||||
|
||||
/**
|
||||
* The arena's spawn location
|
||||
*/
|
||||
SPAWN_LOCATION("spawnLocation", (arena) -> String.valueOf(arena.getSpawnLocation()),
|
||||
EditablePropertyType.LOCATION),
|
||||
|
||||
/**
|
||||
* The arena's exit location
|
||||
*/
|
||||
EXIT_LOCATION("exitLocation", (arena) -> String.valueOf(arena.getExitLocation()),
|
||||
EditablePropertyType.LOCATION),
|
||||
|
||||
/**
|
||||
* The arena's win block type
|
||||
*/
|
||||
WIN_BLOCK_TYPE("winBlockType", (arena) -> arena.getWinBlockType().toString(),
|
||||
EditablePropertyType.BLOCK_TYPE),
|
||||
|
||||
/**
|
||||
* The arena's win location (overrides the win block type)
|
||||
*/
|
||||
WIN_LOCATION("winLocation", (arena) -> {
|
||||
if (arena.getWinLocation() != null) {
|
||||
return arena.getWinLocation().toString();
|
||||
} else {
|
||||
return "null";
|
||||
}
|
||||
}, EditablePropertyType.LOCATION),
|
||||
|
||||
/**
|
||||
* The arena's check points. Specifically used for adding.
|
||||
*/
|
||||
CHECKPOINT_ADD("checkpointAdd", (arena) -> String.valueOf(arena.getCheckpoints()),
|
||||
EditablePropertyType.LOCATION),
|
||||
|
||||
/**
|
||||
* The arena's check points. Specifically used for clearing.
|
||||
*/
|
||||
CHECKPOINT_CLEAR("checkpointClear", (arena) -> String.valueOf(arena.getCheckpoints()),
|
||||
EditablePropertyType.CHECKPOINT_CLEAR),
|
||||
|
||||
/**
|
||||
* The blocks constituting the arena's lethal blocks
|
||||
*/
|
||||
KILL_PLANE_BLOCKS("killPlaneBlocks", (arena) -> String.valueOf(arena.getKillPlaneBlockNames()),
|
||||
EditablePropertyType.MATERIAL_LIST),
|
||||
;
|
||||
|
||||
private final @NotNull String argumentString;
|
||||
private final Function<ParkourArena, String> currentValueProvider;
|
||||
private final EditablePropertyType propertyType;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena editable property
|
||||
*
|
||||
* @param argumentString <p>The argument string used to specify this property</p>
|
||||
*/
|
||||
ParkourArenaEditableProperty(@NotNull String argumentString, Function<ParkourArena, String> currentValueProvider,
|
||||
EditablePropertyType propertyType) {
|
||||
this.argumentString = argumentString;
|
||||
this.currentValueProvider = currentValueProvider;
|
||||
this.propertyType = propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of property this editable property represents
|
||||
*
|
||||
* @return <p>The type of this property</p>
|
||||
*/
|
||||
public EditablePropertyType getPropertyType() {
|
||||
return this.propertyType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(ParkourArena 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 ParkourArenaEditableProperty getFromArgumentString(String argumentString) {
|
||||
for (ParkourArenaEditableProperty property : ParkourArenaEditableProperty.values()) {
|
||||
if (property.argumentString.equalsIgnoreCase(argumentString)) {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
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 ParkourArenaGameMode implements ConfigurationSerializable, ArenaGameMode {
|
||||
|
||||
/**
|
||||
* The default game-mode. Failing once throws the player out.
|
||||
*/
|
||||
DEFAULT,
|
||||
;
|
||||
|
||||
/**
|
||||
* Tries to match the correct game-mode according to the given string
|
||||
*
|
||||
* @param gameMode <p>The game-mode string to match</p>
|
||||
* @return <p>The specified arena game-mode</p>
|
||||
*/
|
||||
public static @NotNull ParkourArenaGameMode matchGamemode(@NotNull String gameMode) {
|
||||
try {
|
||||
return ParkourArenaGameMode.valueOf(gameMode.toUpperCase());
|
||||
} catch (IllegalArgumentException exception) {
|
||||
return ParkourArenaGameMode.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 ParkourArenaGameMode deserialize(Map<String, Object> data) {
|
||||
return ParkourArenaGameMode.valueOf((String) data.get("name"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ParkourArenaGameMode[] getValues() {
|
||||
return ParkourArenaGameMode.values();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaGroup;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A sorted group of arenas that must be completed in sequence
|
||||
*/
|
||||
public class ParkourArenaGroup extends ArenaGroup<ParkourArena, ParkourArenaGroup> {
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour arena group
|
||||
*
|
||||
* @param groupName <p>The name of this group</p>
|
||||
*/
|
||||
public ParkourArenaGroup(@NotNull String groupName) {
|
||||
super(groupName, MiniGames.getInstance().getParkourArenaHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour 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 ParkourArenaGroup(@NotNull UUID groupId, @NotNull String groupName, @NotNull List<UUID> arenas) {
|
||||
super(groupId, groupName, arenas, MiniGames.getInstance().getParkourArenaHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ParkourArenaGroup deserialize(@NotNull Map<String, Object> data) {
|
||||
UUID id = ((SerializableUUID) data.get("groupId")).getRawValue();
|
||||
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.getRawValue());
|
||||
}
|
||||
return new ParkourArenaGroup(id, name, arenas);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaHandler;
|
||||
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.ParkourArenaStorageHelper;
|
||||
|
||||
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;
|
||||
|
||||
/**
|
||||
* A handler that keeps track of all parkour arenas
|
||||
*/
|
||||
public class ParkourArenaHandler extends ArenaHandler<ParkourArena, ParkourArenaGroup> {
|
||||
|
||||
/**
|
||||
* Instantiates a new arena handler
|
||||
*
|
||||
* @param playerRegistry <p>The registry keeping track of player sessions</p>
|
||||
*/
|
||||
public ParkourArenaHandler(ArenaPlayerRegistry<ParkourArena> playerRegistry) {
|
||||
super(playerRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveGroups() {
|
||||
try {
|
||||
ParkourArenaStorageHelper.saveParkourArenaGroups(new HashSet<>(this.arenaGroups.values()));
|
||||
} catch (IOException e) {
|
||||
MiniGames.log(Level.SEVERE, Message.ERROR_CANNOT_SAVE_ARENA_GROUPS.getMessage());
|
||||
MiniGames.log(Level.SEVERE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadGroups() {
|
||||
Set<ParkourArenaGroup> arenaGroups = ParkourArenaStorageHelper.loadParkourArenaGroups();
|
||||
Map<UUID, ParkourArenaGroup> arenaGroupMap = new HashMap<>();
|
||||
for (ParkourArenaGroup arenaGroup : arenaGroups) {
|
||||
for (UUID arenaId : arenaGroup.getArenas()) {
|
||||
arenaGroupMap.put(arenaId, arenaGroup);
|
||||
}
|
||||
}
|
||||
this.arenaGroups = arenaGroupMap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveArenas() {
|
||||
try {
|
||||
ParkourArenaStorageHelper.saveParkourArenas(this.arenas);
|
||||
} catch (IOException e) {
|
||||
MiniGames.log(Level.SEVERE, "Unable to save current arenas! " +
|
||||
"Data loss can occur!");
|
||||
MiniGames.log(Level.SEVERE, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void loadArenas() {
|
||||
this.arenas = ParkourArenaStorageHelper.loadParkourArenas();
|
||||
|
||||
// Save a map from arena name to arena id for improved performance
|
||||
this.arenaNameLookup = new HashMap<>();
|
||||
for (Map.Entry<UUID, ParkourArena> arena : this.arenas.entrySet()) {
|
||||
String sanitizedName = arena.getValue().getArenaNameSanitized();
|
||||
this.arenaNameLookup.put(sanitizedName, arena.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.arena.AbstractArenaPlayerRegistry;
|
||||
|
||||
/**
|
||||
* A registry to keep track of which players are playing in which arenas
|
||||
*/
|
||||
public class ParkourArenaPlayerRegistry extends AbstractArenaPlayerRegistry<ParkourArena> {
|
||||
|
||||
@Override
|
||||
protected String getEntryStateStorageKey() {
|
||||
return "parkour";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
|
||||
import net.knarcraft.minigames.arena.record.IntegerRecord;
|
||||
import net.knarcraft.minigames.arena.record.LongRecord;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A registry keeping track of all records
|
||||
*/
|
||||
public class ParkourArenaRecordsRegistry extends ArenaRecordsRegistry {
|
||||
|
||||
/**
|
||||
* Instantiates a new empty records registry
|
||||
*/
|
||||
public ParkourArenaRecordsRegistry(@NotNull UUID arenaId) {
|
||||
super(arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new records registry
|
||||
*
|
||||
* @param leastDeaths <p>The existing least death records to use</p>
|
||||
* @param shortestTimeMilliSeconds <p>The existing leash time records to use</p>
|
||||
*/
|
||||
private ParkourArenaRecordsRegistry(@NotNull UUID arenaId, @NotNull Set<IntegerRecord> leastDeaths,
|
||||
@NotNull Set<LongRecord> shortestTimeMilliSeconds) {
|
||||
super(arenaId, leastDeaths, shortestTimeMilliSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves changed records
|
||||
*/
|
||||
@Override
|
||||
protected void save() {
|
||||
MiniGames.getInstance().getParkourArenaHandler().saveData(this.arenaId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the given data
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized records registry</p>
|
||||
*/
|
||||
@SuppressWarnings({"unused", "unchecked"})
|
||||
public static ParkourArenaRecordsRegistry deserialize(Map<String, Object> data) {
|
||||
UUID arenaId = ((SerializableUUID) data.get("arenaId")).getRawValue();
|
||||
Set<IntegerRecord> leastDeaths =
|
||||
(Set<IntegerRecord>) data.getOrDefault("leastDeaths", new HashMap<>());
|
||||
Set<LongRecord> shortestTimeMilliseconds =
|
||||
(Set<LongRecord>) data.getOrDefault("shortestTime", new HashMap<>());
|
||||
|
||||
leastDeaths.removeIf(Objects::isNull);
|
||||
shortestTimeMilliseconds.removeIf(Objects::isNull);
|
||||
return new ParkourArenaRecordsRegistry(arenaId, leastDeaths, shortestTimeMilliseconds);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.AbstractArenaSession;
|
||||
import net.knarcraft.minigames.arena.PlayerEntryState;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.config.ParkourConfiguration;
|
||||
import net.knarcraft.minigames.util.PlayerTeleporter;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* A representation of a player's current session in a parkour arena
|
||||
*/
|
||||
public class ParkourArenaSession extends AbstractArenaSession {
|
||||
|
||||
private final @NotNull ParkourArena arena;
|
||||
private final @NotNull Player player;
|
||||
private final @NotNull ParkourArenaGameMode gameMode;
|
||||
private Location reachedCheckpoint = null;
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour arena session
|
||||
*
|
||||
* @param parkourArena <p>The arena that's being played in</p>
|
||||
* @param player <p>The player playing the arena</p>
|
||||
* @param gameMode <p>The game-mode</p>
|
||||
*/
|
||||
public ParkourArenaSession(@NotNull ParkourArena parkourArena, @NotNull Player player,
|
||||
@NotNull ParkourArenaGameMode gameMode) {
|
||||
super(parkourArena, player, gameMode);
|
||||
this.arena = parkourArena;
|
||||
this.player = player;
|
||||
this.gameMode = gameMode;
|
||||
|
||||
ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
|
||||
boolean makeInvisible = configuration.makePlayersInvisible();
|
||||
this.entryState = new ParkourPlayerEntryState(player, makeInvisible);
|
||||
this.entryState.setArenaState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the checkpoint this session's player has reached
|
||||
*
|
||||
* @param location <p>The location of the checkpoint</p>
|
||||
*/
|
||||
public void registerCheckpoint(@NotNull Location location) {
|
||||
this.reachedCheckpoint = location;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the checkpoint currently registered as the player's spawn location
|
||||
*
|
||||
* @return <p>The registered checkpoint, or null if not set</p>
|
||||
*/
|
||||
public @Nullable Location getRegisteredCheckpoint() {
|
||||
return this.reachedCheckpoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull PlayerEntryState getEntryState() {
|
||||
return this.entryState;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerWin() {
|
||||
// Stop this session
|
||||
removeSession();
|
||||
|
||||
// Check for, and display, records
|
||||
MiniGames miniGames = MiniGames.getInstance();
|
||||
boolean ignore = miniGames.getParkourConfiguration().ignoreRecordsUntilGroupBeatenOnce();
|
||||
ParkourArenaGroup group = miniGames.getParkourArenaHandler().getGroup(this.arena.getArenaId());
|
||||
if (!ignore || group == null || group.hasBeatenAll(this.gameMode, this.player)) {
|
||||
registerRecord();
|
||||
}
|
||||
|
||||
// Mark the arena as cleared
|
||||
if (this.arena.getData().setCompleted(this.gameMode, this.player)) {
|
||||
this.player.sendMessage(Message.SUCCESS_ARENA_FIRST_CLEAR.getMessage());
|
||||
}
|
||||
this.player.sendMessage(Message.SUCCESS_ARENA_WIN.getMessage());
|
||||
|
||||
// Teleport the player out of the arena
|
||||
teleportToExit(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void triggerLoss() {
|
||||
this.deaths++;
|
||||
//Teleport the player back to the top
|
||||
Location spawnLocation = this.reachedCheckpoint != null ? this.reachedCheckpoint : this.arena.getSpawnLocation();
|
||||
PlayerTeleporter.teleportPlayer(this.player, spawnLocation, true, false);
|
||||
this.entryState.setArenaState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ParkourArena getArena() {
|
||||
return this.arena;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void removeSession() {
|
||||
// Remove this session for game sessions to stop listeners from fiddling more with the player
|
||||
boolean removedSession = MiniGames.getInstance().getParkourArenaPlayerRegistry().removePlayer(
|
||||
player.getUniqueId(), true);
|
||||
if (!removedSession) {
|
||||
MiniGames.log(Level.SEVERE, "Unable to remove parkour arena session for " + player.getName() + ". " +
|
||||
"This will have unintended consequences.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getGameModeString() {
|
||||
return switch (this.gameMode) {
|
||||
case DEFAULT -> "default";
|
||||
};
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A representation of each key used for storing arena data
|
||||
*/
|
||||
public enum ParkourArenaStorageKey {
|
||||
|
||||
/**
|
||||
* The key for an arena's id
|
||||
*/
|
||||
ID("arenaId"),
|
||||
|
||||
/**
|
||||
* The key for an arena's name
|
||||
*/
|
||||
NAME("arenaName"),
|
||||
|
||||
/**
|
||||
* The key for an arena's spawn location
|
||||
*/
|
||||
SPAWN_LOCATION("arenaSpawnLocation"),
|
||||
|
||||
/**
|
||||
* The key for an arena's exit location
|
||||
*/
|
||||
EXIT_LOCATION("arenaExitLocation"),
|
||||
|
||||
/**
|
||||
* The key for the type of this arena's win block
|
||||
*/
|
||||
WIN_BLOCK_TYPE("winBlockType"),
|
||||
|
||||
/**
|
||||
* The key for this arena's win location (overrides win block type)
|
||||
*/
|
||||
WIN_LOCATION("winLocation"),
|
||||
|
||||
/**
|
||||
* The key for this arena's kill plane blocks (overrides the config)
|
||||
*/
|
||||
KILL_PLANE_BLOCKS("killPlaneBlocks"),
|
||||
|
||||
/**
|
||||
* The key for this arena's checkpoint locations
|
||||
*/
|
||||
CHECKPOINTS("checkpoints"),
|
||||
|
||||
/**
|
||||
* The hey for this arena's data
|
||||
*/
|
||||
DATA("arenaData"),
|
||||
;
|
||||
|
||||
private final @NotNull String key;
|
||||
|
||||
/**
|
||||
* Instantiates a new arena storage key
|
||||
*
|
||||
* @param key <p>The string path of the configuration key this value represents.</p>
|
||||
*/
|
||||
ParkourArenaStorageKey(@NotNull String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the configuration key this enum represents
|
||||
*
|
||||
* @return <p>The string key representation.</p>
|
||||
*/
|
||||
public @NotNull String getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
package net.knarcraft.minigames.arena.parkour;
|
||||
|
||||
import net.knarcraft.minigames.arena.AbstractPlayerEntryState;
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.bukkit.GameMode;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* The state of a player before entering a parkour arena
|
||||
*/
|
||||
public class ParkourPlayerEntryState extends AbstractPlayerEntryState {
|
||||
|
||||
/**
|
||||
* Instantiates a new player state
|
||||
*
|
||||
* @param player <p>The player whose state should be stored</p>
|
||||
*/
|
||||
public ParkourPlayerEntryState(@NotNull Player player, boolean makePlayerInvisible) {
|
||||
super(player, makePlayerInvisible);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new parkour player entry state
|
||||
*
|
||||
* @param playerId <p>The id of the player whose state this should keep track of</p>
|
||||
* @param makePlayerInvisible <p>Whether players should be made invisible while in the arena</p>
|
||||
* @param entryLocation <p>The location the player entered from</p>
|
||||
* @param originalIsFlying <p>Whether the player was flying before entering the arena</p>
|
||||
* @param originalGameMode <p>The game-mode of the player before entering the arena</p>
|
||||
* @param originalAllowFlight <p>Whether the player was allowed flight before entering the arena</p>
|
||||
* @param originalInvulnerable <p>Whether the player was invulnerable before entering the arena</p>
|
||||
* @param originalIsSwimming <p>Whether the player was swimming before entering the arena</p>
|
||||
* @param originalCollideAble <p>Whether the player was collide-able before entering the arena</p>
|
||||
*/
|
||||
public ParkourPlayerEntryState(@NotNull UUID playerId, boolean makePlayerInvisible, Location entryLocation,
|
||||
boolean originalIsFlying, GameMode originalGameMode, boolean originalAllowFlight,
|
||||
boolean originalInvulnerable, boolean originalIsSwimming,
|
||||
boolean originalCollideAble) {
|
||||
super(playerId, makePlayerInvisible, entryLocation, originalIsFlying, originalGameMode, originalAllowFlight,
|
||||
originalInvulnerable, originalIsSwimming, originalCollideAble);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setArenaState() {
|
||||
super.setArenaState();
|
||||
Player player = getPlayer();
|
||||
if (player == null) {
|
||||
return;
|
||||
}
|
||||
player.setAllowFlight(false);
|
||||
player.setFlying(false);
|
||||
player.setGameMode(GameMode.ADVENTURE);
|
||||
player.setSwimming(false);
|
||||
player.setCollidable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a ParkourPlayerEntryState from the given data
|
||||
*
|
||||
* @return <p>The data to deserialize</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static ParkourPlayerEntryState deserialize(Map<String, Object> data) {
|
||||
UUID playerId = ((SerializableUUID) data.get("playerId")).getRawValue();
|
||||
boolean makePlayerInvisible = (boolean) data.get("makePlayerInvisible");
|
||||
Location entryLocation = (Location) data.get("entryLocation");
|
||||
boolean originalIsFlying = (boolean) data.get("originalIsFlying");
|
||||
GameMode originalGameMode = GameMode.valueOf((String) data.get("originalGameMode"));
|
||||
boolean originalAllowFlight = (boolean) data.get("originalAllowFlight");
|
||||
boolean originalInvulnerable = (boolean) data.get("originalInvulnerable");
|
||||
boolean originalIsSwimming = (boolean) data.get("originalIsSwimming");
|
||||
boolean originalCollideAble = (boolean) data.get("originalCollideAble");
|
||||
|
||||
return new ParkourPlayerEntryState(playerId, makePlayerInvisible, entryLocation, originalIsFlying,
|
||||
originalGameMode, originalAllowFlight, originalInvulnerable, originalIsSwimming, originalCollideAble);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,76 @@
|
||||
package net.knarcraft.minigames.arena.record;
|
||||
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A record stored for an arena
|
||||
*/
|
||||
public abstract class ArenaRecord<K extends Comparable<K>> implements Comparable<ArenaRecord<K>>, ConfigurationSerializable {
|
||||
|
||||
private final UUID userId;
|
||||
private final K record;
|
||||
|
||||
/**
|
||||
* @param userId <p>The id of the player that achieved the record</p>
|
||||
* @param record <p>The record achieved</p>
|
||||
*/
|
||||
public ArenaRecord(UUID userId, K record) {
|
||||
this.userId = userId;
|
||||
this.record = record;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id of the user this record belongs to
|
||||
*
|
||||
* @return <p>The record's achiever</p>
|
||||
*/
|
||||
public UUID getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of the stored record
|
||||
*
|
||||
* @return <p>The record value</p>
|
||||
*/
|
||||
public K getRecord() {
|
||||
return record;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof ArenaRecord<?> && userId.equals(((ArenaRecord<?>) other).userId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull ArenaRecord<K> other) {
|
||||
return record.compareTo(other.record);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("userId", new SerializableUUID(getUserId()));
|
||||
data.put("record", record);
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(userId, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return userId + ": " + record;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package net.knarcraft.minigames.arena.record;
|
||||
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A record storing an integer
|
||||
*/
|
||||
public class IntegerRecord extends SummableArenaRecord<Integer> {
|
||||
|
||||
/**
|
||||
* @param userId <p>The id of the player that achieved the record</p>
|
||||
* @param record <p>The record achieved</p>
|
||||
*/
|
||||
public IntegerRecord(UUID userId, Integer record) {
|
||||
super(userId, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SummableArenaRecord<Integer> sum(Integer value) {
|
||||
return new IntegerRecord(this.getUserId(), this.getRecord() + value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof IntegerRecord && this.getUserId().equals(((IntegerRecord) other).getUserId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the saved arena record
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized data</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static IntegerRecord deserialize(@NotNull Map<String, Object> data) {
|
||||
return new IntegerRecord(((SerializableUUID) data.get("userId")).getRawValue(), (Integer) data.get("record"));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
package net.knarcraft.minigames.arena.record;
|
||||
|
||||
import net.knarcraft.minigames.container.SerializableUUID;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A record storing a Long
|
||||
*/
|
||||
public class LongRecord extends SummableArenaRecord<Long> {
|
||||
|
||||
/**
|
||||
* @param userId <p>The id of the player that achieved the record</p>
|
||||
* @param record <p>The record achieved</p>
|
||||
*/
|
||||
public LongRecord(UUID userId, Long record) {
|
||||
super(userId, record);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
return other instanceof LongRecord && this.getUserId().equals(((LongRecord) other).getUserId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public SummableArenaRecord<Long> sum(Long value) {
|
||||
return new LongRecord(this.getUserId(), this.getRecord() + value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes the saved arena record
|
||||
*
|
||||
* @param data <p>The data to deserialize</p>
|
||||
* @return <p>The deserialized data</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static LongRecord deserialize(@NotNull Map<String, Object> data) {
|
||||
return new LongRecord(((SerializableUUID) data.get("userId")).getRawValue(), ((Number) data.get("record")).longValue());
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package net.knarcraft.minigames.arena.record;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A type of arena record which can be summed together
|
||||
*
|
||||
* @param <K> <p>The type of the stored value</p>
|
||||
*/
|
||||
public abstract class SummableArenaRecord<K extends Comparable<K>> extends ArenaRecord<K> {
|
||||
|
||||
/**
|
||||
* @param userId <p>The id of the player that achieved the record</p>
|
||||
* @param record <p>The record achieved</p>
|
||||
*/
|
||||
public SummableArenaRecord(UUID userId, K record) {
|
||||
super(userId, record);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summable record with the resulting sum
|
||||
*
|
||||
* @param value <p>The value to add to the existing value</p>
|
||||
* @return <p>A record with the sum of this record and the given value</p>
|
||||
*/
|
||||
public abstract SummableArenaRecord<K> sum(K value);
|
||||
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
package net.knarcraft.minigames.command;
|
||||
|
||||
import net.knarcraft.minigames.arena.ArenaGameMode;
|
||||
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;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* An abstract class for an arena joining tab-completer
|
||||
*/
|
||||
public abstract class JoinArenaTabCompleter implements TabCompleter {
|
||||
|
||||
private final ArenaGameMode gameMode;
|
||||
private final Supplier<List<String>> arenaNameSupplier;
|
||||
|
||||
/**
|
||||
* Implements a new join arena tab completer
|
||||
*
|
||||
* @param arenaNameSupplier <p>The supplier to ask for arena names</p>
|
||||
* @param gameMode <p>An instance of one of the available game-modes</p>
|
||||
*/
|
||||
public JoinArenaTabCompleter(Supplier<List<String>> arenaNameSupplier, ArenaGameMode gameMode) {
|
||||
this.arenaNameSupplier = arenaNameSupplier;
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
return filterMatchingContains(arenaNameSupplier.get(), arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
List<String> gameModes = new ArrayList<>();
|
||||
for (ArenaGameMode gameMode : gameMode.getValues()) {
|
||||
gameModes.add(gameMode.name().toLowerCase());
|
||||
}
|
||||
return filterMatchingContains(gameModes, arguments[1]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
package net.knarcraft.minigames.command;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaSession;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.command.TabExecutor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The command used to leave the current dropper arena
|
||||
*/
|
||||
public class LeaveArenaCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] strings) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId());
|
||||
if (existingSession == null) {
|
||||
commandSender.sendMessage(Message.ERROR_NOT_IN_ARENA.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
existingSession.triggerQuit(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
package net.knarcraft.minigames.command;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
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 reloading the plugin
|
||||
*/
|
||||
public class ReloadCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
MiniGames.getInstance().reload();
|
||||
commandSender.sendMessage(Message.SUCCESS_PLUGIN_RELOADED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command for creating a new dropper arena
|
||||
*/
|
||||
public class CreateDropperArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Abort if no name was specified
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
|
||||
|
||||
DropperArena existingArena = arenaHandler.getArena(arenaName);
|
||||
if (existingArena != null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NAME_COLLISION.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArena arena = new DropperArena(arenaName, player.getLocation(), arenaHandler);
|
||||
arenaHandler.addArena(arena);
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENA_CREATED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,96 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The command for listing groups and the stages within
|
||||
*/
|
||||
public class DropperGroupListCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
|
||||
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(Message.ERROR_GROUP_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a list of all stages (arenas in the group)
|
||||
StringBuilder builder = new StringBuilder(Message.SUCCESS_GROUP_STAGES.getMessage("{group}", groupName));
|
||||
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 : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
|
||||
groupNames.add(group.getGroupName());
|
||||
}
|
||||
return filterMatchingContains(groupNames, arguments[0]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The command for setting the group of an arena
|
||||
*/
|
||||
public class DropperGroupSetCommand 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 = MiniGames.getInstance().getDropperArenaHandler();
|
||||
|
||||
DropperArena specifiedArena = arenaHandler.getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
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(Message.SUCCESS_ARENA_GROUP_UPDATED.getMessage());
|
||||
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 filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
List<String> possibleValues = new ArrayList<>();
|
||||
possibleValues.add("none");
|
||||
possibleValues.add("GroupName");
|
||||
for (DropperArenaGroup group : MiniGames.getInstance().getDropperArenaHandler().getAllGroups()) {
|
||||
possibleValues.add(group.getGroupName());
|
||||
}
|
||||
return filterMatchingContains(possibleValues, arguments[1]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The command for swapping the order of two arenas in a group
|
||||
*/
|
||||
public class DropperGroupSwapCommand 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 = MiniGames.getInstance().getDropperArenaHandler();
|
||||
|
||||
DropperArena arena1 = arenaHandler.getArena(arguments[0]);
|
||||
if (arena1 == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_1_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArena arena2 = arenaHandler.getArena(arguments[1]);
|
||||
if (arena2 == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_2_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId());
|
||||
DropperArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId());
|
||||
if (arena1Group == null || !arena1Group.equals(arena2Group)) {
|
||||
commandSender.sendMessage(Message.ERROR_SWAP_DIFFERENT_GROUPS.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()),
|
||||
arena1Group.getArenas().indexOf(arena2.getArenaId()));
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENAS_SWAPPED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
DropperArenaHandler arenaHandler = MiniGames.getInstance().getDropperArenaHandler();
|
||||
if (arguments.length == 1) {
|
||||
List<String> arenaNames = new ArrayList<>();
|
||||
for (DropperArena dropperArena : arenaHandler.getArenasInAGroup()) {
|
||||
arenaNames.add(dropperArena.getArenaName());
|
||||
}
|
||||
return filterMatchingContains(arenaNames, arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
return filterMatchingContains(getArenaNamesInSameGroup(arguments[0]), arguments[1]);
|
||||
} 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 = MiniGames.getInstance().getDropperArenaHandler();
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,169 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
|
||||
import net.knarcraft.minigames.config.DropperConfiguration;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.container.PlaceholderContainer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command for editing an existing dropper arena
|
||||
*/
|
||||
public class EditDropperArenaCommand implements CommandExecutor {
|
||||
|
||||
private final DropperConfiguration configuration;
|
||||
|
||||
/**
|
||||
* Instantiates a new edit arena command
|
||||
*
|
||||
* @param configuration <p>The configuration to use</p>
|
||||
*/
|
||||
public EditDropperArenaCommand(DropperConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
DropperArenaEditableProperty editableProperty = DropperArenaEditableProperty.getFromArgumentString(arguments[1]);
|
||||
if (editableProperty == null) {
|
||||
commandSender.sendMessage(Message.ERROR_UNKNOWN_PROPERTY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 3) {
|
||||
// Print the current value of the property
|
||||
String value = editableProperty.getCurrentValueAsString(specifiedArena);
|
||||
commandSender.sendMessage(Message.SUCCESS_CURRENT_VALUE.getMessage(new PlaceholderContainer().add(
|
||||
"{property}", editableProperty.getArgumentString()).add("{value}", value)));
|
||||
return true;
|
||||
} else {
|
||||
boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player);
|
||||
if (successful) {
|
||||
player.sendMessage(Message.SUCCESS_PROPERTY_CHANGED.getMessage("{property}",
|
||||
editableProperty.getArgumentString()));
|
||||
} else {
|
||||
player.sendMessage(Message.ERROR_PROPERTY_INPUT_INVALID.getMessage());
|
||||
}
|
||||
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 DropperArenaEditableProperty 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 = configuration.getVerticalVelocity();
|
||||
}
|
||||
|
||||
// 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 = configuration.getHorizontalVelocity();
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The tab-completer for the edit arena command
|
||||
*/
|
||||
public class EditDropperArenaTabCompleter implements TabCompleter {
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
return filterMatchingContains(TabCompleteHelper.getDropperArenaProperties(), arguments[1]);
|
||||
} else if (arguments.length == 3) {
|
||||
DropperArenaEditableProperty property = DropperArenaEditableProperty.getFromArgumentString(arguments[1]);
|
||||
if (property == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()),
|
||||
arguments[2]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,136 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGroup;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
|
||||
import net.knarcraft.minigames.config.DropperConfiguration;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.PlayerTeleporter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command used to join a dropper arena
|
||||
*/
|
||||
public class JoinDropperArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow joining if the player is already in a mini-game arena
|
||||
if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) {
|
||||
commandSender.sendMessage(Message.ERROR_ALREADY_PLAYING.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the arena exists
|
||||
DropperArena specifiedArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deny vehicles as allowing this is tricky, and will cause problems in some cases
|
||||
if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) {
|
||||
commandSender.sendMessage(Message.ERROR_JOIN_IN_VEHICLE_OR_PASSENGER.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return joinArena(specifiedArena, player, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual arena joining
|
||||
*
|
||||
* @param specifiedArena <p>The arena the player wants to join</p>
|
||||
* @param player <p>The player joining the arena</p>
|
||||
* @param arguments <p>The arguments given</p>
|
||||
* @return <p>Whether the arena was joined successfully</p>
|
||||
*/
|
||||
private boolean joinArena(DropperArena specifiedArena, Player player, String[] arguments) {
|
||||
// Find the specified game-mode
|
||||
DropperArenaGameMode gameMode;
|
||||
if (arguments.length > 1) {
|
||||
gameMode = DropperArenaGameMode.matchGamemode(arguments[1]);
|
||||
} else {
|
||||
gameMode = DropperArenaGameMode.DEFAULT;
|
||||
}
|
||||
|
||||
// Make sure the player has beaten the necessary levels
|
||||
DropperArenaGroup arenaGroup = MiniGames.getInstance().getDropperArenaHandler().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 (MiniGames.getInstance().getDropperConfiguration().mustDoNormalModeFirst() &&
|
||||
gameMode != DropperArenaGameMode.DEFAULT &&
|
||||
specifiedArena.getData().hasNotCompleted(DropperArenaGameMode.DEFAULT, player)) {
|
||||
player.sendMessage(Message.ERROR_NORMAL_MODE_REQUIRED.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register the player's session
|
||||
DropperArenaSession newSession = new DropperArenaSession(specifiedArena, player, gameMode);
|
||||
ArenaPlayerRegistry<DropperArena> playerRegistry = MiniGames.getInstance().getDropperArenaPlayerRegistry();
|
||||
playerRegistry.registerPlayer(player.getUniqueId(), newSession);
|
||||
|
||||
// Try to teleport the player to the arena
|
||||
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
|
||||
if (!teleported) {
|
||||
player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage());
|
||||
newSession.triggerQuit(false);
|
||||
return false;
|
||||
} else {
|
||||
// Make sure to update the state again in the air to remove a potential swimming state
|
||||
newSession.getEntryState().setArenaState();
|
||||
player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 DropperArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
DropperConfiguration configuration = MiniGames.getInstance().getDropperConfiguration();
|
||||
// Require that players beat all arenas in the group in the normal game-mode before trying challenge modes
|
||||
if (configuration.mustDoNormalModeFirst() && arenaGameMode != DropperArenaGameMode.DEFAULT &&
|
||||
!arenaGroup.hasBeatenAll(DropperArenaGameMode.DEFAULT, player)) {
|
||||
player.sendMessage(Message.ERROR_GROUP_NORMAL_MODE_REQUIRED.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Require that the player has beaten the previous arena on the same game-mode before trying this one
|
||||
if (configuration.mustDoGroupedInSequence() &&
|
||||
arenaGroup.cannotPlay(arenaGameMode, player, dropperArena.getArenaId())) {
|
||||
player.sendMessage(Message.ERROR_PREVIOUS_ARENA_REQUIRED.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
|
||||
import net.knarcraft.minigames.command.JoinArenaTabCompleter;
|
||||
import net.knarcraft.minigames.util.TabCompleteHelper;
|
||||
|
||||
/**
|
||||
* The tab-completer for the join command
|
||||
*/
|
||||
public class JoinDropperArenaTabCompleter extends JoinArenaTabCompleter {
|
||||
|
||||
/**
|
||||
* Implements a new join arena tab completer
|
||||
*/
|
||||
public JoinDropperArenaTabCompleter() {
|
||||
super(TabCompleteHelper::getDropperArenas, DropperArenaGameMode.DEFAULT);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
/**
|
||||
* A command for listing existing dropper arenas
|
||||
*/
|
||||
public class ListDropperArenaCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
@NotNull String[] arguments) {
|
||||
StringBuilder builder = new StringBuilder(Message.SUCCESS_DROPPER_ARENAS_LIST.getMessage());
|
||||
for (String arenaName : TabCompleteHelper.getDropperArenas()) {
|
||||
builder.append("\n").append(arenaName);
|
||||
}
|
||||
sender.sendMessage(builder.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArena;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The method used for removing an existing arena
|
||||
*/
|
||||
public class RemoveDropperArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
// Abort if no name was specified
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the specified arena
|
||||
DropperArena targetArena = MiniGames.getInstance().getDropperArenaHandler().getArena(arguments[0]);
|
||||
if (targetArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the arena
|
||||
MiniGames.getInstance().getDropperArenaHandler().removeArena(targetArena);
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENA_REMOVED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package net.knarcraft.minigames.command.dropper;
|
||||
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The tab-completer for the remove arena command
|
||||
*/
|
||||
public class RemoveDropperArenaTabCompleter implements TabCompleter {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
return filterMatchingContains(TabCompleteHelper.getDropperArenas(), arguments[0]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command for creating a new parkour arena
|
||||
*/
|
||||
public class CreateParkourArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Abort if no name was specified
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
|
||||
|
||||
ParkourArena existingArena = arenaHandler.getArena(arenaName);
|
||||
if (existingArena != null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NAME_COLLISION.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArena arena = new ParkourArena(arenaName, player.getLocation(), arenaHandler);
|
||||
arenaHandler.addArena(arena);
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENA_CREATED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.container.PlaceholderContainer;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The command for editing an existing dropper arena
|
||||
*/
|
||||
public class EditParkourArenaCommand implements CommandExecutor {
|
||||
|
||||
/**
|
||||
* Instantiates a new edit arena command
|
||||
*/
|
||||
public EditParkourArenaCommand() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArenaEditableProperty editableProperty = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]);
|
||||
if (editableProperty == null) {
|
||||
commandSender.sendMessage(Message.ERROR_UNKNOWN_PROPERTY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 3) {
|
||||
// Print the current value of the property
|
||||
String value = editableProperty.getCurrentValueAsString(specifiedArena);
|
||||
commandSender.sendMessage(Message.SUCCESS_CURRENT_VALUE.getMessage(new PlaceholderContainer().add(
|
||||
"{property}", editableProperty.getArgumentString()).add("{value}", value)));
|
||||
return true;
|
||||
} else {
|
||||
boolean successful = changeValue(specifiedArena, editableProperty, arguments[2], player);
|
||||
if (successful) {
|
||||
player.sendMessage(Message.SUCCESS_PROPERTY_CHANGED.getMessage("{property}",
|
||||
editableProperty.getArgumentString()));
|
||||
} else {
|
||||
player.sendMessage(Message.ERROR_PROPERTY_INPUT_INVALID.getMessage());
|
||||
}
|
||||
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 ParkourArena arena, @NotNull ParkourArenaEditableProperty property,
|
||||
@NotNull String value, @NotNull Player player) {
|
||||
return switch (property) {
|
||||
case WIN_BLOCK_TYPE -> arena.setWinBlockType(parseMaterial(value));
|
||||
case SPAWN_LOCATION -> arena.setSpawnLocation(parseLocation(player, value));
|
||||
case NAME -> arena.setName(value);
|
||||
case EXIT_LOCATION -> arena.setExitLocation(parseLocation(player, value));
|
||||
case WIN_LOCATION -> arena.setWinLocation(parseLocation(player, value));
|
||||
case CHECKPOINT_ADD -> arena.addCheckpoint(parseLocation(player, value));
|
||||
case CHECKPOINT_CLEAR -> arena.clearCheckpoints();
|
||||
case KILL_PLANE_BLOCKS -> arena.setKillPlaneBlocks(new HashSet<>(List.of(value.split(","))));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The tab-completer for the edit arena command
|
||||
*/
|
||||
public class EditParkourArenaTabCompleter implements TabCompleter {
|
||||
|
||||
@Override
|
||||
public @Nullable List<String> onTabComplete(@NotNull CommandSender sender, @NotNull Command command,
|
||||
@NotNull String label, @NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
return filterMatchingContains(TabCompleteHelper.getParkourArenaProperties(), arguments[1]);
|
||||
} else if (arguments.length == 3) {
|
||||
ParkourArenaEditableProperty property = ParkourArenaEditableProperty.getFromArgumentString(arguments[1]);
|
||||
if (property == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()),
|
||||
arguments[2]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaPlayerRegistry;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.config.ParkourConfiguration;
|
||||
import net.knarcraft.minigames.util.PlayerTeleporter;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The command used to join a parkour arena
|
||||
*/
|
||||
public class JoinParkourArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (!(commandSender instanceof Player player)) {
|
||||
commandSender.sendMessage(Message.ERROR_PLAYER_ONLY.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Disallow joining if the player is already in a mini-game arena
|
||||
if (MiniGames.getInstance().getSession(player.getUniqueId()) != null) {
|
||||
commandSender.sendMessage(Message.ERROR_ALREADY_PLAYING.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the arena exists
|
||||
ParkourArena specifiedArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Deny vehicles as allowing this is tricky, and will cause problems in some cases
|
||||
if (player.isInsideVehicle() || !player.getPassengers().isEmpty()) {
|
||||
commandSender.sendMessage(Message.ERROR_JOIN_IN_VEHICLE_OR_PASSENGER.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return joinArena(specifiedArena, player, arguments);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual arena joining
|
||||
*
|
||||
* @param specifiedArena <p>The arena the player wants to join</p>
|
||||
* @param player <p>The player joining the arena</p>
|
||||
* @param arguments <p>The arguments given</p>
|
||||
* @return <p>Whether the arena was joined successfully</p>
|
||||
*/
|
||||
private boolean joinArena(ParkourArena specifiedArena, Player player, String[] arguments) {
|
||||
// Find the specified game-mode
|
||||
ParkourArenaGameMode gameMode;
|
||||
if (arguments.length > 1) {
|
||||
gameMode = ParkourArenaGameMode.matchGamemode(arguments[1]);
|
||||
} else {
|
||||
gameMode = ParkourArenaGameMode.DEFAULT;
|
||||
}
|
||||
|
||||
// Make sure the player has beaten the necessary levels
|
||||
ParkourArenaGroup arenaGroup = MiniGames.getInstance().getParkourArenaHandler().getGroup(specifiedArena.getArenaId());
|
||||
if (arenaGroup != null && !doGroupChecks(specifiedArena, arenaGroup, gameMode, player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register the player's session
|
||||
ParkourArenaSession newSession = new ParkourArenaSession(specifiedArena, player, gameMode);
|
||||
ArenaPlayerRegistry<ParkourArena> playerRegistry = MiniGames.getInstance().getParkourArenaPlayerRegistry();
|
||||
playerRegistry.registerPlayer(player.getUniqueId(), newSession);
|
||||
|
||||
// Try to teleport the player to the arena
|
||||
boolean teleported = PlayerTeleporter.teleportPlayer(player, specifiedArena.getSpawnLocation(), false, false);
|
||||
if (!teleported) {
|
||||
player.sendMessage(Message.ERROR_ARENA_TELEPORT_FAILED.getMessage());
|
||||
newSession.triggerQuit(false);
|
||||
return false;
|
||||
} else {
|
||||
// Make sure to update the state again in the air to remove a potential swimming state
|
||||
newSession.getEntryState().setArenaState();
|
||||
player.sendMessage(Message.SUCCESS_ARENA_JOINED.getMessage());
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs necessary check for the given arena's group
|
||||
*
|
||||
* @param parkourArena <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 ParkourArena parkourArena, @NotNull ParkourArenaGroup arenaGroup,
|
||||
@NotNull ParkourArenaGameMode arenaGameMode, @NotNull Player player) {
|
||||
ParkourConfiguration configuration = MiniGames.getInstance().getParkourConfiguration();
|
||||
|
||||
// Require that the player has beaten the previous arena on the same game-mode before trying this one
|
||||
if (configuration.mustDoGroupedInSequence() &&
|
||||
arenaGroup.cannotPlay(arenaGameMode, player, parkourArena.getArenaId())) {
|
||||
player.sendMessage(Message.ERROR_PREVIOUS_ARENA_REQUIRED.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGameMode;
|
||||
import net.knarcraft.minigames.command.JoinArenaTabCompleter;
|
||||
import net.knarcraft.minigames.util.TabCompleteHelper;
|
||||
|
||||
/**
|
||||
* The tab-completer for the join command
|
||||
*/
|
||||
public class JoinParkourArenaTabCompleter extends JoinArenaTabCompleter {
|
||||
|
||||
/**
|
||||
* Implements a new join arena tab completer
|
||||
*/
|
||||
public JoinParkourArenaTabCompleter() {
|
||||
super(TabCompleteHelper::getParkourArenas, ParkourArenaGameMode.DEFAULT);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
/**
|
||||
* A command for listing existing parkour arenas
|
||||
*/
|
||||
public class ListParkourArenaCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command, @NotNull String label,
|
||||
@NotNull String[] arguments) {
|
||||
StringBuilder builder = new StringBuilder(Message.SUCCESS_PARKOUR_ARENAS_LIST.getMessage());
|
||||
for (String arenaName : TabCompleteHelper.getParkourArenas()) {
|
||||
builder.append("\n").append(arenaName);
|
||||
}
|
||||
sender.sendMessage(builder.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
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.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The command for listing groups and the stages within
|
||||
*/
|
||||
public class ParkourGroupListCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
|
||||
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 parkour 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 ParkourArenaHandler arenaHandler, @NotNull CommandSender sender) {
|
||||
StringBuilder builder = new StringBuilder("Parkour 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 ParkourArenaHandler arenaHandler, @NotNull CommandSender sender,
|
||||
@NotNull String groupName) {
|
||||
ParkourArenaGroup arenaGroup = arenaHandler.getGroup(groupName);
|
||||
if (arenaGroup == null) {
|
||||
sender.sendMessage(Message.ERROR_GROUP_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send a list of all stages (arenas in the group)
|
||||
StringBuilder builder = new StringBuilder(Message.SUCCESS_GROUP_STAGES.getMessage("{group}", groupName));
|
||||
int counter = 1;
|
||||
for (UUID arenaId : arenaGroup.getArenas()) {
|
||||
ParkourArena arena = arenaHandler.getArena(arenaId);
|
||||
if (arena != null) {
|
||||
builder.append("\n").append(counter++).append(". ").append(arena.getArenaName());
|
||||
}
|
||||
}
|
||||
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<>();
|
||||
Set<ParkourArenaGroup> arenaGroups = MiniGames.getInstance().getParkourArenaHandler().getAllGroups();
|
||||
for (ParkourArenaGroup group : arenaGroups) {
|
||||
groupNames.add(group.getGroupName());
|
||||
}
|
||||
return filterMatchingContains(groupNames, arguments[0]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,82 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.util.StringSanitizer;
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The command for setting the group of an arena
|
||||
*/
|
||||
public class ParkourGroupSetCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
|
||||
|
||||
ParkourArena specifiedArena = arenaHandler.getArena(arguments[0]);
|
||||
if (specifiedArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
String groupName = StringSanitizer.removeUnwantedCharacters(arguments[1]);
|
||||
|
||||
if (groupName.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArenaGroup arenaGroup;
|
||||
if (groupName.equalsIgnoreCase("null") || groupName.equalsIgnoreCase("none")) {
|
||||
arenaGroup = null;
|
||||
} else {
|
||||
arenaGroup = arenaHandler.getGroup(groupName);
|
||||
if (arenaGroup == null) {
|
||||
arenaGroup = new ParkourArenaGroup(groupName);
|
||||
}
|
||||
}
|
||||
|
||||
arenaHandler.setGroup(specifiedArena.getArenaId(), arenaGroup);
|
||||
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENA_GROUP_UPDATED.getMessage());
|
||||
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 filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
List<String> possibleValues = new ArrayList<>();
|
||||
possibleValues.add("none");
|
||||
possibleValues.add("GroupName");
|
||||
for (ParkourArenaGroup group : MiniGames.getInstance().getParkourArenaHandler().getAllGroups()) {
|
||||
possibleValues.add(group.getGroupName());
|
||||
}
|
||||
return filterMatchingContains(possibleValues, arguments[1]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,105 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaGroup;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaHandler;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The command for swapping the order of two arenas in a group
|
||||
*/
|
||||
public class ParkourGroupSwapCommand implements TabExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length < 2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
|
||||
|
||||
ParkourArena arena1 = arenaHandler.getArena(arguments[0]);
|
||||
if (arena1 == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_1_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArena arena2 = arenaHandler.getArena(arguments[1]);
|
||||
if (arena2 == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_2_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
ParkourArenaGroup arena1Group = arenaHandler.getGroup(arena1.getArenaId());
|
||||
ParkourArenaGroup arena2Group = arenaHandler.getGroup(arena2.getArenaId());
|
||||
if (arena1Group == null || !arena1Group.equals(arena2Group)) {
|
||||
commandSender.sendMessage(Message.ERROR_SWAP_DIFFERENT_GROUPS.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
arena1Group.swapArenas(arena1Group.getArenas().indexOf(arena1.getArenaId()),
|
||||
arena1Group.getArenas().indexOf(arena2.getArenaId()));
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENAS_SWAPPED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
|
||||
if (arguments.length == 1) {
|
||||
List<String> arenaNames = new ArrayList<>();
|
||||
for (ParkourArena parkourArena : arenaHandler.getArenasInAGroup()) {
|
||||
arenaNames.add(parkourArena.getArenaName());
|
||||
}
|
||||
return filterMatchingContains(arenaNames, arguments[0]);
|
||||
} else if (arguments.length == 2) {
|
||||
return filterMatchingContains(getArenaNamesInSameGroup(arguments[0]), arguments[1]);
|
||||
} 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) {
|
||||
ParkourArenaHandler arenaHandler = MiniGames.getInstance().getParkourArenaHandler();
|
||||
ParkourArena arena1 = arenaHandler.getArena(arenaName);
|
||||
if (arena1 == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
// Only display other arenas in the selected group
|
||||
List<String> arenaNames = new ArrayList<>();
|
||||
ParkourArenaGroup group = arenaHandler.getGroup(arena1.getArenaId());
|
||||
if (group == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
for (UUID arenaId : group.getArenas()) {
|
||||
ParkourArena arena = arenaHandler.getArena(arenaId);
|
||||
if (arena != null && arena.getArenaId() != arena1.getArenaId()) {
|
||||
arenaNames.add(arena.getArenaName());
|
||||
}
|
||||
}
|
||||
return arenaNames;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandExecutor;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The method used for removing an existing arena
|
||||
*/
|
||||
public class RemoveParkourArenaCommand implements CommandExecutor {
|
||||
|
||||
@Override
|
||||
public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
// Abort if no name was specified
|
||||
if (arguments.length < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the specified arena
|
||||
ParkourArena targetArena = MiniGames.getInstance().getParkourArenaHandler().getArena(arguments[0]);
|
||||
if (targetArena == null) {
|
||||
commandSender.sendMessage(Message.ERROR_ARENA_NOT_FOUND.getMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Remove the arena
|
||||
MiniGames.getInstance().getParkourArenaHandler().removeArena(targetArena);
|
||||
commandSender.sendMessage(Message.SUCCESS_ARENA_REMOVED.getMessage());
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package net.knarcraft.minigames.command.parkour;
|
||||
|
||||
import net.knarcraft.minigames.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;
|
||||
|
||||
import static net.knarcraft.minigames.util.TabCompleteHelper.filterMatchingContains;
|
||||
|
||||
/**
|
||||
* The tab-completer for the remove arena command
|
||||
*/
|
||||
public class RemoveParkourArenaTabCompleter implements TabCompleter {
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public List<String> onTabComplete(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s,
|
||||
@NotNull String[] arguments) {
|
||||
if (arguments.length == 1) {
|
||||
return filterMatchingContains(TabCompleteHelper.getParkourArenas(), arguments[0]);
|
||||
} else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,188 @@
|
||||
package net.knarcraft.minigames.config;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The configuration keeping track of dropper settings
|
||||
*/
|
||||
public class DropperConfiguration extends MiniGameConfiguration {
|
||||
|
||||
private final static String rootNode = "dropper.";
|
||||
|
||||
private double verticalVelocity;
|
||||
private float horizontalVelocity;
|
||||
private int randomlyInvertedTimer;
|
||||
private boolean mustDoGroupedInSequence;
|
||||
private boolean ignoreRecordsUntilGroupBeatenOnce;
|
||||
private boolean mustDoNormalModeFirst;
|
||||
private boolean makePlayersInvisible;
|
||||
private boolean disableHitCollision;
|
||||
private boolean blockSneaking;
|
||||
private boolean blockSprinting;
|
||||
private Set<Material> blockWhitelist;
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper configuration
|
||||
*
|
||||
* @param configuration <p>The YAML configuration to use internally</p>
|
||||
*/
|
||||
public DropperConfiguration(FileConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default vertical velocity
|
||||
*
|
||||
* @return <p>The default vertical velocity</p>
|
||||
*/
|
||||
public double getVerticalVelocity() {
|
||||
return this.verticalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default horizontal velocity
|
||||
*
|
||||
* @return <p>The default horizontal velocity</p>
|
||||
*/
|
||||
public float getHorizontalVelocity() {
|
||||
return this.horizontalVelocity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of seconds before the randomly inverted game-mode toggles
|
||||
*
|
||||
* @return <p>Number of seconds before the inversion toggles</p>
|
||||
*/
|
||||
public int getRandomlyInvertedTimer() {
|
||||
return this.randomlyInvertedTimer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether grouped arenas must be done in the set sequence
|
||||
*
|
||||
* @return <p>Whether grouped arenas must be done in sequence</p>
|
||||
*/
|
||||
public boolean mustDoGroupedInSequence() {
|
||||
return this.mustDoGroupedInSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether the normal/default mode must be beaten before playing another game-mode
|
||||
*
|
||||
* @return <p>Whether the normal game-mode must be beaten first</p>
|
||||
*/
|
||||
public boolean mustDoNormalModeFirst() {
|
||||
return this.mustDoNormalModeFirst;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the types of block which should not trigger a loss
|
||||
*
|
||||
* @return <p>The materials that should not trigger a loss</p>
|
||||
*/
|
||||
public Set<Material> getBlockWhitelist() {
|
||||
return new HashSet<>(this.blockWhitelist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether records should be discarded, unless the player has already beaten all arenas in the group
|
||||
*
|
||||
* @return <p>Whether to ignore records on the first play-through</p>
|
||||
*/
|
||||
public boolean ignoreRecordsUntilGroupBeatenOnce() {
|
||||
return this.ignoreRecordsUntilGroupBeatenOnce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether players should be made invisible while in an arena
|
||||
*
|
||||
* @return <p>Whether players should be made invisible</p>
|
||||
*/
|
||||
public boolean makePlayersInvisible() {
|
||||
return this.makePlayersInvisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether entity hit-collision of players in an arena should be disabled
|
||||
*
|
||||
* @return <p>Whether to disable hit collision</p>
|
||||
*/
|
||||
public boolean disableHitCollision() {
|
||||
return this.disableHitCollision;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether players trying to sneak while in a dropper arena to increase their downwards speed should be blocked
|
||||
*
|
||||
* @return <p>Whether to block sneak to speed up</p>
|
||||
*/
|
||||
public boolean blockSneaking() {
|
||||
return blockSneaking;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether players trying to sprint to improve their horizontal speed while in a dropper arena should be blocked
|
||||
*
|
||||
* @return <p>Whether to block sprint to speed up</p>
|
||||
*/
|
||||
public boolean blockSprinting() {
|
||||
return this.blockSprinting;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() {
|
||||
this.verticalVelocity = configuration.getDouble(rootNode + "verticalVelocity", 1.0);
|
||||
this.horizontalVelocity = (float) configuration.getDouble(rootNode + "horizontalVelocity", 1.0);
|
||||
this.randomlyInvertedTimer = configuration.getInt(rootNode + "randomlyInvertedTimer", 7);
|
||||
this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
|
||||
this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
|
||||
this.mustDoNormalModeFirst = configuration.getBoolean(rootNode + "mustDoNormalModeFirst", true);
|
||||
this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
|
||||
this.disableHitCollision = configuration.getBoolean(rootNode + "disableHitCollision", true);
|
||||
this.blockSprinting = configuration.getBoolean(rootNode + "blockSprinting", true);
|
||||
this.blockSneaking = configuration.getBoolean(rootNode + "blockSneaking", true);
|
||||
this.blockWhitelist = loadMaterialList(rootNode + "blockWhitelist");
|
||||
sanitizeValues();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitizes configuration values to ensure they are within expected bounds
|
||||
*/
|
||||
private void sanitizeValues() {
|
||||
if (this.horizontalVelocity > 1 || this.horizontalVelocity <= 0) {
|
||||
this.horizontalVelocity = 1;
|
||||
}
|
||||
|
||||
if (this.verticalVelocity <= 0 || this.verticalVelocity > 75) {
|
||||
this.verticalVelocity = 1;
|
||||
}
|
||||
|
||||
if (this.randomlyInvertedTimer <= 0 || this.randomlyInvertedTimer > 3600) {
|
||||
this.randomlyInvertedTimer = 7;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(
|
||||
"Current configuration:" +
|
||||
"\n" + "Vertical velocity: " + verticalVelocity +
|
||||
"\n" + "Horizontal velocity: " + horizontalVelocity +
|
||||
"\n" + "Randomly inverted timer: " + randomlyInvertedTimer +
|
||||
"\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
|
||||
"\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
|
||||
"\n" + "Must do normal mode first: " + mustDoNormalModeFirst +
|
||||
"\n" + "Make players invisible: " + makePlayersInvisible +
|
||||
"\n" + "Disable hit collision: " + disableHitCollision +
|
||||
"\n" + "Block whitelist: ");
|
||||
for (Material material : blockWhitelist) {
|
||||
builder.append("\n - ").append(material.name());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
300
src/main/java/net/knarcraft/minigames/config/Message.java
Normal file
300
src/main/java/net/knarcraft/minigames/config/Message.java
Normal file
@ -0,0 +1,300 @@
|
||||
package net.knarcraft.minigames.config;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.container.PlaceholderContainer;
|
||||
import net.knarcraft.minigames.util.ColorHelper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A message which ca be displayed to the user
|
||||
*/
|
||||
public enum Message {
|
||||
|
||||
/* ************** *
|
||||
* Error messages *
|
||||
* ************** */
|
||||
|
||||
/**
|
||||
* The message displayed if saving arena groups fails
|
||||
*/
|
||||
ERROR_CANNOT_SAVE_ARENA_GROUPS("&cUnable to save current arena groups! Data loss can occur!"),
|
||||
|
||||
/**
|
||||
* The message displayed if an un-parse-able message is given by a user
|
||||
*/
|
||||
ERROR_MATERIAL_NOT_PARSE_ABLE("&cUnable to parse material: {material}"),
|
||||
|
||||
/**
|
||||
* The message displayed if a player tries to be teleported to/from an arena with a passenger
|
||||
*/
|
||||
ERROR_TELEPORT_WITH_PASSENGER("&cYou cannot be teleported with a passenger!"),
|
||||
|
||||
/**
|
||||
* The message displayed if a player tries to be teleported to/from an arena with a vehicle
|
||||
*/
|
||||
ERROR_TELEPORT_IN_VEHICLE("&cYou cannot be teleported while in a vehicle"),
|
||||
|
||||
/**
|
||||
* The message displayed if an arena cannot be loaded
|
||||
*/
|
||||
ERROR_ARENA_NOT_LOADED("&cCould not load the arena at configuration section {section}. Please check " +
|
||||
"the {file} storage file for issues."),
|
||||
|
||||
/**
|
||||
* The message displayed if an arena's data cannot be loaded
|
||||
*/
|
||||
ERROR_ARENA_DATA_NOT_LOADED("&cUnable to load arena data for dropper arena: {arena}"),
|
||||
|
||||
/**
|
||||
* The message displayed if the user specifies an unrecognized arena
|
||||
*/
|
||||
ERROR_ARENA_NOT_FOUND("&cUnable to find the specified arena."),
|
||||
|
||||
/**
|
||||
* The message displayed if the user specifies an unrecognized group
|
||||
*/
|
||||
ERROR_GROUP_NOT_FOUND("&cUnable to find the specified group!"),
|
||||
|
||||
/**
|
||||
* The message displayed if the console tries to execute a player-only command
|
||||
*/
|
||||
ERROR_PLAYER_ONLY("&cThis command must be used by a player"),
|
||||
|
||||
/**
|
||||
* The message displayed if the name of an arena is duplicated
|
||||
*/
|
||||
ERROR_ARENA_NAME_COLLISION("&cThere already exists an arena with that name!"),
|
||||
|
||||
/**
|
||||
* The message displayed if the player is required to win on the default difficulty first
|
||||
*/
|
||||
ERROR_NORMAL_MODE_REQUIRED("&cYou must complete this arena in normal mode first!"),
|
||||
|
||||
/**
|
||||
* The message displayed if the player is required to win on the default difficulty for all arenas in the group first
|
||||
*/
|
||||
ERROR_GROUP_NORMAL_MODE_REQUIRED("&cYou have not yet beaten the default game-mode for all arenas in this group!"),
|
||||
|
||||
/**
|
||||
* The message displayed if the player is required to beat the previous arena in the group
|
||||
*/
|
||||
ERROR_PREVIOUS_ARENA_REQUIRED("&cYou have not yet beaten the previous arena!"),
|
||||
|
||||
/**
|
||||
* The message displayed if player teleportation failed for some reason
|
||||
*/
|
||||
ERROR_ARENA_TELEPORT_FAILED("&cUnable to teleport you to the arena."),
|
||||
|
||||
/**
|
||||
* The message displayed if the player tries to quit the arena while not in an arena
|
||||
*/
|
||||
ERROR_NOT_IN_ARENA("&cYou are not in a mini-games arena!"),
|
||||
|
||||
/**
|
||||
* The message displayed if the player tries to join an arena while already playing
|
||||
*
|
||||
* <p>This should in theory be impossible, as players cannot use any commands except /miniGamesLeave while playing
|
||||
* in an arena.</p>
|
||||
*/
|
||||
ERROR_ALREADY_PLAYING("&cYou are already playing a mini-game!"),
|
||||
|
||||
/**
|
||||
* The message displayed if a player tries to join an arena with a passenger or riding a vehicle
|
||||
*/
|
||||
ERROR_JOIN_IN_VEHICLE_OR_PASSENGER("&cYou cannot join an arena while inside a vehicle or carrying a passenger."),
|
||||
|
||||
/**
|
||||
* The message displayed if the player tries to change an unrecognized arena property
|
||||
*/
|
||||
ERROR_UNKNOWN_PROPERTY("&cUnknown property specified."),
|
||||
|
||||
/**
|
||||
* The message displayed if the given input to /dEdit or /pEdit's value is invalid
|
||||
*/
|
||||
ERROR_PROPERTY_INPUT_INVALID("&cUnable to change the property. Make sure your input is valid!"),
|
||||
|
||||
/**
|
||||
* The message displayed if the first arena specified in /dgSwap or /pgSwap is invalid
|
||||
*/
|
||||
ERROR_ARENA_1_NOT_FOUND("&cUnable to find the first specified arena."),
|
||||
|
||||
/**
|
||||
* The message displayed if the second arena specified in /dgSwap or /pgSwap is invalid
|
||||
*/
|
||||
ERROR_ARENA_2_NOT_FOUND("&cUnable to find the second specified dropper arena."),
|
||||
|
||||
/**
|
||||
* The message displayed if the two groups specified for /dgSwap or /pgSwap are in different arenas
|
||||
*/
|
||||
ERROR_SWAP_DIFFERENT_GROUPS("&cYou cannot swap arenas in different groups!"),
|
||||
|
||||
/**
|
||||
* The message displayed if a player tries to use any command other than /mLeave while in an arena
|
||||
*/
|
||||
ERROR_ILLEGAL_COMMAND("&cYou cannot use that command while in an arena!"),
|
||||
|
||||
/* **************** *
|
||||
* Success messages *
|
||||
* **************** */
|
||||
|
||||
/**
|
||||
* The message displayed if an arena's group has been changed
|
||||
*/
|
||||
SUCCESS_ARENA_GROUP_UPDATED("&aThe arena's group has been updated"),
|
||||
|
||||
/**
|
||||
* The message displayed if the MiniGames plugin is reloaded
|
||||
*/
|
||||
SUCCESS_PLUGIN_RELOADED("&aPlugin reloaded!"),
|
||||
|
||||
/**
|
||||
* The message displayed if a new arena has been created
|
||||
*/
|
||||
SUCCESS_ARENA_CREATED("&aThe arena was successfully created!"),
|
||||
|
||||
/**
|
||||
* The message displayed if a player clears/wins an arena for the first time
|
||||
*/
|
||||
SUCCESS_ARENA_FIRST_CLEAR("&aYou cleared the arena!"),
|
||||
|
||||
/**
|
||||
* The message displayed when a player wins an arena
|
||||
*/
|
||||
SUCCESS_ARENA_WIN("&aYou won!"),
|
||||
|
||||
/**
|
||||
* The message displayed when a player quits an arena
|
||||
*/
|
||||
SUCCESS_ARENA_QUIT("&aYou quit the arena!"),
|
||||
|
||||
/**
|
||||
* The message used to display the current value of an arena property
|
||||
*/
|
||||
SUCCESS_CURRENT_VALUE("&aCurrent value of {property} is: {value}"),
|
||||
|
||||
/**
|
||||
* The message used to announce that an arena property has been changed
|
||||
*/
|
||||
SUCCESS_PROPERTY_CHANGED("&aProperty {property} successfully changed"),
|
||||
|
||||
/**
|
||||
* The message displayed when two arenas' order in a group have been swapped
|
||||
*/
|
||||
SUCCESS_ARENAS_SWAPPED("&aThe arenas have been swapped!"),
|
||||
|
||||
/**
|
||||
* The message displayed when an arena has been removed
|
||||
*/
|
||||
SUCCESS_ARENA_REMOVED("&aThe specified arena has been successfully removed"),
|
||||
|
||||
/**
|
||||
* The header displayed before listing all dropper arenas
|
||||
*/
|
||||
SUCCESS_DROPPER_ARENAS_LIST("&aDropper arenas:&r"),
|
||||
|
||||
/**
|
||||
* The header displayed before listing all parkour arenas
|
||||
*/
|
||||
SUCCESS_PARKOUR_ARENAS_LIST("&aParkour arenas:&r"),
|
||||
|
||||
/**
|
||||
* The message displayed when a player reaches a new checkpoint in a parkour arena
|
||||
*/
|
||||
SUCCESS_CHECKPOINT_REACHED("&aCheckpoint reached!"),
|
||||
|
||||
/**
|
||||
* The header displayed before listing all arenas (stages) in a group
|
||||
*/
|
||||
SUCCESS_GROUP_STAGES("&a{group}'s stages:&r"),
|
||||
|
||||
/**
|
||||
* The message displayed when a new record has been achieved
|
||||
*/
|
||||
SUCCESS_RECORD_ACHIEVED("&aYou just set a {recordInfo} on the {gameMode} game-mode!"),
|
||||
|
||||
/**
|
||||
* The partial message used to describe that the player achieved a world record
|
||||
*/
|
||||
RECORD_ACHIEVED_GLOBAL("new {recordType} record"),
|
||||
|
||||
/**
|
||||
* The partial message used to describe that the player achieved a personal best record
|
||||
*/
|
||||
RECORD_ACHIEVED_PERSONAL("personal {recordType} record"),
|
||||
|
||||
/**
|
||||
* The message displayed when a player joins an arena
|
||||
*/
|
||||
SUCCESS_ARENA_JOINED("&aYou joined the arena."),
|
||||
;
|
||||
|
||||
private final @NotNull String defaultMessage;
|
||||
|
||||
/**
|
||||
* Instantiates a new message
|
||||
*
|
||||
* @param defaultMessage <p>The default value of the message</p>
|
||||
*/
|
||||
Message(@NotNull String defaultMessage) {
|
||||
this.defaultMessage = defaultMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message this enum represents
|
||||
*
|
||||
* @return <p>The formatted message</p>
|
||||
*/
|
||||
public @NotNull String getMessage() {
|
||||
return formatMessage(this.defaultMessage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message this enum represents
|
||||
*
|
||||
* @param placeholder <p>The placeholder to replace</p>
|
||||
* @param replacement <p>The replacement to use</p>
|
||||
* @return <p>The formatted message</p>
|
||||
*/
|
||||
public @NotNull String getMessage(@NotNull String placeholder, @NotNull String replacement) {
|
||||
return formatMessage(this.defaultMessage.replace(placeholder, replacement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message this enum represents, intended for display within another message
|
||||
*
|
||||
* @param placeholder <p>The placeholder to replace</p>
|
||||
* @param replacement <p>The replacement to use</p>
|
||||
* @return <p>The formatted message</p>
|
||||
*/
|
||||
public @NotNull String getPartialMessage(@NotNull String placeholder, @NotNull String replacement) {
|
||||
return ColorHelper.translateAllColorCodes(this.defaultMessage.replace(placeholder, replacement));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message this enum represents
|
||||
*
|
||||
* @param placeholders <p>The placeholder -> replacement map specifying necessary replacements</p>
|
||||
* @return <p>The formatted message</p>
|
||||
*/
|
||||
public @NotNull String getMessage(@NotNull PlaceholderContainer placeholders) {
|
||||
String replaced = this.defaultMessage;
|
||||
for (Map.Entry<String, String> entry : placeholders.getPlaceholders().entrySet()) {
|
||||
replaced = replaced.replace(entry.getKey(), entry.getValue());
|
||||
}
|
||||
return formatMessage(replaced);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the formatted version of the given message
|
||||
*
|
||||
* @param message <p>The message to format</p>
|
||||
* @return <p>The formatted message</p>
|
||||
*/
|
||||
private @NotNull String formatMessage(@NotNull String message) {
|
||||
String prefix = MiniGames.getInstance().getDescription().getPrefix();
|
||||
return ColorHelper.translateAllColorCodes("#546EED[&r&l" + prefix + "#546EED]&r " + message);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.knarcraft.minigames.config;
|
||||
|
||||
import net.knarcraft.minigames.util.MaterialHelper;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A configuration for a mini-game
|
||||
*/
|
||||
public abstract class MiniGameConfiguration {
|
||||
|
||||
protected FileConfiguration configuration;
|
||||
|
||||
/**
|
||||
* Instantiates a new mini-game configuration
|
||||
*
|
||||
* @param configuration <p>The YAML configuration to use internally</p>
|
||||
*/
|
||||
public MiniGameConfiguration(@NotNull FileConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
this.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all configuration values from disk
|
||||
*
|
||||
* @param configuration <p>The configuration to load</p>
|
||||
*/
|
||||
public void load(FileConfiguration configuration) {
|
||||
this.configuration = configuration;
|
||||
this.load();
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all configuration values from disk, using the current file configuration
|
||||
*/
|
||||
protected abstract void load();
|
||||
|
||||
/**
|
||||
* Loads the materials specified in the block whitelist
|
||||
*/
|
||||
public @NotNull Set<Material> loadMaterialList(@NotNull String path) {
|
||||
List<?> blockWhitelist = configuration.getList(path, new ArrayList<>());
|
||||
return MaterialHelper.loadMaterialList(blockWhitelist);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
package net.knarcraft.minigames.config;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* The configuration keeping track of parkour settings
|
||||
*/
|
||||
public class ParkourConfiguration extends MiniGameConfiguration {
|
||||
|
||||
private final static String rootNode = "parkour.";
|
||||
|
||||
private boolean enforceCheckpointOrder;
|
||||
private boolean mustDoGroupedInSequence;
|
||||
private boolean ignoreRecordsUntilGroupBeatenOnce;
|
||||
private boolean makePlayersInvisible;
|
||||
private Set<Material> killPlaneBlocks;
|
||||
|
||||
/**
|
||||
* Instantiates a new dropper configuration
|
||||
*
|
||||
* @param configuration <p>The YAML configuration to use internally</p>
|
||||
*/
|
||||
public ParkourConfiguration(FileConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether all checkpoints must be triggered in the order they are set when configuring the parkour arena
|
||||
*
|
||||
* @return <p>Whether checkpoints must be triggered in order</p>
|
||||
*/
|
||||
public boolean enforceCheckpointOrder() {
|
||||
return this.enforceCheckpointOrder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether grouped arenas must be done in the set sequence
|
||||
*
|
||||
* @return <p>Whether grouped arenas must be done in sequence</p>
|
||||
*/
|
||||
public boolean mustDoGroupedInSequence() {
|
||||
return this.mustDoGroupedInSequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether records should be discarded, unless the player has already beaten all arenas in the group
|
||||
*
|
||||
* @return <p>Whether to ignore records on the first play-through</p>
|
||||
*/
|
||||
public boolean ignoreRecordsUntilGroupBeatenOnce() {
|
||||
return this.ignoreRecordsUntilGroupBeatenOnce;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets whether players should be made invisible while in an arena
|
||||
*
|
||||
* @return <p>Whether players should be made invisible</p>
|
||||
*/
|
||||
public boolean makePlayersInvisible() {
|
||||
return this.makePlayersInvisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all types of blocks constituting parkour arenas' kill planes
|
||||
*
|
||||
* @return <p>The types of blocks causing a player to fail a parkour map</p>
|
||||
*/
|
||||
public Set<Material> getKillPlaneBlocks() {
|
||||
return new HashSet<>(this.killPlaneBlocks);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() {
|
||||
this.enforceCheckpointOrder = configuration.getBoolean(rootNode + "enforceCheckpointOrder", false);
|
||||
this.mustDoGroupedInSequence = configuration.getBoolean(rootNode + "mustDoGroupedInSequence", true);
|
||||
this.ignoreRecordsUntilGroupBeatenOnce = configuration.getBoolean(rootNode + "ignoreRecordsUntilGroupBeatenOnce", false);
|
||||
this.makePlayersInvisible = configuration.getBoolean(rootNode + "makePlayersInvisible", false);
|
||||
this.killPlaneBlocks = loadMaterialList(rootNode + "killPlaneBlocks");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder builder = new StringBuilder(
|
||||
"Current configuration:" +
|
||||
"Current configuration:" +
|
||||
"\n" + "Must do groups in sequence: " + mustDoGroupedInSequence +
|
||||
"\n" + "Ignore records until group beaten once: " + ignoreRecordsUntilGroupBeatenOnce +
|
||||
"\n" + "Make players invisible: " + makePlayersInvisible +
|
||||
"\n" + "Kill plane blocks: ");
|
||||
for (Material material : killPlaneBlocks) {
|
||||
builder.append("\n - ").append(material.name());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package net.knarcraft.minigames.config;
|
||||
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* The configuration keeping track of shared settings
|
||||
*/
|
||||
public class SharedConfiguration extends MiniGameConfiguration {
|
||||
|
||||
private final static String rootNode = "shared.";
|
||||
private double liquidHitBoxDepth;
|
||||
private double solidHitBoxDistance;
|
||||
|
||||
/**
|
||||
* Instantiates a new shared configuration
|
||||
*
|
||||
* @param configuration <p>The YAML configuration to use internally</p>
|
||||
*/
|
||||
public SharedConfiguration(@NotNull FileConfiguration configuration) {
|
||||
super(configuration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the negative depth a player must reach in a liquid block for fail/win detection to trigger
|
||||
*
|
||||
* <p>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 increases.</p>
|
||||
*
|
||||
* @return <p>The liquid hit box depth to use</p>
|
||||
*/
|
||||
public double getLiquidHitBoxDepth() {
|
||||
return this.liquidHitBoxDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the positive distance a player must at most be from a block for fail/win detection to trigger
|
||||
*
|
||||
* <p>This decides the distance the player must be from a block below them before a hit triggers. If too low, the
|
||||
* likelihood of detecting the hit decreases, but it won't look like the player hit the block without being near.</p>
|
||||
*
|
||||
* @return <p>The solid hit box distance to use</p>
|
||||
*/
|
||||
public double getSolidHitBoxDistance() {
|
||||
return this.solidHitBoxDistance;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void load() {
|
||||
this.liquidHitBoxDepth = configuration.getDouble(rootNode + "liquidHitBoxDepth", -0.8);
|
||||
this.solidHitBoxDistance = configuration.getDouble(rootNode + "solidHitBoxDistance", 0.2);
|
||||
|
||||
if (this.liquidHitBoxDepth <= -1 || this.liquidHitBoxDepth > 0) {
|
||||
this.liquidHitBoxDepth = -0.8;
|
||||
}
|
||||
|
||||
if (this.solidHitBoxDistance <= 0 || this.solidHitBoxDistance > 1) {
|
||||
this.solidHitBoxDistance = 0.2;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Current configuration:" +
|
||||
"\n" + "Liquid hit box depth: " + liquidHitBoxDepth +
|
||||
"\n" + "Solid hit box distance: " + solidHitBoxDistance;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
package net.knarcraft.minigames.container;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A container for keeping track of several placeholder to value mappings
|
||||
*/
|
||||
public class PlaceholderContainer {
|
||||
|
||||
private final Map<String, String> placeholders;
|
||||
|
||||
/**
|
||||
* Instantiates a new placeholder container
|
||||
*/
|
||||
public PlaceholderContainer() {
|
||||
this.placeholders = new HashMap<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all placeholders
|
||||
*
|
||||
* @return <p>All placeholders</p>
|
||||
*/
|
||||
public Map<String, String> getPlaceholders() {
|
||||
return new HashMap<>(this.placeholders);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new placeholder
|
||||
*
|
||||
* @param placeholder <p>The placeholder to register</p>
|
||||
* @param value <p>The value of the placeholder</p>
|
||||
* @return <p>This object</p>
|
||||
*/
|
||||
public PlaceholderContainer add(String placeholder, String value) {
|
||||
this.placeholders.put(placeholder, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package net.knarcraft.minigames.container;
|
||||
|
||||
import org.bukkit.configuration.serialization.ConfigurationSerializable;
|
||||
|
||||
/**
|
||||
* A container that is serializable
|
||||
*
|
||||
* @param <K> <p>The type of the contained object</p>
|
||||
*/
|
||||
public abstract class SerializableContainer<K> implements ConfigurationSerializable {
|
||||
|
||||
private final K value;
|
||||
|
||||
/**
|
||||
* Instantiates a new serializable container
|
||||
*
|
||||
* @param value <p>The value to contain</p>
|
||||
*/
|
||||
public SerializableContainer(K value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the raw, non-serializable object
|
||||
*
|
||||
* @return <p>The raw stored value</p>
|
||||
*/
|
||||
public K getRawValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a serializable container containing the given value
|
||||
*
|
||||
* @param value <p>The value to make serializable</p>
|
||||
* @return <p>The serializable value</p>
|
||||
*/
|
||||
public abstract SerializableContainer<K> getSerializable(K value);
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object instanceof SerializableContainer<?>) {
|
||||
return this.getRawValue().equals(((SerializableContainer<?>) object).getRawValue());
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.knarcraft.minigames.container;
|
||||
|
||||
import org.bukkit.Material;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A material container able to be serialized
|
||||
*/
|
||||
public class SerializableMaterial extends SerializableContainer<Material> {
|
||||
|
||||
/**
|
||||
* Instantiates a new serializable material
|
||||
*
|
||||
* @param material <p>The material to contain</p>
|
||||
*/
|
||||
public SerializableMaterial(Material material) {
|
||||
super(material);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializableContainer<Material> getSerializable(Material value) {
|
||||
return new SerializableMaterial(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("name", getRawValue().name());
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a serialized material
|
||||
*
|
||||
* @param data <p>The serialized data</p>
|
||||
* @return <p>The deserialized material</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static SerializableMaterial deserialize(Map<String, Object> data) {
|
||||
Material material = Material.matchMaterial((String) data.get("name"));
|
||||
if (material == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new SerializableMaterial(material);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
package net.knarcraft.minigames.container;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A UUID container able to be serialized
|
||||
*/
|
||||
public class SerializableUUID extends SerializableContainer<UUID> {
|
||||
|
||||
/**
|
||||
* Instantiates a new serializable uuid
|
||||
*
|
||||
* @param value <p>The uuid to contain</p>
|
||||
*/
|
||||
public SerializableUUID(UUID value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SerializableContainer<UUID> getSerializable(UUID value) {
|
||||
return new SerializableUUID(value);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public Map<String, Object> serialize() {
|
||||
Map<String, Object> data = new HashMap<>();
|
||||
data.put("id", getRawValue().toString());
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes a serialized UUID
|
||||
*
|
||||
* @param data <p>The serialized data</p>
|
||||
* @return <p>The deserialized UUID</p>
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public static SerializableUUID deserialize(Map<String, Object> data) {
|
||||
String id = (String) data.getOrDefault("id", null);
|
||||
if (id != null) {
|
||||
return new SerializableUUID(UUID.fromString(id));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
package net.knarcraft.minigames.listener;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaSession;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerCommandPreprocessEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A listener for players trying to use commands while inside a dropper arena
|
||||
*/
|
||||
public class CommandListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onCommand(PlayerCommandPreprocessEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
ArenaSession existingSession = MiniGames.getInstance().getSession(player.getUniqueId());
|
||||
if (existingSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> allowedCommands = new ArrayList<>();
|
||||
allowedCommands.add("/miniGamesLeave");
|
||||
allowedCommands.add("/mLeave");
|
||||
allowedCommands.add("/dLeave");
|
||||
allowedCommands.add("/pLeave");
|
||||
|
||||
String message = event.getMessage();
|
||||
if (!message.startsWith("/")) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (String command : allowedCommands) {
|
||||
if (message.equalsIgnoreCase(command)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
player.sendMessage(Message.ERROR_ILLEGAL_COMMAND.getMessage());
|
||||
event.setCancelled(true);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
package net.knarcraft.minigames.listener;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.ArenaSession;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.entity.EntityCombustEvent;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
|
||||
/**
|
||||
* A listener for checking if a player takes damage within a dropper arena
|
||||
*/
|
||||
public class DamageListener implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerDamage(EntityDamageEvent event) {
|
||||
// Only player damage matters
|
||||
if (event.getEntityType() != EntityType.PLAYER) {
|
||||
return;
|
||||
}
|
||||
|
||||
Player player = (Player) event.getEntity();
|
||||
|
||||
// We don't care about damage outside arenas
|
||||
ArenaSession arenaSession = MiniGames.getInstance().getSession(player.getUniqueId());
|
||||
if (arenaSession == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
// Only trigger a loss when a player suffers fall damage in a dropper arena
|
||||
if (arenaSession instanceof DropperArenaSession && event.getCause() == EntityDamageEvent.DamageCause.FALL) {
|
||||
arenaSession.triggerLoss();
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true)
|
||||
public void onPlayerCombustion(EntityCombustEvent event) {
|
||||
if (event.getEntityType() != EntityType.PLAYER) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArenaSession arenaSession = MiniGames.getInstance().getSession(event.getEntity().getUniqueId());
|
||||
if (arenaSession != null) {
|
||||
// Cancel combustion for any player in an arena
|
||||
event.setCancelled(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
234
src/main/java/net/knarcraft/minigames/listener/MoveListener.java
Normal file
234
src/main/java/net/knarcraft/minigames/listener/MoveListener.java
Normal file
@ -0,0 +1,234 @@
|
||||
package net.knarcraft.minigames.listener;
|
||||
|
||||
import net.knarcraft.minigames.MiniGames;
|
||||
import net.knarcraft.minigames.arena.Arena;
|
||||
import net.knarcraft.minigames.arena.ArenaSession;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaGameMode;
|
||||
import net.knarcraft.minigames.arena.dropper.DropperArenaSession;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArena;
|
||||
import net.knarcraft.minigames.arena.parkour.ParkourArenaSession;
|
||||
import net.knarcraft.minigames.config.DropperConfiguration;
|
||||
import net.knarcraft.minigames.config.Message;
|
||||
import net.knarcraft.minigames.config.ParkourConfiguration;
|
||||
import net.knarcraft.minigames.config.SharedConfiguration;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* A listener for players moving inside a dropper arena
|
||||
*/
|
||||
public class MoveListener implements Listener {
|
||||
|
||||
private final DropperConfiguration dropperConfiguration;
|
||||
private final ParkourConfiguration parkourConfiguration;
|
||||
|
||||
/**
|
||||
* Instantiates a new move listener
|
||||
*
|
||||
* @param dropperConfiguration <p>The dropper configuration to use</p>
|
||||
* @param parkourConfiguration <p>The parkour configuration to use</p>
|
||||
*/
|
||||
public MoveListener(DropperConfiguration dropperConfiguration, ParkourConfiguration parkourConfiguration) {
|
||||
this.dropperConfiguration = dropperConfiguration;
|
||||
this.parkourConfiguration = parkourConfiguration;
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onPlayerMove(PlayerMoveEvent event) {
|
||||
// Ignore if no actual movement is happening
|
||||
if (event.getFrom().equals(event.getTo()) || event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ArenaSession session = MiniGames.getInstance().getSession(event.getPlayer().getUniqueId());
|
||||
if (session instanceof DropperArenaSession dropperSession) {
|
||||
doDropperArenaChecks(event, dropperSession);
|
||||
} else if (session instanceof ParkourArenaSession parkourSession) {
|
||||
doParkourArenaChecks(event, parkourSession);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the necessary checks and tasks for the player's session
|
||||
*
|
||||
* @param event <p>The move event triggered</p>
|
||||
* @param arenaSession <p>The dropper session of the player triggering the event</p>
|
||||
*/
|
||||
private void doParkourArenaChecks(@NotNull PlayerMoveEvent event, ParkourArenaSession arenaSession) {
|
||||
// Ignore movement which won't cause the player's block to change
|
||||
if (event.getTo() == null || event.getFrom().getBlock() == event.getTo().getBlock()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only do block type checking if the block beneath the player changes
|
||||
if (checkForSpecialBlock(arenaSession, event.getTo())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the player reached one of the checkpoints for the arena
|
||||
ParkourArena arena = arenaSession.getArena();
|
||||
List<Location> checkpoints = arena.getCheckpoints();
|
||||
for (Location checkpoint : checkpoints) {
|
||||
Location previousCheckpoint = arenaSession.getRegisteredCheckpoint();
|
||||
|
||||
// Skip if checkpoint has not been reached
|
||||
if (!checkpoint.getBlock().equals(event.getTo().getBlock())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// If the checkpoint is the same as the previously reached one, abort
|
||||
if (previousCheckpoint != null && checkpoint.getBlock().equals(previousCheckpoint.getBlock())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If not the correct checkpoint according to the enforced order, abort
|
||||
if (parkourConfiguration.enforceCheckpointOrder()) {
|
||||
int checkpointIndex = checkpoints.indexOf(checkpoint);
|
||||
int previousIndex = previousCheckpoint == null ? -1 : checkpoints.indexOf(previousCheckpoint);
|
||||
if (checkpointIndex - previousIndex != 1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Register the checkpoint
|
||||
arenaSession.registerCheckpoint(checkpoint.clone());
|
||||
event.getPlayer().sendMessage(Message.SUCCESS_CHECKPOINT_REACHED.getMessage());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the necessary checks and tasks for the player's session
|
||||
*
|
||||
* @param event <p>The move event triggered</p>
|
||||
* @param arenaSession <p>The dropper session of the player triggering the event</p>
|
||||
*/
|
||||
private void doDropperArenaChecks(@NotNull PlayerMoveEvent event, @NotNull DropperArenaSession arenaSession) {
|
||||
if (event.getTo() == null) {
|
||||
return;
|
||||
}
|
||||
// Prevent the player from flying upwards while in flight mode
|
||||
if (event.getFrom().getY() < event.getTo().getY() ||
|
||||
(dropperConfiguration.blockSneaking() && event.getPlayer().isSneaking()) ||
|
||||
(dropperConfiguration.blockSprinting() && event.getPlayer().isSprinting())) {
|
||||
event.setCancelled(true);
|
||||
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(ArenaSession arenaSession, Location toLocation) {
|
||||
SharedConfiguration sharedConfiguration = MiniGames.getInstance().getSharedConfiguration();
|
||||
double liquidDepth = sharedConfiguration.getLiquidHitBoxDepth();
|
||||
double solidDepth = sharedConfiguration.getSolidHitBoxDistance();
|
||||
|
||||
Arena arena = arenaSession.getArena();
|
||||
|
||||
// For water, only trigger when the player enters the water, but trigger earlier for everything else
|
||||
double depth = arena.winLocationIsSolid() ? solidDepth : liquidDepth;
|
||||
for (Block block : getBlocksBeneathLocation(toLocation, depth)) {
|
||||
if (arena.willCauseWin(block)) {
|
||||
arenaSession.triggerWin();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 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() && arena.willCauseLoss(block)) {
|
||||
arenaSession.triggerLoss();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the blocks at the given location that will be affected by the player's hit-box
|
||||
*
|
||||
* @param location <p>The location to check</p>
|
||||
* @return <p>The blocks beneath the player</p>
|
||||
*/
|
||||
private Set<Block> getBlocksBeneathLocation(Location location, double depth) {
|
||||
Set<Block> blocksBeneath = new HashSet<>();
|
||||
double halfPlayerWidth = 0.3;
|
||||
blocksBeneath.add(location.clone().subtract(halfPlayerWidth, depth, halfPlayerWidth).getBlock());
|
||||
blocksBeneath.add(location.clone().subtract(-halfPlayerWidth, depth, halfPlayerWidth).getBlock());
|
||||
blocksBeneath.add(location.clone().subtract(halfPlayerWidth, depth, -halfPlayerWidth).getBlock());
|
||||
blocksBeneath.add(location.clone().subtract(-halfPlayerWidth, depth, -halfPlayerWidth).getBlock());
|
||||
return blocksBeneath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the velocity of the player in the given session
|
||||
*
|
||||
* @param session <p>The session to update the velocity for</p>
|
||||
*/
|
||||
private void updatePlayerVelocity(@NotNull DropperArenaSession session) {
|
||||
// Override the vertical velocity
|
||||
Player player = session.getPlayer();
|
||||
Vector playerVelocity = player.getVelocity();
|
||||
double arenaVelocity = session.getArena().getPlayerVerticalVelocity();
|
||||
Vector newVelocity = new Vector(playerVelocity.getX() * 5, -arenaVelocity, playerVelocity.getZ() * 5);
|
||||
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() != DropperArenaGameMode.RANDOM_INVERTED) {
|
||||
return;
|
||||
}
|
||||
Player player = session.getPlayer();
|
||||
float horizontalVelocity = session.getArena().getPlayerHorizontalVelocity();
|
||||
float secondsBetweenToggle = dropperConfiguration.getRandomlyInvertedTimer();
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user