Focus on bow spleef and classic spleef
This commit is contained in:
1 parent
63acbc99d5
commit
07433ba005
20 files changed
+429
-651
No files matched your search
+8
-6
@@ -3,12 +3,14 @@
|
||||
## 2.0.1-SNAPSHOT:
|
||||
|
||||
- First beta-ready snapshot of the Spleef-OG fork. Targets Purpur / Paper 1.19.4.
|
||||
- **Editor layer lookup**: `SpleefMap.positionToLayers` is now maintained on `addLayer`, `removeLayer`, and `clearLayers`. Layers created via `/spleef layer add` are immediately usable; no server restart required. Previously the lookup map was only populated in `postProcess`, so runtime-added layers were invisible to block-break, decay, and projectile-hit logic.
|
||||
- **Decay scheduler crash**: `SpleefCompetition.beginLayerDecay` now guards against empty layers and clamps the per-block interval to `Math.max(1, totalTicks / blocks)`. Prevents `ArithmeticException` on empty layers and runaway 0-tick-period timers on very large layers.
|
||||
- **Splegg phantom eggs**: `SpleefArena.onInteract` now returns early when the held item is not a tagged spleef item. Previously an egg was launched on every right-click while in SPLEGG mode, regardless of the item in hand.
|
||||
- **Mode scope**: Spleef-OG now supports only Classic Spleef and Bow Spleef. Unsupported projectile-cannon and TNT Run code paths and templates were removed.
|
||||
- **Classic snowballs**: Breaking configured snow floor layers now grants throwable snowballs, and thrown snowballs break configured Spleef layer blocks.
|
||||
- **WorldGuard region flow**: Arenas are intended to run in WorldGuard regions in the main spawn world. The default `world-whitelist` now targets `world`, and `/spleef worldguardregion <map> <region>` stores the map region ID.
|
||||
- **Region whitelist**: `config.yml` now supports `region-whitelist` for limiting Spleef arena creation, joining, resets, floor breaking, projectile floor breaking, and Bow Spleef TNT handling to specific WorldGuard regions.
|
||||
- **Editor layer lookup**: `SpleefMap.positionToLayers` is now maintained on `addLayer`, `removeLayer`, and `clearLayers`. Layers created via `/spleef layer add` are immediately usable; no server restart required.
|
||||
- **NPE on layer removal**: `SpleefMap.removeLayer` and `clearLayers` null-guard `this.layers`, matching the lazy init in `addLayer`. `/spleef layer clear` on a fresh map no longer throws.
|
||||
- **Bow Spleef broken**: `SpleefEventResolvers.PROJECTILE_HIT` now accepts any `Projectile`, with dedicated branches for `Egg` (splegg or `projectiles-break-blocks`) and `AbstractArrow` (BOW_SPLEEF or `projectiles-break-blocks`). Arrows now break layer blocks in bow-spleef mode.
|
||||
- **Bow Spleef projectiles**: Arrows break layer blocks in bow-spleef mode.
|
||||
- **`SpleefConfig.getShovel` NPE**: Now null-safe when the configured shovel name is unknown, honoring the `@Nullable` contract. Prevents crash on typos in arena YAML (e.g. `give-shovel{shovel=clasic}`).
|
||||
- **PlayerMoveEvent hot path**: `SpleefArena.onMove` now returns early when `from` and `to` share block coordinates, cutting per-tick overhead for the death-region and decay-trigger checks by a large margin.
|
||||
- **Scheduler leak on disable**: `ArenaSpleef.onDisable` cancels all scheduler tasks owned by the plugin, preventing leaked decay tasks from firing against stale map references after reload.
|
||||
- **PlayerMoveEvent hot path**: `SpleefArena.onMove` now returns early when `from` and `to` share block coordinates, cutting per-tick overhead for death-region checks by a large margin.
|
||||
- **Scheduler cleanup on disable**: `ArenaSpleef.onDisable` cancels all scheduler tasks owned by the plugin, preventing delayed tasks from firing against stale map references after reload.
|
||||
- Gradle build updated to use the TrueOG bootstrap maven-local repository when available, falling back to `~/.m2`.
|
||||
@@ -1,54 +1,83 @@
|
||||
# Spleef-OG
|
||||
|
||||
A spleef plugin using [BattleArena](https://github.com/BattlePlugins/BattleArena).
|
||||
A BattleArena-backed Spleef plugin for Purpur / Paper 1.19.4.
|
||||
|
||||
Spleef-OG is a fork of BattlePlugins' ArenaSpleef, maintained for Purpur 1.19.4. It keeps the classic spleef experience alongside multiple modes and addresses critical stability bugs that blocked beta release of upstream 2.0.x.
|
||||
Spleef-OG is maintained for TrueOG-style server-spawn minigames. It runs arenas in WorldGuard regions in the main world, not in MyWorlds world copies.
|
||||
|
||||
## Spleef Modes
|
||||
- **Classic**: Players receive a shovel to break blocks and knock other players into the void.
|
||||
- **Splegg**: Players receive an egg cannon to shoot eggs that break blocks.
|
||||
- **Decay**: Blocks decay beneath the player. Commonly played as "TNT Run", but any block type works.
|
||||
- **Bow Spleef**: Players receive a bow to shoot arrows that break blocks. TNT layers prime on hit.
|
||||
## Supported Modes
|
||||
- **Classic Spleef**: players receive a shovel and break configured snow floor layers. Breaking `snow_block` floors grants 4 snowballs; breaking `snow` layers grants snowballs equal to the layer count. Thrown snowballs break configured Spleef layer blocks.
|
||||
- **Bow Spleef**: players receive a bow and arrow. Arrows break configured layer blocks, and TNT projectile handling is gated by a configured WorldGuard region.
|
||||
|
||||
For example configurations of these modes, see the [templates](./templates) folder.
|
||||
For example configurations, see the [templates](./templates) folder.
|
||||
|
||||
## Region Model
|
||||
- Spleef arenas are intended to live in WorldGuard regions in the main `world` near server spawn.
|
||||
- `config.yml` defaults `world-whitelist` to `world`. Add your real spawn-world name there if it differs.
|
||||
- `region-whitelist` can restrict Spleef to specific WorldGuard region IDs. Leave it empty to allow any region in an allowed world.
|
||||
- Arena creation requires standing in a WorldGuard region with `allow-spleef` set to `allow`.
|
||||
- Maps can set `worldguard-region` to their region ID. Bow Spleef maps should set it to the region that covers the TNT/layer area.
|
||||
- When `region-whitelist` is set, joins, layer resets, block breaks, snowball/arrow block hits, and Bow Spleef TNT handling are limited to whitelisted regions.
|
||||
- Spleef-OG does not depend on MyWorlds and does not create per-match world copies.
|
||||
|
||||
Example setup flow:
|
||||
```text
|
||||
/rg define spleef_classic
|
||||
/rg flag spleef_classic allow-spleef allow
|
||||
/spleef create classic
|
||||
/spleef layer add classic
|
||||
/spleef deathregion classic
|
||||
```
|
||||
|
||||
For Bow Spleef, also run:
|
||||
```text
|
||||
/spleef worldguardregion bowspleef spleef_bow
|
||||
```
|
||||
|
||||
Example `config.yml` whitelist:
|
||||
```yaml
|
||||
world-whitelist:
|
||||
- world
|
||||
region-whitelist:
|
||||
- spleef_classic
|
||||
- spleef_bow
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
- **Server**: Purpur / Paper 1.19.4
|
||||
- **Java**: 17+
|
||||
- **Depends**: BattleArena 4.0.0-SNAPSHOT, WorldGuard
|
||||
- **Soft-depends**: HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, WorldEdit
|
||||
|
||||
## Notes
|
||||
- **Arena reset**: Layer pasting now hard-resets configured layer volumes each round instead of only filling air blocks.
|
||||
- **Bow Spleef TNT**: TNT priming is now gated by WorldGuard. Set `worldguard-region: <region-id>` on the map used by a Bow Spleef arena, and make sure that region covers the TNT layer.
|
||||
- **Soft-depends**: GameModeInventories-OG, HorseTp-OG, PetTeleport-OG, PlayerBounties-OG, WorldEdit
|
||||
|
||||
## Integrations
|
||||
- **[PetTeleport-OG](https://github.com/true-og/PetTeleport-OG)**: pet teleports are suppressed while a player is in a Spleef match. No configuration required — PetTeleport-OG reads `SpleefAPI.isInSpleef`.
|
||||
- **[HorseTp-OG](https://github.com/true-og/HorseTp-OG)**: horse teleports are suppressed while a player is in a Spleef match. No configuration required — HorseTp-OG reads `SpleefAPI.isInSpleef`.
|
||||
- **[PlayerBounties-OG](https://github.com/true-og/PlayerBounties-OG)**: bounty claims and new bounty placements are cancelled whenever the claimant, victim, or target is in a Spleef match. Active automatically when PlayerBounties-OG is installed.
|
||||
- **PetTeleport-OG**: pet teleports are suppressed while a player is in a Spleef match. No configuration required; PetTeleport-OG reads `SpleefAPI.isInSpleef`.
|
||||
- **HorseTp-OG**: horse teleports are suppressed while a player is in a Spleef match. No configuration required; HorseTp-OG reads `SpleefAPI.isInSpleef`.
|
||||
- **PlayerBounties-OG**: bounty claims and new bounty placements are cancelled whenever the claimant, victim, or target is in a Spleef match.
|
||||
- **GameModeInventories-OG**: gamemode-specific inventories are preserved while players enter and leave Spleef.
|
||||
|
||||
## Commands
|
||||
| Command | Description |
|
||||
|---------------------------------------|------------------------------------------------|
|
||||
| /spleef deathregion <map> <region> | Sets the death region for a spleef arena. |
|
||||
| /spleef layer add <map> | Adds a layer to a spleef arena. |
|
||||
| /spleef layer remove <map> <index> | Removes a layer from a spleef arena. |
|
||||
| /spleef layer clear <map> | Clears all layers from a spleef arena. |
|
||||
| /spleef layer index <map> <from> <to> | Changes the index of a layer. |
|
||||
| /spleef layer list <map> | Lists all layers in a spleef arena. |
|
||||
| Command | Description |
|
||||
|-----------------------------------------------|-----------------------------------------------------|
|
||||
| `/spleef deathregion <map>` | Sets the death region for a Spleef arena. |
|
||||
| `/spleef layer add <map>` | Adds a floor layer to a Spleef arena. |
|
||||
| `/spleef layer remove <map> <index>` | Removes a layer from a Spleef arena. |
|
||||
| `/spleef layer clear <map>` | Clears all layers from a Spleef arena. |
|
||||
| `/spleef layer index <map> <from> <to>` | Changes the index of a layer. |
|
||||
| `/spleef layer list <map>` | Lists all layers in a Spleef arena. |
|
||||
| `/spleef worldguardregion <map> <region>` | Sets the map WorldGuard region ID. |
|
||||
|
||||
## Permissions
|
||||
| Permission | Command |
|
||||
|-----------------------------------------|----------------------|
|
||||
| battlearena.command.spleef.deathregion | /spleef deathregion |
|
||||
| battlearena.command.spleef.layer.add | /spleef layer add |
|
||||
| battlearena.command.spleef.layer.remove | /spleef layer remove |
|
||||
| battlearena.command.spleef.layer.clear | /spleef layer clear |
|
||||
| battlearena.command.spleef.layer.index | /spleef layer index |
|
||||
| battlearena.command.spleef.layer.list | /spleef layer list |
|
||||
| Permission | Command |
|
||||
|----------------------------------------------|---------------------------------|
|
||||
| `battlearena.command.spleef.deathregion` | `/spleef deathregion` |
|
||||
| `battlearena.command.spleef.layer.add` | `/spleef layer add` |
|
||||
| `battlearena.command.spleef.layer.remove` | `/spleef layer remove` |
|
||||
| `battlearena.command.spleef.layer.clear` | `/spleef layer clear` |
|
||||
| `battlearena.command.spleef.layer.index` | `/spleef layer index` |
|
||||
| `battlearena.command.spleef.layer.list` | `/spleef layer list` |
|
||||
| `battlearena.command.spleef.worldguardregion` | `/spleef worldguardregion` |
|
||||
|
||||
## API
|
||||
Spleef-OG exposes a small Bukkit-native API for other plugins. No BattleArena types on the surface — only `org.bukkit.entity.Player`.
|
||||
Spleef-OG exposes a small Bukkit-native API for other plugins. No BattleArena types on the surface, only `org.bukkit.entity.Player`.
|
||||
|
||||
### Check if a player is in a Spleef match
|
||||
Java:
|
||||
@@ -77,83 +106,10 @@ Both extend `org.bukkit.event.player.PlayerEvent`.
|
||||
| `SpleefJoinEvent` | Player joins any Spleef-mode competition | `true` |
|
||||
| `SpleefLeaveEvent` | Player leaves any Spleef-mode competition | `false` |
|
||||
|
||||
Covers every mode (Classic, Splegg, Decay, Bow Spleef). State flips before the event fires, so listeners observe the post-transition value.
|
||||
|
||||
### Consumer setup (Gradle Kotlin DSL)
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly(files("libs/Spleef-OG-<version>.jar"))
|
||||
}
|
||||
```
|
||||
|
||||
`plugin.yml`:
|
||||
```yaml
|
||||
depend: [Spleef-OG]
|
||||
```
|
||||
|
||||
### Listener examples
|
||||
Java:
|
||||
```java
|
||||
import org.battleplugins.arena.spleef.api.SpleefAPI;
|
||||
import org.battleplugins.arena.spleef.api.SpleefJoinEvent;
|
||||
import org.battleplugins.arena.spleef.api.SpleefLeaveEvent;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.Listener;
|
||||
|
||||
public class SpleefHooks implements Listener {
|
||||
|
||||
@EventHandler
|
||||
public void onJoin(SpleefJoinEvent event) {
|
||||
|
||||
Player player = event.getPlayer();
|
||||
// SpleefAPI.isInSpleef(player) == true
|
||||
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onLeave(SpleefLeaveEvent event) {
|
||||
|
||||
Player player = event.getPlayer();
|
||||
// SpleefAPI.isInSpleef(player) == false
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
Kotlin:
|
||||
```kotlin
|
||||
import org.battleplugins.arena.spleef.api.SpleefAPI
|
||||
import org.battleplugins.arena.spleef.api.SpleefJoinEvent
|
||||
import org.battleplugins.arena.spleef.api.SpleefLeaveEvent
|
||||
import org.bukkit.event.EventHandler
|
||||
import org.bukkit.event.Listener
|
||||
|
||||
class SpleefHooks : Listener {
|
||||
|
||||
@EventHandler
|
||||
fun onJoin(event: SpleefJoinEvent) {
|
||||
val player = event.player
|
||||
// SpleefAPI.isInSpleef(player) == true
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
fun onLeave(event: SpleefLeaveEvent) {
|
||||
val player = event.player
|
||||
// SpleefAPI.isInSpleef(player) == false
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
Covers Classic Spleef and Bow Spleef. State flips before the event fires, so listeners observe the post-transition value.
|
||||
|
||||
## Building
|
||||
```
|
||||
```text
|
||||
./gradlew build
|
||||
```
|
||||
Output: `build/libs/Spleef-OG-<version>.jar`
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ repositories {
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.4-R0.1-SNAPSHOT")
|
||||
compileOnly("org.battleplugins:arena:4.0.0-SNAPSHOT")
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.9")
|
||||
compileOnly("com.sk89q.worldguard:worldguard-bukkit:7.0.8") // Import WorldGuard API.
|
||||
}
|
||||
|
||||
/* ---------------------- Reproducible jars ---------------------------- */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.config.ArenaOption;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -10,20 +11,14 @@ import java.util.Map;
|
||||
|
||||
public class SpleefConfig {
|
||||
|
||||
@ArenaOption(name = "projectiles-break-blocks", description = "Whether snowballs should break blocks.")
|
||||
private boolean projectilesBreakBlocks = false;
|
||||
|
||||
@ArenaOption(name = "shovels", description = "The shovels for this spleef game.", required = true)
|
||||
private Map<String, ItemStack> shovels;
|
||||
|
||||
@ArenaOption(name = "world-whitelist", description = "Worlds where Spleef arenas may be created and run. Empty list = all worlds allowed.")
|
||||
private List<String> worldWhitelist;
|
||||
|
||||
public boolean shouldProjectilesBreakBlocks() {
|
||||
|
||||
return this.projectilesBreakBlocks;
|
||||
|
||||
}
|
||||
@ArenaOption(name = "region-whitelist", description = "WorldGuard regions where Spleef arenas may be created and matches may modify blocks. Empty list = all regions allowed.")
|
||||
private List<String> regionWhitelist;
|
||||
|
||||
@Nullable
|
||||
public ItemStack getShovel(String name) {
|
||||
@@ -74,4 +69,57 @@ public class SpleefConfig {
|
||||
|
||||
}
|
||||
|
||||
public boolean hasRegionWhitelist() {
|
||||
|
||||
return this.regionWhitelist != null && !this.regionWhitelist.isEmpty();
|
||||
|
||||
}
|
||||
|
||||
public boolean isRegionAllowed(String regionName) {
|
||||
|
||||
if (!this.hasRegionWhitelist()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
if (regionName == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
String target = regionName.toLowerCase(Locale.ROOT);
|
||||
for (String allowed : this.regionWhitelist) {
|
||||
|
||||
if (allowed != null && allowed.toLowerCase(Locale.ROOT).equals(target)) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
public boolean isRegionAllowedAt(Location location) {
|
||||
|
||||
if (!this.hasRegionWhitelist()) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
return WorldGuardSupport.isInsideAnyRegion(location, this.regionWhitelist);
|
||||
|
||||
}
|
||||
|
||||
public List<String> getRegionWhitelist() {
|
||||
|
||||
return this.regionWhitelist == null ? List.of() : List.copyOf(this.regionWhitelist);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import com.destroystokyo.paper.event.entity.ThrownEggHatchEvent;
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
import org.battleplugins.arena.BattleArena;
|
||||
import org.battleplugins.arena.competition.Competition;
|
||||
@@ -11,9 +10,9 @@ import org.battleplugins.arena.spleef.arena.SpleefCompetition;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefGame;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.bukkit.entity.AbstractArrow;
|
||||
import org.bukkit.entity.Egg;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.Projectile;
|
||||
import org.bukkit.entity.Snowball;
|
||||
import org.bukkit.event.block.TNTPrimeEvent;
|
||||
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||
import org.bukkit.projectiles.ProjectileSource;
|
||||
@@ -55,11 +54,16 @@ public final class SpleefEventResolvers {
|
||||
|
||||
}
|
||||
|
||||
boolean breakBlocks = ArenaSpleef.getInstance().getMainConfig().shouldProjectilesBreakBlocks();
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
if (config != null && !config.isRegionAllowedAt(event.getHitBlock().getLocation())) {
|
||||
|
||||
if (projectile instanceof Egg egg) {
|
||||
return null;
|
||||
|
||||
if (egg.hasMetadata("splegg") || breakBlocks) {
|
||||
}
|
||||
|
||||
if (projectile instanceof Snowball) {
|
||||
|
||||
if (arena.getGame() == SpleefGame.CLASSIC) {
|
||||
|
||||
return arenaPlayer.getCompetition();
|
||||
|
||||
@@ -71,7 +75,7 @@ public final class SpleefEventResolvers {
|
||||
|
||||
if (projectile instanceof AbstractArrow) {
|
||||
|
||||
if (arena.getGame() == SpleefGame.BOW_SPLEEF || breakBlocks) {
|
||||
if (arena.getGame() == SpleefGame.BOW_SPLEEF) {
|
||||
|
||||
return arenaPlayer.getCompetition();
|
||||
|
||||
@@ -81,42 +85,10 @@ public final class SpleefEventResolvers {
|
||||
|
||||
}
|
||||
|
||||
if (breakBlocks) {
|
||||
|
||||
return arenaPlayer.getCompetition();
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
};
|
||||
|
||||
public static final Function<ThrownEggHatchEvent, LiveCompetition<?>> THROWN_EGG_HATCH = event -> {
|
||||
|
||||
if (!event.getEgg().hasMetadata("splegg")) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
ProjectileSource shooter = event.getEgg().getShooter();
|
||||
if (!(shooter instanceof Player player)) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
ArenaPlayer arenaPlayer = ArenaPlayer.getArenaPlayer(player);
|
||||
if (arenaPlayer == null || !(arenaPlayer.getArena() instanceof SpleefArena)) {
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
return arenaPlayer.getCompetition();
|
||||
|
||||
};
|
||||
|
||||
public static final Function<SpleefArena, Function<TNTPrimeEvent, LiveCompetition<?>>> TNT_PRIME = arena -> event -> {
|
||||
|
||||
if (arena.getGame() != SpleefGame.BOW_SPLEEF) {
|
||||
@@ -154,6 +126,13 @@ public final class SpleefEventResolvers {
|
||||
|
||||
}
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
if (config != null && !config.isRegionAllowedAt(event.getBlock().getLocation())) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
if (WorldGuardSupport.isInsideRegion(event.getBlock().getLocation(), worldGuardRegion)) {
|
||||
|
||||
return spleefCompetition;
|
||||
|
||||
@@ -1,15 +1,20 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.battleplugins.arena.BattleArena;
|
||||
import org.battleplugins.arena.command.ArenaCommand;
|
||||
import org.battleplugins.arena.command.ArenaCommandExecutor;
|
||||
import org.battleplugins.arena.command.Argument;
|
||||
import org.battleplugins.arena.competition.map.CompetitionMap;
|
||||
import org.battleplugins.arena.competition.map.options.Bounds;
|
||||
import org.battleplugins.arena.config.ParseException;
|
||||
import org.battleplugins.arena.messages.Messages;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefArena;
|
||||
import org.battleplugins.arena.spleef.arena.SpleefMap;
|
||||
import org.battleplugins.arena.spleef.editor.SpleefEditorWizards;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class SpleefExecutor extends ArenaCommandExecutor {
|
||||
|
||||
public SpleefExecutor(SpleefArena arena) {
|
||||
@@ -37,6 +42,13 @@ public class SpleefExecutor extends ArenaCommandExecutor {
|
||||
|
||||
}
|
||||
|
||||
if (config != null && !config.isRegionAllowedAt(player.getLocation())) {
|
||||
|
||||
SpleefMessages.CREATE_REGION_NOT_WHITELISTED.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (!WorldGuardSupport.isSpleefAllowedAt(player)) {
|
||||
|
||||
SpleefMessages.CREATE_WG_FLAG_REQUIRED.send(player);
|
||||
@@ -178,4 +190,45 @@ public class SpleefExecutor extends ArenaCommandExecutor {
|
||||
|
||||
}
|
||||
|
||||
@ArenaCommand(commands = "worldguardregion", description = "Sets the WorldGuard region for a spleef arena.", permissionNode = "worldguardregion")
|
||||
public void setWorldGuardRegion(Player player, CompetitionMap map, @Argument(name = "region") String region) {
|
||||
|
||||
if (!(map instanceof SpleefMap spleefMap)) {
|
||||
|
||||
return; // Should not happen but just incase
|
||||
|
||||
}
|
||||
|
||||
if (!WorldGuardSupport.isEnabled()) {
|
||||
|
||||
SpleefMessages.CREATE_WG_MISSING.send(player);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (spleefMap.getWorld() == null || !WorldGuardSupport.regionExists(spleefMap.getWorld(), region)) {
|
||||
|
||||
SpleefMessages.WORLDGUARD_REGION_NOT_FOUND.send(player, region);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
spleefMap.setWorldGuardRegion(region);
|
||||
|
||||
try {
|
||||
|
||||
spleefMap.save();
|
||||
|
||||
} catch (ParseException | IOException e) {
|
||||
|
||||
BattleArena.getInstance().error("Failed to save map file for arena {}", this.arena.getName(), e);
|
||||
Messages.MAP_FAILED_TO_SAVE.send(player, spleefMap.getName());
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
SpleefMessages.WORLDGUARD_REGION_SET.send(player, region);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -36,11 +36,19 @@ public final class SpleefMessages {
|
||||
|
||||
public static final Message CREATE_WORLD_NOT_WHITELISTED = Messages.error("spleef-create-world-not-whitelisted",
|
||||
"Spleef arenas cannot be created in this world. World <secondary>{}</secondary> is not in the Spleef world whitelist.");
|
||||
public static final Message CREATE_REGION_NOT_WHITELISTED = Messages.error("spleef-create-region-not-whitelisted",
|
||||
"Spleef arenas cannot be created here. Stand inside a WorldGuard region listed in the Spleef region whitelist.");
|
||||
public static final Message CREATE_WG_FLAG_REQUIRED = Messages.error("spleef-create-wg-flag-required",
|
||||
"You must stand inside a WorldGuard region that has the <secondary>allow-spleef</secondary> flag set to <secondary>allow</secondary> to create a Spleef arena here.");
|
||||
public static final Message CREATE_WG_MISSING = Messages.error("spleef-create-wg-missing",
|
||||
"WorldGuard is not loaded; Spleef arena creation is disabled.");
|
||||
public static final Message WORLD_BLOCKED_AT_RUNTIME = Messages.error("spleef-world-blocked",
|
||||
"Spleef matches are not permitted in world <secondary>{}</secondary>.");
|
||||
public static final Message MATCH_REGION_NOT_WHITELISTED = Messages.error("spleef-match-region-not-whitelisted",
|
||||
"This Spleef map is not inside a whitelisted WorldGuard region.");
|
||||
public static final Message WORLDGUARD_REGION_NOT_FOUND = Messages.error("spleef-worldguard-region-not-found",
|
||||
"WorldGuard region <secondary>{}</secondary> was not found in this map's world.");
|
||||
public static final Message WORLDGUARD_REGION_SET = Messages.success("spleef-worldguard-region-set",
|
||||
"WorldGuard region set to <secondary>{}</secondary>.");
|
||||
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
package org.battleplugins.arena.spleef;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.util.NumberConversions;
|
||||
|
||||
public final class SpleefUtil {
|
||||
|
||||
public static Block getBlockUnderPlayer(int y, Location location) {
|
||||
|
||||
Location loc = location.clone();
|
||||
loc.setY(y);
|
||||
|
||||
double boxWidth = 0.3;
|
||||
Block block1 = getBlock(loc, +boxWidth, -boxWidth);
|
||||
if (!block1.getType().isAir()) {
|
||||
|
||||
return block1;
|
||||
|
||||
}
|
||||
|
||||
Block block2 = getBlock(loc, -boxWidth, +boxWidth);
|
||||
if (!block2.getType().isAir()) {
|
||||
|
||||
return block2;
|
||||
|
||||
}
|
||||
|
||||
Block block3 = getBlock(loc, +boxWidth, +boxWidth);
|
||||
if (!block3.getType().isAir()) {
|
||||
|
||||
return block3;
|
||||
|
||||
}
|
||||
|
||||
Block block4 = getBlock(loc, -boxWidth, -boxWidth);
|
||||
if (!block4.getType().isAir()) {
|
||||
|
||||
return block4;
|
||||
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
private static Block getBlock(Location loc, double x, double z) {
|
||||
|
||||
return loc.getWorld().getBlockAt(NumberConversions.floor(loc.getX() + x), loc.getBlockY(),
|
||||
NumberConversions.floor(loc.getZ() + z));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,8 +10,11 @@ import com.sk89q.worldguard.protection.flags.StateFlag;
|
||||
import com.sk89q.worldguard.protection.managers.RegionManager;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public final class WorldGuardSupport {
|
||||
|
||||
private WorldGuardSupport() {
|
||||
@@ -26,6 +29,12 @@ public final class WorldGuardSupport {
|
||||
|
||||
public static boolean isInsideRegion(Location location, String regionId) {
|
||||
|
||||
if (location.getWorld() == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(location.getWorld()));
|
||||
if (regionManager == null) {
|
||||
@@ -40,6 +49,43 @@ public final class WorldGuardSupport {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isInsideAnyRegion(Location location, List<String> regionIds) {
|
||||
|
||||
if (location == null || location.getWorld() == null || regionIds == null || regionIds.isEmpty()) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(location.getWorld()));
|
||||
if (regionManager == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
BlockVector3 position = BlockVector3.at(location.getBlockX(), location.getBlockY(), location.getBlockZ());
|
||||
ApplicableRegionSet regions = regionManager.getApplicableRegions(position);
|
||||
return regions.getRegions().stream().anyMatch(region -> regionIds.stream()
|
||||
.anyMatch(allowed -> allowed != null && region.getId().equalsIgnoreCase(allowed)));
|
||||
|
||||
}
|
||||
|
||||
public static boolean regionExists(World world, String regionId) {
|
||||
|
||||
RegionManager regionManager = WorldGuard.getInstance().getPlatform().getRegionContainer()
|
||||
.get(BukkitAdapter.adapt(world));
|
||||
if (regionManager == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return regionManager.getRegion(regionId) != null;
|
||||
|
||||
}
|
||||
|
||||
public static boolean isSpleefAllowedAt(Player player) {
|
||||
|
||||
StateFlag flag = SpleefFlags.getAllowSpleefFlag();
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
import com.destroystokyo.paper.event.entity.ThrownEggHatchEvent;
|
||||
import io.papermc.paper.math.BlockPosition;
|
||||
import org.battleplugins.arena.Arena;
|
||||
import org.battleplugins.arena.ArenaPlayer;
|
||||
@@ -9,43 +8,33 @@ import org.battleplugins.arena.competition.map.MapFactory;
|
||||
import org.battleplugins.arena.competition.phase.CompetitionPhaseType;
|
||||
import org.battleplugins.arena.config.ArenaOption;
|
||||
import org.battleplugins.arena.event.ArenaEventHandler;
|
||||
import org.battleplugins.arena.event.arena.ArenaPhaseCompleteEvent;
|
||||
import org.battleplugins.arena.event.arena.ArenaPhaseStartEvent;
|
||||
import org.battleplugins.arena.event.player.ArenaLeaveEvent;
|
||||
import org.battleplugins.arena.options.ArenaOptionType;
|
||||
import org.battleplugins.arena.options.types.BooleanArenaOption;
|
||||
import org.battleplugins.arena.spleef.ArenaSpleef;
|
||||
import org.battleplugins.arena.spleef.SpleefEventResolvers;
|
||||
import org.battleplugins.arena.spleef.SpleefExecutor;
|
||||
import org.battleplugins.arena.spleef.SpleefUtil;
|
||||
import org.battleplugins.arena.spleef.SpleefMessages;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.entity.Egg;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.block.data.type.Snow;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.entity.TNTPrimed;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.block.Action;
|
||||
import org.bukkit.event.block.BlockBreakEvent;
|
||||
import org.bukkit.event.block.TNTPrimeEvent;
|
||||
import org.bukkit.event.entity.ProjectileHitEvent;
|
||||
import org.bukkit.event.player.PlayerInteractEvent;
|
||||
import org.bukkit.event.player.PlayerMoveEvent;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.bukkit.inventory.meta.ItemMeta;
|
||||
import org.bukkit.metadata.FixedMetadataValue;
|
||||
import org.bukkit.persistence.PersistentDataType;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SpleefArena extends Arena {
|
||||
|
||||
@ArenaOption(name = "layer-decay-delay", description = "The delay before each layer decays.")
|
||||
private Duration layerDecayDelay = Duration.ofMinutes(2);
|
||||
|
||||
@ArenaOption(name = "layer-decay-time", description = "The time it takes to decay and entire layer.")
|
||||
private Duration layerDecayTime = Duration.ofMinutes(1);
|
||||
|
||||
@ArenaOption(name = "game", description = "The spleef game.")
|
||||
private SpleefGame game = SpleefGame.CLASSIC;
|
||||
|
||||
@@ -54,7 +43,6 @@ public class SpleefArena extends Arena {
|
||||
super();
|
||||
|
||||
this.getEventManager().registerArenaResolver(ProjectileHitEvent.class, SpleefEventResolvers.PROJECTILE_HIT);
|
||||
this.getEventManager().registerArenaResolver(ThrownEggHatchEvent.class, SpleefEventResolvers.THROWN_EGG_HATCH);
|
||||
this.getEventManager().registerArenaResolver(TNTPrimeEvent.class, SpleefEventResolvers.TNT_PRIME.apply(this));
|
||||
|
||||
}
|
||||
@@ -91,28 +79,20 @@ public class SpleefArena extends Arena {
|
||||
ArenaSpleef.getInstance().getSLF4JLogger().warn(
|
||||
"Refusing to start Spleef competition '{}' — world '{}' is not in the Spleef world whitelist.",
|
||||
spleefCompetition.getMap().getName(), worldName);
|
||||
this.leaveWorldBlockedCompetition(spleefCompetition, worldName);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
spleefCompetition.beginLayerDecay();
|
||||
if (!spleefCompetition.isMatchRegionAllowed()) {
|
||||
|
||||
}
|
||||
ArenaSpleef.getInstance().getSLF4JLogger().warn(
|
||||
"Refusing to start Spleef competition '{}' — map is not inside a whitelisted WorldGuard region.",
|
||||
spleefCompetition.getMap().getName());
|
||||
this.leaveRegionBlockedCompetition(spleefCompetition);
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler
|
||||
public void onPhaseComplete(ArenaPhaseCompleteEvent event) {
|
||||
|
||||
if (!CompetitionPhaseType.INGAME.equals(event.getPhase().getType())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (event.getCompetition() instanceof SpleefCompetition spleefCompetition) {
|
||||
|
||||
spleefCompetition.stopLayerDecay();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -141,7 +121,16 @@ public class SpleefArena extends Arena {
|
||||
SpleefMap.Layer layer = spleefMap.getLayer(pos);
|
||||
if (layer != null) {
|
||||
|
||||
if (!(player.getCompetition() instanceof SpleefCompetition competition)
|
||||
|| !competition.isLocationAllowed(event.getBlock().getLocation()))
|
||||
{
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(false);
|
||||
this.giveSnowballsFromFloor(event);
|
||||
|
||||
}
|
||||
|
||||
@@ -163,79 +152,9 @@ public class SpleefArena extends Arena {
|
||||
if (spleefMap.getDeathRegion() != null && spleefMap.getDeathRegion().isInside(event.getTo())) {
|
||||
|
||||
event.getPlayer().damage(10000.0D); // Kill the player
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// Ensure that the game is the decay spleef game
|
||||
if (this.game != SpleefGame.DECAY) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
// If a player changes blocks and the block below is of a
|
||||
// spleef layer, then decay the layer
|
||||
Block block = SpleefUtil.getBlockUnderPlayer(event.getTo().getBlockY() - 1, event.getTo());
|
||||
if (block == null || block.getType().isAir()) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
BlockPosition pos = block.getLocation().toBlock();
|
||||
SpleefMap.Layer layer = spleefMap.getLayer(pos);
|
||||
if (layer == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
competition.decayBlock(pos);
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler(priority = EventPriority.LOW)
|
||||
public void onInteract(PlayerInteractEvent event) {
|
||||
|
||||
ItemStack item = event.getItem();
|
||||
if (item == null || item.getItemMeta() == null) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (this.game != SpleefGame.SPLEGG) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
if (event.getAction() != Action.RIGHT_CLICK_AIR && event.getAction() != Action.RIGHT_CLICK_BLOCK) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
ItemMeta meta = item.getItemMeta();
|
||||
if (!meta.getPersistentDataContainer().has(ArenaSpleef.getInstance().getSpleefItemKey(),
|
||||
PersistentDataType.BOOLEAN))
|
||||
{
|
||||
|
||||
return; // Not a spleef item, do not fire egg
|
||||
|
||||
}
|
||||
|
||||
event.setCancelled(true);
|
||||
|
||||
// Shoot an egg
|
||||
event.getPlayer().launchProjectile(Egg.class, null, egg -> {
|
||||
|
||||
egg.setMetadata("splegg", new FixedMetadataValue(ArenaSpleef.getInstance(), true));
|
||||
event.getPlayer().playSound(event.getPlayer().getLocation(), Sound.ENTITY_EGG_THROW, 1, 1);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// For the events below:
|
||||
@@ -257,21 +176,13 @@ public class SpleefArena extends Arena {
|
||||
|
||||
}
|
||||
|
||||
block.setType(Material.AIR);
|
||||
|
||||
}
|
||||
|
||||
@ArenaEventHandler(priority = EventPriority.LOW)
|
||||
public void onEggHatch(ThrownEggHatchEvent event) {
|
||||
|
||||
Egg egg = event.getEgg();
|
||||
if (!egg.hasMetadata("splegg")) {
|
||||
if (!competition.isLocationAllowed(block.getLocation())) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setHatching(false);
|
||||
block.setType(Material.AIR);
|
||||
|
||||
}
|
||||
|
||||
@@ -301,15 +212,68 @@ public class SpleefArena extends Arena {
|
||||
|
||||
}
|
||||
|
||||
public Duration getLayerDecayDelay() {
|
||||
private void giveSnowballsFromFloor(BlockBreakEvent event) {
|
||||
|
||||
return this.layerDecayDelay;
|
||||
int amount = this.getSnowballDropCount(event.getBlock());
|
||||
if (amount <= 0) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
event.setDropItems(false);
|
||||
event.getPlayer().getInventory().addItem(new ItemStack(Material.SNOWBALL, amount));
|
||||
|
||||
}
|
||||
|
||||
public Duration getLayerDecayTime() {
|
||||
private int getSnowballDropCount(Block block) {
|
||||
|
||||
return this.layerDecayTime;
|
||||
if (block.getType() == Material.SNOW_BLOCK) {
|
||||
|
||||
return 4;
|
||||
|
||||
}
|
||||
|
||||
if (block.getType() != Material.SNOW) {
|
||||
|
||||
return 0;
|
||||
|
||||
}
|
||||
|
||||
BlockData blockData = block.getBlockData();
|
||||
if (blockData instanceof Snow snow) {
|
||||
|
||||
return snow.getLayers();
|
||||
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
private void leaveWorldBlockedCompetition(SpleefCompetition competition, String worldName) {
|
||||
|
||||
List<ArenaPlayer> players = new ArrayList<>(competition.getPlayers());
|
||||
players.addAll(competition.getSpectators());
|
||||
for (ArenaPlayer player : players) {
|
||||
|
||||
SpleefMessages.WORLD_BLOCKED_AT_RUNTIME.send(player.getPlayer(), worldName);
|
||||
competition.leave(player, ArenaLeaveEvent.Cause.PLUGIN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void leaveRegionBlockedCompetition(SpleefCompetition competition) {
|
||||
|
||||
List<ArenaPlayer> players = new ArrayList<>(competition.getPlayers());
|
||||
players.addAll(competition.getSpectators());
|
||||
for (ArenaPlayer player : players) {
|
||||
|
||||
SpleefMessages.MATCH_REGION_NOT_WHITELISTED.send(player.getPlayer());
|
||||
competition.leave(player, ArenaLeaveEvent.Cause.PLUGIN);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,117 +1,89 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
import io.papermc.paper.math.Position;
|
||||
import org.battleplugins.arena.competition.CompetitionType;
|
||||
import org.battleplugins.arena.competition.JoinResult;
|
||||
import org.battleplugins.arena.competition.LiveCompetition;
|
||||
import org.battleplugins.arena.competition.PlayerRole;
|
||||
import org.battleplugins.arena.competition.map.options.Bounds;
|
||||
import org.battleplugins.arena.spleef.ArenaSpleef;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Material;
|
||||
import org.battleplugins.arena.spleef.SpleefConfig;
|
||||
import org.battleplugins.arena.spleef.SpleefMessages;
|
||||
import org.battleplugins.arena.spleef.WorldGuardSupport;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
public class SpleefCompetition extends LiveCompetition<SpleefCompetition> {
|
||||
|
||||
private final SpleefArena arena;
|
||||
private final SpleefMap map;
|
||||
private static final JoinResult REGION_NOT_JOINABLE = new JoinResult(false,
|
||||
SpleefMessages.MATCH_REGION_NOT_WHITELISTED);
|
||||
|
||||
private final List<BukkitTask> decayTasks = new ArrayList<>();
|
||||
private final List<Position> pendingDecays = new ArrayList<>();
|
||||
private final SpleefMap map;
|
||||
|
||||
public SpleefCompetition(SpleefArena arena, CompetitionType type, SpleefMap map) {
|
||||
|
||||
super(arena, type, map);
|
||||
|
||||
this.arena = arena;
|
||||
this.map = map;
|
||||
|
||||
}
|
||||
|
||||
public void beginLayerDecay() {
|
||||
@Override
|
||||
public CompletableFuture<JoinResult> canJoin(Collection<Player> players, PlayerRole role) {
|
||||
|
||||
List<SpleefMap.Layer> layers = new ArrayList<>(this.map.getLayers());
|
||||
layers.sort((layer1, layer2) -> layer2.getBounds().getMaxY() - layer1.getBounds().getMaxY());
|
||||
if (!this.isMatchRegionAllowed()) {
|
||||
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
|
||||
SpleefMap.Layer layer = layers.get(i);
|
||||
|
||||
long delay = (i + 1) * this.arena.getLayerDecayDelay().toSeconds() * 20;
|
||||
BukkitTask decayTask = Bukkit.getScheduler().runTaskLater(ArenaSpleef.getInstance(), () -> {
|
||||
|
||||
Duration layerDecayTime = this.arena.getLayerDecayTime();
|
||||
List<Block> blocks = new ArrayList<>(layer.getBounds().getVolume());
|
||||
|
||||
for (int x = layer.getBounds().getMinX(); x <= layer.getBounds().getMaxX(); x++) {
|
||||
|
||||
for (int y = layer.getBounds().getMinY(); y <= layer.getBounds().getMaxY(); y++) {
|
||||
|
||||
for (int z = layer.getBounds().getMinZ(); z <= layer.getBounds().getMaxZ(); z++) {
|
||||
|
||||
blocks.add(this.map.getWorld().getBlockAt(x, y, z));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (blocks.isEmpty()) {
|
||||
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Collections.shuffle(blocks);
|
||||
|
||||
long totalDecayTimeTicks = layerDecayTime.getSeconds() * 20;
|
||||
long intervalBetweenDecayTicks = Math.max(1L, totalDecayTimeTicks / blocks.size());
|
||||
|
||||
this.decayTasks.add(new BukkitRunnable() {
|
||||
|
||||
private int index = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
if (index >= blocks.size()) {
|
||||
|
||||
this.cancel();
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
Block block = blocks.get(index);
|
||||
block.setType(Material.AIR);
|
||||
|
||||
this.index++;
|
||||
|
||||
}
|
||||
|
||||
}.runTaskTimer(ArenaSpleef.getInstance(), 0, intervalBetweenDecayTicks));
|
||||
|
||||
}, delay);
|
||||
|
||||
this.decayTasks.add(decayTask);
|
||||
return CompletableFuture.completedFuture(REGION_NOT_JOINABLE);
|
||||
|
||||
}
|
||||
|
||||
return super.canJoin(players, role);
|
||||
|
||||
}
|
||||
|
||||
public void stopLayerDecay() {
|
||||
public boolean isMatchRegionAllowed() {
|
||||
|
||||
for (BukkitTask decayTask : this.decayTasks) {
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
if (config == null || !config.hasRegionWhitelist()) {
|
||||
|
||||
decayTask.cancel();
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
this.decayTasks.clear();
|
||||
World world = this.map.getWorld();
|
||||
if (world == null) {
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
String worldGuardRegion = this.map.getWorldGuardRegion();
|
||||
if (config.isRegionAllowed(worldGuardRegion) && WorldGuardSupport.regionExists(world, worldGuardRegion)) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
List<SpleefMap.Layer> layers = this.map.getLayers();
|
||||
if (!layers.isEmpty()) {
|
||||
|
||||
return layers.stream().allMatch(layer -> this.isBoundsInAllowedRegion(layer.getBounds()));
|
||||
|
||||
}
|
||||
|
||||
return this.map.bounds().map(this::isBoundsInAllowedRegion).orElse(false);
|
||||
|
||||
}
|
||||
|
||||
public boolean isLocationAllowed(Location location) {
|
||||
|
||||
SpleefConfig config = ArenaSpleef.getInstance().getMainConfig();
|
||||
return config == null || config.isRegionAllowedAt(location);
|
||||
|
||||
}
|
||||
|
||||
@@ -127,6 +99,12 @@ public class SpleefCompetition extends LiveCompetition<SpleefCompetition> {
|
||||
for (int z = layer.getBounds().getMinZ(); z <= layer.getBounds().getMaxZ(); z++) {
|
||||
|
||||
Block block = this.map.getWorld().getBlockAt(x, y, z);
|
||||
if (!this.isLocationAllowed(block.getLocation())) {
|
||||
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
block.setBlockData(layer.getBlockData());
|
||||
|
||||
}
|
||||
@@ -139,24 +117,23 @@ public class SpleefCompetition extends LiveCompetition<SpleefCompetition> {
|
||||
|
||||
}
|
||||
|
||||
public void decayBlock(Position position) {
|
||||
private boolean isBoundsInAllowedRegion(Bounds bounds) {
|
||||
|
||||
if (this.pendingDecays.contains(position)) {
|
||||
World world = this.map.getWorld();
|
||||
if (world == null) {
|
||||
|
||||
return;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.pendingDecays.add(position);
|
||||
|
||||
Bukkit.getScheduler().runTaskLater(ArenaSpleef.getInstance(), () -> {
|
||||
|
||||
Block block = this.map.getWorld().getBlockAt(position.blockX(), position.blockY(), position.blockZ());
|
||||
block.setType(Material.AIR);
|
||||
|
||||
this.pendingDecays.remove(position);
|
||||
|
||||
}, 5);
|
||||
return this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMinY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMinY(), bounds.getMaxZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMaxY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMinX(), bounds.getMaxY(), bounds.getMaxZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMinY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMinY(), bounds.getMaxZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMaxY(), bounds.getMinZ()))
|
||||
&& this.isLocationAllowed(new Location(world, bounds.getMaxX(), bounds.getMaxY(), bounds.getMaxZ()));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
package org.battleplugins.arena.spleef.arena;
|
||||
|
||||
public enum SpleefGame {
|
||||
CLASSIC, SPLEGG, DECAY, BOW_SPLEEF
|
||||
CLASSIC, BOW_SPLEEF
|
||||
}
|
||||
@@ -28,7 +28,7 @@ public class SpleefMap extends LiveCompetitionMap {
|
||||
@ArenaOption(name = "death-region", description = "The region where players will die if they fall into.")
|
||||
private Bounds deathRegion;
|
||||
|
||||
@ArenaOption(name = "worldguard-region", description = "The WorldGuard region ID used for Bow Spleef TNT.")
|
||||
@ArenaOption(name = "worldguard-region", description = "The WorldGuard region ID used for match region checks and Bow Spleef TNT.")
|
||||
private String worldGuardRegion;
|
||||
|
||||
private final Map<Position, Layer> positionToLayers = new HashMap<>();
|
||||
@@ -194,6 +194,12 @@ public class SpleefMap extends LiveCompetitionMap {
|
||||
|
||||
}
|
||||
|
||||
public void setWorldGuardRegion(String worldGuardRegion) {
|
||||
|
||||
this.worldGuardRegion = worldGuardRegion;
|
||||
|
||||
}
|
||||
|
||||
public static class Layer {
|
||||
|
||||
@ArenaOption(name = "bounds", description = "The bounds of this layer.", required = true)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Spleef
|
||||
type: Match
|
||||
# Optional. If config.yml region-whitelist is set, use a whitelisted region ID here.
|
||||
# worldguard-region: spleef_classic
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1
|
||||
@@ -14,8 +16,6 @@ victory-conditions:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
layer-decay-delay: 2m
|
||||
layer-decay-time: 1m
|
||||
game: classic
|
||||
events:
|
||||
on-join:
|
||||
@@ -115,4 +115,4 @@ phases:
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
@@ -7,16 +7,16 @@
|
||||
# -----------------
|
||||
config-version: 2.0 # The config version, do not change!
|
||||
|
||||
# Whether projectiles (i.e. snowballs) should break blocks or not.
|
||||
projectiles-break-blocks: false
|
||||
|
||||
# Worlds where Spleef arenas may be created and where matches may run.
|
||||
# Leave empty to allow every world. Names are matched case-insensitively.
|
||||
world-whitelist: []
|
||||
world-whitelist:
|
||||
- world
|
||||
|
||||
# The shovels to use for Spleef.
|
||||
# WorldGuard regions where Spleef arenas may be created and matches may modify blocks.
|
||||
# Leave empty to allow any region in an allowed world.
|
||||
region-whitelist: []
|
||||
|
||||
# The tools to use for Spleef.
|
||||
shovels:
|
||||
classic: diamond_shovel{display-name=<yellow>Spleef Shovel}
|
||||
super-shovel: golden_shovel{display-name=<aqua><bold>Super Spleef Shovel;enchantments=[unbreaking:10,efficiency:5]}
|
||||
egg-cannon: netherite_shovel{display-name=<gold><bold>Egg Cannon;enchantments=[unbreaking:10,efficiency:5];item-flags=[hide_enchants]}
|
||||
bow: bow{display-name=<red><bold>Fire Bow;enchantments=[unbreaking:10,infinity:1,flame:1]}
|
||||
@@ -1,7 +1,7 @@
|
||||
name: Spleef-OG
|
||||
main: org.battleplugins.arena.spleef.ArenaSpleef
|
||||
version: ${version}
|
||||
description: A Spleef mode for BattleArena.
|
||||
description: Classic and Bow Spleef modes for BattleArena WorldGuard regions.
|
||||
author: BattlePlugins
|
||||
website: https://battleplugins.org
|
||||
api-version: ${apiVersion}
|
||||
|
||||
@@ -15,10 +15,8 @@ victory-conditions:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
layer-decay-delay: 2m
|
||||
layer-decay-time: 1m
|
||||
game: bow_spleef
|
||||
# Required for Bow Spleef TNT handling.
|
||||
# Required for Bow Spleef TNT handling. If config.yml region-whitelist is set, use a whitelisted region ID here.
|
||||
# worldguard-region: bowspleef
|
||||
events:
|
||||
on-join:
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
name: Spleef
|
||||
type: Match
|
||||
# Optional. If config.yml region-whitelist is set, use a whitelisted region ID here.
|
||||
# worldguard-region: spleef_classic
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1
|
||||
@@ -14,8 +16,6 @@ victory-conditions:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
layer-decay-delay: 2m
|
||||
layer-decay-time: 1m
|
||||
game: classic
|
||||
events:
|
||||
on-join:
|
||||
@@ -115,4 +115,4 @@ phases:
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
@@ -1,102 +0,0 @@
|
||||
name: DecaySpleef
|
||||
mode: Spleef
|
||||
type: Match
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1
|
||||
team-amount: 2+
|
||||
team-selection: none
|
||||
modules:
|
||||
- scoreboards
|
||||
lives:
|
||||
enabled: false
|
||||
victory-conditions:
|
||||
teams-alive:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
layer-decay-delay: 2m
|
||||
layer-decay-time: 1m
|
||||
game: decay
|
||||
events:
|
||||
on-join:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- flight{enabled=false}
|
||||
- teleport{location=waitroom}
|
||||
on-spectate:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=spectator}
|
||||
- flight{enabled=true}
|
||||
- teleport{location=spectator}
|
||||
on-leave:
|
||||
- clear-effects
|
||||
- restore{types=all}
|
||||
- remove-scoreboard
|
||||
on-death:
|
||||
- clear-inventory
|
||||
- respawn
|
||||
- delay{ticks=1}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- teleport{location=waitroom}
|
||||
options:
|
||||
- block-break{enabled=false}
|
||||
- block-place{enabled=false}
|
||||
- block-drops{enabled=false}
|
||||
- block-interact{enabled=false}
|
||||
- item-drops{enabled=false}
|
||||
- keep-inventory{enabled=true}
|
||||
- keep-experience{enabled=true}
|
||||
- damage-entities{option=never}
|
||||
- damage-players{option=never}
|
||||
initial-phase: waiting
|
||||
phases:
|
||||
waiting:
|
||||
allow-join: true
|
||||
next-phase: countdown
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
countdown:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: ingame
|
||||
countdown-time: 5s
|
||||
events:
|
||||
on-start:
|
||||
- paste-layers
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-complete:
|
||||
- teleport{location=team_spawn}
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
ingame:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
next-phase: victory
|
||||
options:
|
||||
- block-interact{enabled=true}
|
||||
events:
|
||||
on-start:
|
||||
- change-gamemode{gamemode=survival}
|
||||
- apply-scoreboard{scoreboard=ingame-list-alive}
|
||||
victory:
|
||||
allow-join: false
|
||||
allow-spectate: false
|
||||
next-phase: waiting
|
||||
duration: 5s
|
||||
events:
|
||||
on-complete:
|
||||
- leave
|
||||
- remove-scoreboard
|
||||
on-victory:
|
||||
- send-message{message=<green>Congrats, you won!</green>}
|
||||
- play-sound{sound=entity.player.levelup;pitch=1;volume=1}
|
||||
on-lose:
|
||||
- send-message{message=<red>Sorry, you lost!</red>}
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
@@ -1,103 +0,0 @@
|
||||
name: Splegg
|
||||
mode: Spleef
|
||||
type: Match
|
||||
team-options:
|
||||
named-teams: false
|
||||
team-size: 1+
|
||||
team-amount: 2
|
||||
team-selection: none
|
||||
modules:
|
||||
- scoreboards
|
||||
lives:
|
||||
enabled: false
|
||||
victory-conditions:
|
||||
teams-alive:
|
||||
amount: 1
|
||||
time-limit:
|
||||
time-limit: 5m
|
||||
layer-decay-delay: 2m
|
||||
layer-decay-time: 1m
|
||||
game: splegg
|
||||
events:
|
||||
on-join:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- flight{enabled=false}
|
||||
- teleport{location=waitroom}
|
||||
on-spectate:
|
||||
- store{types=all}
|
||||
- change-gamemode{gamemode=spectator}
|
||||
- flight{enabled=true}
|
||||
- teleport{location=spectator}
|
||||
on-leave:
|
||||
- clear-effects
|
||||
- restore{types=all}
|
||||
- remove-scoreboard
|
||||
on-death:
|
||||
- clear-inventory
|
||||
- respawn
|
||||
- delay{ticks=1}
|
||||
- change-gamemode{gamemode=adventure}
|
||||
- teleport{location=waitroom}
|
||||
options:
|
||||
- block-break{enabled=false}
|
||||
- block-place{enabled=false}
|
||||
- block-drops{enabled=false}
|
||||
- block-interact{enabled=false}
|
||||
- item-drops{enabled=false}
|
||||
- keep-inventory{enabled=true}
|
||||
- keep-experience{enabled=true}
|
||||
- damage-entities{option=never}
|
||||
- damage-players{option=never}
|
||||
initial-phase: waiting
|
||||
phases:
|
||||
waiting:
|
||||
allow-join: true
|
||||
next-phase: countdown
|
||||
events:
|
||||
on-start:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
on-join:
|
||||
- apply-scoreboard{scoreboard=waiting}
|
||||
countdown:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
revert-phase: true
|
||||
next-phase: ingame
|
||||
countdown-time: 5s
|
||||
events:
|
||||
on-start:
|
||||
- paste-layers
|
||||
- apply-scoreboard{scoreboard=countdown}
|
||||
on-complete:
|
||||
- teleport{location=team_spawn}
|
||||
- play-sound{sound=block.note_block.pling;pitch=2;volume=1}
|
||||
ingame:
|
||||
allow-join: false
|
||||
allow-spectate: true
|
||||
next-phase: victory
|
||||
options:
|
||||
- block-interact{enabled=true}
|
||||
events:
|
||||
on-start:
|
||||
- change-gamemode{gamemode=survival}
|
||||
- give-shovel{shovel=egg-cannon}
|
||||
- apply-scoreboard{scoreboard=ingame-list-alive}
|
||||
victory:
|
||||
allow-join: false
|
||||
allow-spectate: false
|
||||
next-phase: waiting
|
||||
duration: 5s
|
||||
events:
|
||||
on-complete:
|
||||
- leave
|
||||
- remove-scoreboard
|
||||
on-victory:
|
||||
- send-message{message=<green>Congrats, you won!</green>}
|
||||
- play-sound{sound=entity.player.levelup;pitch=1;volume=1}
|
||||
on-lose:
|
||||
- send-message{message=<red>Sorry, you lost!</red>}
|
||||
- play-sound{sound=block.anvil.place;pitch=0;volume=1}
|
||||
on-draw:
|
||||
- send-message{message=<yellow>It's a draw!</yellow>}
|
||||
- play-sound{sound=block.beacon.deactivate;pitch=0;volume=1}
|
||||
Reference in new issue
Block a user