Implements #37 and improves material tab-completion

This commit is contained in:
Kristian Knarvik 2024-05-16 13:14:33 +02:00
parent bfc0eb7334
commit d101c7ed02
23 changed files with 486 additions and 76 deletions

10
pom.xml
View File

@ -73,12 +73,6 @@
<include>org/jetbrains/annotations/**</include>
</includes>
</filter>
<filter>
<excludes>
<exclude>*.MF</exclude>
<exclude>*.yml</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
@ -120,7 +114,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.20-R0.1-SNAPSHOT</version>
<version>1.20.6-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>
@ -144,7 +138,7 @@
<dependency>
<groupId>net.knarcraft</groupId>
<artifactId>knarlib</artifactId>
<version>1.2.5</version>
<version>1.2.7</version>
<scope>compile</scope>
</dependency>
<dependency>

View File

@ -5,6 +5,7 @@ import net.knarcraft.minigames.arena.reward.RewardCondition;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.entity.EntityDamageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -182,4 +183,40 @@ public interface Arena {
*/
boolean setMaxPlayers(int newValue);
/**
* Gets the damage causes that won't be blocked for this arena
*
* <p>Receiving any damage of this type will be allowed, but if the damage is fatal, a loss will be triggered.</p>
*
* @return <p>The damage causes that won't be blocked</p>
*/
@NotNull
Set<EntityDamageEvent.DamageCause> getAllowedDamageCauses();
/**
* Sets the damage causes that will trigger a loss for this arena
*
* <p>Receiving any damage of this type will immediately cause a loss to the player.</p>
*
* @return <p>The damage causes that will trigger a loss</p>
*/
@NotNull
Set<EntityDamageEvent.DamageCause> getLossTriggerDamageCauses();
/**
* Sets the damage causes that are allowed for this arena
*
* @param causes <p>The allowed damage causes</p>
*/
@SuppressWarnings("SameReturnValue")
boolean setAllowedDamageCauses(@NotNull Set<EntityDamageEvent.DamageCause> causes);
/**
* Sets the damage causes that will trigger a loss for this arena
*
* @param causes <p>The causes that cause a loss</p>
*/
@SuppressWarnings("SameReturnValue")
boolean setLossTriggerDamageCauses(@NotNull Set<EntityDamageEvent.DamageCause> causes);
}

View File

@ -50,4 +50,9 @@ public enum EditablePropertyType {
*/
INTEGER,
/**
* The property is a comma-separated list of damage causes
*/
DAMAGE_CAUSE_LIST,
}

View File

@ -0,0 +1,17 @@
package net.knarcraft.minigames.arena;
import org.jetbrains.annotations.NotNull;
/**
* A representation of each key used for storing arena data
*/
public interface StorageKey {
/**
* Gets the configuration key this enum represents
*
* @return <p>The string key representation.</p>
*/
@NotNull String getKey();
}

View File

@ -8,10 +8,12 @@ import net.knarcraft.minigames.arena.reward.Reward;
import net.knarcraft.minigames.arena.reward.RewardCondition;
import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.util.DropperArenaStorageHelper;
import net.knarcraft.minigames.util.InputValidationHelper;
import net.knarcraft.minigames.util.StringSanitizer;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.event.entity.EntityDamageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -68,6 +70,16 @@ public class DropperArena implements Arena {
*/
private int maxPlayers = -1;
/**
* Types of damage that won't be blocked in this arena
*/
private Set<EntityDamageEvent.DamageCause> allowedDamageCauses;
/**
* Types of damage that will trigger a loss in this arena
*/
private Set<EntityDamageEvent.DamageCause> lossTriggerDamageCauses;
/**
* The material of the block players have to hit to win this dropper arena
*/
@ -98,12 +110,16 @@ public class DropperArena implements Arena {
* @param rewards <p>The rewards given by this 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>
* @param allowedDamageCauses <p>The damage causes to not cancel. If the player received fatal damage, a loss is triggered.</p>
* @param lossTriggerDamageCauses <p>The damage causes that will trigger a loss (for arrow traps and similar)</p>
*/
public DropperArena(@NotNull UUID arenaId, @NotNull String arenaName, @NotNull Location spawnLocation,
@Nullable Location exitLocation, double playerVerticalVelocity, float playerHorizontalVelocity,
@NotNull Material winBlockType, int maxPlayers,
@NotNull Map<RewardCondition, Set<Reward>> rewards, @NotNull DropperArenaData dropperArenaData,
@NotNull DropperArenaHandler arenaHandler) {
@NotNull DropperArenaHandler arenaHandler,
@Nullable Set<String> allowedDamageCauses,
@Nullable Set<String> lossTriggerDamageCauses) {
this.arenaId = arenaId;
this.arenaName = arenaName;
this.spawnLocation = spawnLocation;
@ -115,6 +131,8 @@ public class DropperArena implements Arena {
this.dropperArenaHandler = arenaHandler;
this.rewards = rewards;
this.maxPlayers = maxPlayers;
this.allowedDamageCauses = InputValidationHelper.parseDamageCauses(allowedDamageCauses);
this.lossTriggerDamageCauses = InputValidationHelper.parseDamageCauses(lossTriggerDamageCauses);
}
/**
@ -145,6 +163,8 @@ public class DropperArena implements Arena {
this.dropperArenaData = new DropperArenaData(this.arenaId, recordRegistries, new HashMap<>());
this.winBlockType = Material.WATER;
this.dropperArenaHandler = arenaHandler;
this.allowedDamageCauses = new HashSet<>();
this.lossTriggerDamageCauses = new HashSet<>();
}
@Override
@ -201,11 +221,37 @@ public class DropperArena implements Arena {
@Override
public boolean setMaxPlayers(int newValue) {
if (newValue < -1) {
return false;
}
this.maxPlayers = newValue;
this.saveArena();
return true;
}
@Override
public @NotNull Set<EntityDamageEvent.DamageCause> getAllowedDamageCauses() {
return this.allowedDamageCauses;
}
@Override
public @NotNull Set<EntityDamageEvent.DamageCause> getLossTriggerDamageCauses() {
return this.lossTriggerDamageCauses;
}
@Override
public boolean setAllowedDamageCauses(@NotNull Set<EntityDamageEvent.DamageCause> causes) {
this.allowedDamageCauses = causes;
return true;
}
@Override
public boolean setLossTriggerDamageCauses(@NotNull Set<EntityDamageEvent.DamageCause> causes) {
this.lossTriggerDamageCauses = causes;
return true;
}
@Override
@NotNull
public Material getWinBlockType() {

View File

@ -1,6 +1,7 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.EditablePropertyType;
import net.knarcraft.minigames.util.ArenaStorageHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -51,6 +52,20 @@ public enum DropperArenaEditableProperty {
*/
MAX_PLAYERS("maxPlayers", (arena) -> String.valueOf(arena.getMaxPlayers()),
EditablePropertyType.INTEGER),
/**
* The arena's allowed damage causes
*/
ALLOWED_DAMAGE_CAUSES("allowedDamageCauses", (arena) -> String.valueOf(
ArenaStorageHelper.getDamageCauseNames(arena.getAllowedDamageCauses())),
EditablePropertyType.DAMAGE_CAUSE_LIST),
/**
* The arena's loss trigger damage causes
*/
LOSS_TRIGGER_DAMAGE_CAUSES("lossTriggerDamageCauses", (arena) -> String.valueOf(
ArenaStorageHelper.getDamageCauseNames(arena.getLossTriggerDamageCauses())),
EditablePropertyType.DAMAGE_CAUSE_LIST),
;
private final @NotNull String argumentString;

View File

@ -1,11 +1,12 @@
package net.knarcraft.minigames.arena.dropper;
import net.knarcraft.minigames.arena.StorageKey;
import org.jetbrains.annotations.NotNull;
/**
* A representation of each key used for storing arena data
*/
public enum DropperArenaStorageKey {
public enum DropperArenaStorageKey implements StorageKey {
/**
* The key for an arena's id
@ -56,6 +57,16 @@ public enum DropperArenaStorageKey {
* The key for this arena's maximum players
*/
MAX_PLAYERS("maxPlayers"),
/**
* The key for this arena's allowed damage causes
*/
ALLOWED_DAMAGE_CAUSES("allowedDamageCauses"),
/**
* The key for this arena's loss trigger damage causes
*/
LOSS_TRIGGER_DAMAGE_CAUSES("lossTriggerDamageCauses"),
;
private final @NotNull String key;
@ -69,11 +80,7 @@ public enum DropperArenaStorageKey {
this.key = key;
}
/**
* Gets the configuration key this enum represents
*
* @return <p>The string key representation.</p>
*/
@Override
public @NotNull String getKey() {
return this.key;
}

View File

@ -7,12 +7,14 @@ import net.knarcraft.minigames.arena.ArenaGameMode;
import net.knarcraft.minigames.arena.ArenaRecordsRegistry;
import net.knarcraft.minigames.arena.reward.Reward;
import net.knarcraft.minigames.arena.reward.RewardCondition;
import net.knarcraft.minigames.util.InputValidationHelper;
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.bukkit.block.BlockFace;
import org.bukkit.event.entity.EntityDamageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -89,6 +91,16 @@ public class ParkourArena implements Arena {
*/
private int maxPlayers;
/**
* Types of damage that won't be blocked in this arena
*/
private Set<EntityDamageEvent.DamageCause> allowedDamageCauses;
/**
* Types of damage that will trigger a loss in this arena
*/
private Set<EntityDamageEvent.DamageCause> lossTriggerDamageCauses;
/**
* The checkpoints for this arena. Entering a checkpoint overrides the player's spawn location.
*/
@ -106,26 +118,30 @@ public class ParkourArena implements Arena {
/**
* 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 killPlaneBlockNames <p>The names of the types of blocks that trigger a loss when stepped on</p>
* @param obstacleBlockNames <p>The names of the types of blocks that trigger a loss when touched</p>
* @param checkpoints <p>The checkpoints set for this arena</p>
* @param maxPlayers <p>The maximum amount of players able to join this arena at once</p>
* @param rewards <p>The rewards given by 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>
* @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 killPlaneBlockNames <p>The names of the types of blocks that trigger a loss when stepped on</p>
* @param obstacleBlockNames <p>The names of the types of blocks that trigger a loss when touched</p>
* @param checkpoints <p>The checkpoints set for this arena</p>
* @param maxPlayers <p>The maximum amount of players able to join this arena at once</p>
* @param rewards <p>The rewards given by 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>
* @param allowedDamageCauses <p>The damage causes to not cancel. If the player received fatal damage, a loss is triggered.</p>
* @param lossTriggerDamageCauses <p>The damage causes that will trigger a loss (for arrow traps and similar)</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, @Nullable Set<String> obstacleBlockNames,
@NotNull List<Location> checkpoints, int maxPlayers,
@NotNull Map<RewardCondition, Set<Reward>> rewards,
@NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler) {
@NotNull ParkourArenaData parkourArenaData, @NotNull ParkourArenaHandler arenaHandler,
@Nullable Set<String> allowedDamageCauses,
@Nullable Set<String> lossTriggerDamageCauses) {
this.arenaId = arenaId;
this.arenaName = arenaName;
this.spawnLocation = spawnLocation;
@ -143,6 +159,8 @@ public class ParkourArena implements Arena {
this.parkourArenaHandler = arenaHandler;
this.rewards = rewards;
this.maxPlayers = maxPlayers;
this.allowedDamageCauses = InputValidationHelper.parseDamageCauses(allowedDamageCauses);
this.lossTriggerDamageCauses = InputValidationHelper.parseDamageCauses(lossTriggerDamageCauses);
}
/**
@ -175,6 +193,8 @@ public class ParkourArena implements Arena {
this.checkpoints = new ArrayList<>();
this.parkourArenaHandler = arenaHandler;
this.maxPlayers = -1;
this.allowedDamageCauses = new HashSet<>();
this.lossTriggerDamageCauses = new HashSet<>();
}
@Override
@ -240,6 +260,28 @@ public class ParkourArena implements Arena {
return true;
}
@Override
public @NotNull Set<EntityDamageEvent.DamageCause> getAllowedDamageCauses() {
return this.allowedDamageCauses;
}
@Override
public @NotNull Set<EntityDamageEvent.DamageCause> getLossTriggerDamageCauses() {
return this.lossTriggerDamageCauses;
}
@Override
public boolean setAllowedDamageCauses(@NotNull Set<EntityDamageEvent.DamageCause> causes) {
this.allowedDamageCauses = causes;
return true;
}
@Override
public boolean setLossTriggerDamageCauses(@NotNull Set<EntityDamageEvent.DamageCause> causes) {
this.lossTriggerDamageCauses = causes;
return true;
}
@Override
@NotNull
public Material getWinBlockType() {

View File

@ -1,6 +1,7 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.EditablePropertyType;
import net.knarcraft.minigames.util.ArenaStorageHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@ -74,6 +75,20 @@ public enum ParkourArenaEditableProperty {
*/
MAX_PLAYERS("maxPlayers", (arena) -> String.valueOf(arena.getMaxPlayers()),
EditablePropertyType.INTEGER),
/**
* The arena's allowed damage causes
*/
ALLOWED_DAMAGE_CAUSES("allowedDamageCauses", (arena) -> String.valueOf(
ArenaStorageHelper.getDamageCauseNames(arena.getAllowedDamageCauses())),
EditablePropertyType.DAMAGE_CAUSE_LIST),
/**
* The arena's loss trigger damage causes
*/
LOSS_TRIGGER_DAMAGE_CAUSES("lossTriggerDamageCauses", (arena) -> String.valueOf(
ArenaStorageHelper.getDamageCauseNames(arena.getLossTriggerDamageCauses())),
EditablePropertyType.DAMAGE_CAUSE_LIST),
;
private final @NotNull String argumentString;

View File

@ -1,11 +1,12 @@
package net.knarcraft.minigames.arena.parkour;
import net.knarcraft.minigames.arena.StorageKey;
import org.jetbrains.annotations.NotNull;
/**
* A representation of each key used for storing arena data
*/
public enum ParkourArenaStorageKey {
public enum ParkourArenaStorageKey implements StorageKey {
/**
* The key for an arena's id
@ -66,6 +67,16 @@ public enum ParkourArenaStorageKey {
* The key for this arena's maximum players
*/
MAX_PLAYERS("maxPlayers"),
/**
* The key for this arena's allowed damage causes
*/
ALLOWED_DAMAGE_CAUSES("allowedDamageCauses"),
/**
* The key for this arena's loss trigger damage causes
*/
LOSS_TRIGGER_DAMAGE_CAUSES("lossTriggerDamageCauses"),
;
private final @NotNull String key;
@ -79,11 +90,7 @@ public enum ParkourArenaStorageKey {
this.key = key;
}
/**
* Gets the configuration key this enum represents
*
* @return <p>The string key representation.</p>
*/
@Override
public @NotNull String getKey() {
return this.key;
}

View File

@ -7,6 +7,10 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* An abstract arena edit command, implementing input validation
*/
@ -110,4 +114,15 @@ public abstract class EditArenaCommand implements CommandExecutor {
return material;
}
/**
* Splits the given string on comma, and returns a set of its parts
*
* @param input <p>The input string to get as a set</p>
* @return <p>The resulting string set</p>
*/
@NotNull
protected Set<String> asSet(@NotNull String input) {
return new HashSet<>(List.of(input.split(",")));
}
}

View File

@ -19,7 +19,18 @@ import java.util.UUID;
import static net.knarcraft.knarlib.util.TabCompletionHelper.filterMatchingContains;
public abstract class GroupListCommand<K extends ArenaHandler<L, M>, L extends Arena, M extends ArenaGroup<L, M>> implements TabExecutor {
/**
* A command for listing the arenas in a group
*
* @param <K> <p>The type of arena handler to get arenas from</p>
* @param <L> <p>The type of arena to list</p>
* @param <M> <p>The type of arena group to list</p>
*/
public abstract class GroupListCommand<
K extends ArenaHandler<L, M>,
L extends Arena,
M extends ArenaGroup<L, M>
> implements TabExecutor {
/**
* Displays all currently existing dropper arena groups
@ -29,7 +40,7 @@ public abstract class GroupListCommand<K extends ArenaHandler<L, M>, L extends A
*/
protected void displayExistingGroups(@NotNull K arenaHandler, @NotNull CommandSender sender) {
StringFormatter stringFormatter = MiniGames.getInstance().getStringFormatter();
StringBuilder builder = new StringBuilder(stringFormatter.getUnformattedMessage(
StringBuilder builder = new StringBuilder(stringFormatter.getUnFormattedMessage(
MiniGameMessage.SUCCESS_GROUPS)).append("\n");
arenaHandler.getAllGroups().stream().sorted().forEachOrdered((group) ->
builder.append("- ").append(group.getGroupName()).append("\n"));

View File

@ -7,6 +7,7 @@ import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.command.EditArenaCommand;
import net.knarcraft.minigames.config.DropperConfiguration;
import net.knarcraft.minigames.config.MiniGameMessage;
import net.knarcraft.minigames.util.InputValidationHelper;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
@ -90,6 +91,10 @@ public class EditDropperArenaCommand extends EditArenaCommand {
case NAME -> arena.setName(value);
case EXIT_LOCATION -> arena.setExitLocation(parseLocation(player, value));
case MAX_PLAYERS -> arena.setMaxPlayers(parseMaxPlayers(value));
case ALLOWED_DAMAGE_CAUSES ->
arena.setAllowedDamageCauses(InputValidationHelper.parseDamageCauses(asSet(value)));
case LOSS_TRIGGER_DAMAGE_CAUSES ->
arena.setLossTriggerDamageCauses(InputValidationHelper.parseDamageCauses(asSet(value)));
};
}

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.command.dropper;
import net.knarcraft.minigames.arena.EditablePropertyType;
import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command;
@ -30,8 +31,14 @@ public class EditDropperArenaTabCompleter implements TabCompleter {
if (property == null) {
return new ArrayList<>();
}
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()),
arguments[2]);
EditablePropertyType propertyType = property.getPropertyType();
if (propertyType == EditablePropertyType.MATERIAL_LIST ||
propertyType == EditablePropertyType.DAMAGE_CAUSE_LIST) {
return TabCompleteHelper.getListCompleteSuggestions(propertyType, arguments[2]);
} else {
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(propertyType), arguments[2]);
}
} else {
return new ArrayList<>();
}

View File

@ -6,14 +6,12 @@ import net.knarcraft.minigames.arena.parkour.ParkourArena;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
import net.knarcraft.minigames.command.EditArenaCommand;
import net.knarcraft.minigames.config.MiniGameMessage;
import net.knarcraft.minigames.util.InputValidationHelper;
import org.bukkit.command.Command;
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
*/
@ -96,9 +94,13 @@ public class EditParkourArenaCommand extends EditArenaCommand {
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(","))));
case OBSTACLE_BLOCKS -> arena.setObstacleBlocks(new HashSet<>(List.of(value.split(","))));
case KILL_PLANE_BLOCKS -> arena.setKillPlaneBlocks(asSet(value));
case OBSTACLE_BLOCKS -> arena.setObstacleBlocks(asSet(value));
case MAX_PLAYERS -> arena.setMaxPlayers(parseMaxPlayers(value));
case ALLOWED_DAMAGE_CAUSES ->
arena.setAllowedDamageCauses(InputValidationHelper.parseDamageCauses(asSet(value)));
case LOSS_TRIGGER_DAMAGE_CAUSES ->
arena.setLossTriggerDamageCauses(InputValidationHelper.parseDamageCauses(asSet(value)));
};
}

View File

@ -1,5 +1,6 @@
package net.knarcraft.minigames.command.parkour;
import net.knarcraft.minigames.arena.EditablePropertyType;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
import net.knarcraft.minigames.util.TabCompleteHelper;
import org.bukkit.command.Command;
@ -30,8 +31,14 @@ public class EditParkourArenaTabCompleter implements TabCompleter {
if (property == null) {
return new ArrayList<>();
}
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(property.getPropertyType()),
arguments[2]);
EditablePropertyType propertyType = property.getPropertyType();
if (propertyType == EditablePropertyType.MATERIAL_LIST ||
propertyType == EditablePropertyType.DAMAGE_CAUSE_LIST) {
return TabCompleteHelper.getListCompleteSuggestions(propertyType, arguments[2]);
} else {
return filterMatchingContains(TabCompleteHelper.getTabCompleteSuggestions(propertyType), arguments[2]);
}
} else {
return new ArrayList<>();
}

View File

@ -3,6 +3,8 @@ 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.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@ -32,9 +34,29 @@ public class DamageListener implements Listener {
event.setCancelled(true);
// Only trigger a loss when a player suffers fall damage in a dropper arena
// Only trigger a loss when a player suffers fall damage in a dropper arena (This cannot be cancelled!)
if (arenaSession instanceof DropperArenaSession && event.getCause() == EntityDamageEvent.DamageCause.FALL) {
arenaSession.triggerLoss();
return;
}
// If set as allowed damage, do nothing, except if the damage is fatal
if (arenaSession.getArena().getAllowedDamageCauses().contains(event.getCause())) {
if (event.getFinalDamage() >= player.getHealth()) {
AttributeInstance health = player.getAttribute(Attribute.GENERIC_MAX_HEALTH);
if (health != null) {
player.setHealth(health.getValue());
}
arenaSession.triggerLoss();
} else {
event.setCancelled(false);
}
return;
}
// If set as trigger loss, trigger a loss
if (arenaSession.getArena().getLossTriggerDamageCauses().contains(event.getCause())) {
arenaSession.triggerLoss();
}
}

View File

@ -2,8 +2,12 @@ package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames;
import net.knarcraft.minigames.arena.PlayerEntryState;
import net.knarcraft.minigames.arena.StorageKey;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.event.entity.EntityDamageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.IOException;
@ -78,4 +82,40 @@ public final class ArenaStorageHelper {
return arenaDataFile;
}
/**
* Loads a set of strings from the given configuration section
*
* @param configurationSection <p>The configuration section to load from</p>
* @param storageKey <p>The key to the info to load</p>
* @return <p>The loaded items, or null if not set</p>
*/
@Nullable
@SuppressWarnings("unchecked")
public static Set<String> loadStrings(@NotNull ConfigurationSection configurationSection,
@NotNull StorageKey storageKey) {
List<?> rawList = configurationSection.getList(storageKey.getKey());
Set<String> output;
if (rawList == null) {
output = null;
} else {
output = new HashSet<>((List<String>) rawList);
}
return output;
}
/**
* Gets the names of the given damage causes
*
* @param causes <p>The damage causes to get names of</p>
* @return <p>The names of the damage causes</p>
*/
@NotNull
public static List<String> getDamageCauseNames(@NotNull Set<EntityDamageEvent.DamageCause> causes) {
List<String> output = new ArrayList<>(causes.size());
for (EntityDamageEvent.DamageCause cause : causes) {
output.add(cause.name());
}
return output;
}
}

View File

@ -32,6 +32,7 @@ import java.util.UUID;
import java.util.logging.Level;
import static net.knarcraft.minigames.util.ArenaStorageHelper.getArenaDataFile;
import static net.knarcraft.minigames.util.ArenaStorageHelper.loadStrings;
/**
* A helper class for saving and loading arenas
@ -138,6 +139,10 @@ public final class DropperArenaStorageHelper {
configSection.set(DropperArenaStorageKey.PLAYER_HORIZONTAL_VELOCITY.getKey(), arena.getPlayerHorizontalVelocity());
configSection.set(DropperArenaStorageKey.WIN_BLOCK_TYPE.getKey(), new SerializableMaterial(arena.getWinBlockType()));
configSection.set(DropperArenaStorageKey.MAX_PLAYERS.getKey(), arena.getMaxPlayers());
configSection.set(DropperArenaStorageKey.ALLOWED_DAMAGE_CAUSES.getKey(),
ArenaStorageHelper.getDamageCauseNames(arena.getAllowedDamageCauses()));
configSection.set(DropperArenaStorageKey.LOSS_TRIGGER_DAMAGE_CAUSES.getKey(),
ArenaStorageHelper.getDamageCauseNames(arena.getLossTriggerDamageCauses()));
RewardStorageHelper.saveRewards(arena, configSection, DropperArenaStorageKey.REWARDS.getKey());
saveDropperArenaData(arena.getData());
}
@ -213,8 +218,12 @@ public final class DropperArenaStorageHelper {
arenaData = getEmptyDropperData(arenaId);
}
Set<String> allowedDamageCauseNames = loadStrings(configurationSection, DropperArenaStorageKey.ALLOWED_DAMAGE_CAUSES);
Set<String> lossTriggerDamageCauseNames = loadStrings(configurationSection, DropperArenaStorageKey.LOSS_TRIGGER_DAMAGE_CAUSES);
return new DropperArena(arenaId, arenaName, spawnLocation, exitLocation, verticalVelocity, horizontalVelocity,
winBlockType.getRawValue(), maxPlayers, rewards, arenaData, MiniGames.getInstance().getDropperArenaHandler());
winBlockType.getRawValue(), maxPlayers, rewards, arenaData,
MiniGames.getInstance().getDropperArenaHandler(), allowedDamageCauseNames, lossTriggerDamageCauseNames);
}
/**

View File

@ -1,10 +1,16 @@
package net.knarcraft.minigames.util;
import net.knarcraft.minigames.MiniGames;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.event.entity.EntityDamageEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Level;
/**
* A helper class for validating whether given input is valid
*/
@ -39,4 +45,29 @@ public final class InputValidationHelper {
value.equalsIgnoreCase("none");
}
/**
* Parses a set of damage causes from a set of damage cause names
*
* @param input <p>The damage cause names to parse</p>
* @return <p>The resulting damage causes</p>
*/
@NotNull
public static Set<EntityDamageEvent.DamageCause> parseDamageCauses(@Nullable Set<String> input) {
Set<EntityDamageEvent.DamageCause> output = new HashSet<>();
if (input == null) {
return output;
}
for (String causeName : input) {
try {
output.add(EntityDamageEvent.DamageCause.valueOf(causeName));
} catch (IllegalArgumentException | NullPointerException exception) {
MiniGames.log(Level.WARNING, "The damage cause " + causeName +
" is invalid, and will be ignored.");
}
}
return output;
}
}

View File

@ -33,6 +33,7 @@ import java.util.UUID;
import java.util.logging.Level;
import static net.knarcraft.minigames.util.ArenaStorageHelper.getArenaDataFile;
import static net.knarcraft.minigames.util.ArenaStorageHelper.loadStrings;
/**
* A helper class for saving and loading parkour arenas
@ -141,6 +142,10 @@ public final class ParkourArenaStorageHelper {
configSection.set(ParkourArenaStorageKey.OBSTACLE_BLOCKS.getKey(), getObstacleBlocks(arena));
configSection.set(ParkourArenaStorageKey.CHECKPOINTS.getKey(), arena.getCheckpoints());
configSection.set(ParkourArenaStorageKey.MAX_PLAYERS.getKey(), arena.getMaxPlayers());
configSection.set(ParkourArenaStorageKey.ALLOWED_DAMAGE_CAUSES.getKey(),
ArenaStorageHelper.getDamageCauseNames(arena.getAllowedDamageCauses()));
configSection.set(ParkourArenaStorageKey.LOSS_TRIGGER_DAMAGE_CAUSES.getKey(),
ArenaStorageHelper.getDamageCauseNames(arena.getLossTriggerDamageCauses()));
RewardStorageHelper.saveRewards(arena, configSection, ParkourArenaStorageKey.REWARDS.getKey());
saveParkourArenaData(arena.getData());
}
@ -151,7 +156,8 @@ public final class ParkourArenaStorageHelper {
* @param arena <p>The arena to get kill plane blocks for</p>
* @return <p>The kill plane blocks</p>
*/
private static List<String> getKillPlaneBlocks(ParkourArena arena) {
@NotNull
private static List<String> getKillPlaneBlocks(@NotNull ParkourArena arena) {
if (arena.getKillPlaneBlockNames() == null) {
return new ArrayList<>();
} else {
@ -165,7 +171,8 @@ public final class ParkourArenaStorageHelper {
* @param arena <p>The arena to get obstacle blocks for</p>
* @return <p>The obstacle blocks</p>
*/
private static List<String> getObstacleBlocks(ParkourArena arena) {
@NotNull
private static List<String> getObstacleBlocks(@NotNull ParkourArena arena) {
if (arena.getObstacleBlockNames() == null) {
return new ArrayList<>();
} else {
@ -178,7 +185,8 @@ public final class ParkourArenaStorageHelper {
*
* @return <p>The loaded arenas, or null if the arenas configuration section is missing.</p>
*/
public static @NotNull Map<UUID, ParkourArena> loadParkourArenas() {
@NotNull
public static Map<UUID, ParkourArena> loadParkourArenas() {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(parkourArenaFile);
ConfigurationSection arenaSection = configuration.getConfigurationSection(parkourArenasConfigurationSection);
//If no such section exists, it must be the case that there is no data to load
@ -211,7 +219,8 @@ public final class ParkourArenaStorageHelper {
* @return <p>The loaded arena, or null if invalid</p>
*/
@SuppressWarnings("unchecked")
private static @Nullable ParkourArena loadParkourArena(@NotNull ConfigurationSection configurationSection) {
@Nullable
private static ParkourArena loadParkourArena(@NotNull ConfigurationSection configurationSection) {
UUID arenaId = ((SerializableUUID) configurationSection.get(ParkourArenaStorageKey.ID.getKey(),
new SerializableUUID(UUID.randomUUID()))).getRawValue();
String arenaName = configurationSection.getString(ParkourArenaStorageKey.NAME.getKey());
@ -219,22 +228,9 @@ public final class ParkourArenaStorageHelper {
Location exitLocation = (Location) configurationSection.get(ParkourArenaStorageKey.EXIT_LOCATION.getKey());
Location winLocation = (Location) configurationSection.get(ParkourArenaStorageKey.WIN_LOCATION.getKey());
int maxPlayers = configurationSection.getInt(ParkourArenaStorageKey.MAX_PLAYERS.getKey(), -1);
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(
ParkourArenaStorageKey.WIN_BLOCK_TYPE.getKey());
List<?> killPlaneBlockNamesList = configurationSection.getList(ParkourArenaStorageKey.KILL_PLANE_BLOCKS.getKey());
Set<String> killPlaneBlockNames;
if (killPlaneBlockNamesList == null) {
killPlaneBlockNames = null;
} else {
killPlaneBlockNames = new HashSet<>((List<String>) killPlaneBlockNamesList);
}
List<?> obstacleBlockNamesList = configurationSection.getList(ParkourArenaStorageKey.OBSTACLE_BLOCKS.getKey());
Set<String> obstacleBlockNames;
if (obstacleBlockNamesList == null) {
obstacleBlockNames = null;
} else {
obstacleBlockNames = new HashSet<>((List<String>) obstacleBlockNamesList);
}
SerializableMaterial winBlockType = (SerializableMaterial) configurationSection.get(ParkourArenaStorageKey.WIN_BLOCK_TYPE.getKey());
Set<String> killPlaneBlockNames = loadStrings(configurationSection, ParkourArenaStorageKey.KILL_PLANE_BLOCKS);
Set<String> obstacleBlockNames = loadStrings(configurationSection, ParkourArenaStorageKey.OBSTACLE_BLOCKS);
List<Location> checkpoints = (List<Location>) configurationSection.get(ParkourArenaStorageKey.CHECKPOINTS.getKey());
Map<RewardCondition, Set<Reward>> rewards = RewardStorageHelper.loadRewards(configurationSection,
@ -265,9 +261,12 @@ public final class ParkourArenaStorageHelper {
checkpoints = new ArrayList<>();
}
Set<String> allowedDamageCauseNames = loadStrings(configurationSection, ParkourArenaStorageKey.ALLOWED_DAMAGE_CAUSES);
Set<String> lossTriggerDamageCauseNames = loadStrings(configurationSection, ParkourArenaStorageKey.LOSS_TRIGGER_DAMAGE_CAUSES);
return new ParkourArena(arenaId, arenaName, spawnLocation, exitLocation, winBlockType.getRawValue(), winLocation,
killPlaneBlockNames, obstacleBlockNames, checkpoints, maxPlayers, rewards, arenaData,
MiniGames.getInstance().getParkourArenaHandler());
MiniGames.getInstance().getParkourArenaHandler(), allowedDamageCauseNames, lossTriggerDamageCauseNames);
}
/**
@ -276,7 +275,8 @@ public final class ParkourArenaStorageHelper {
* @param arenaId <p>The id to get parkour data for</p>
* @return <p>Empty parkour data</p>
*/
private static @NotNull ParkourArenaData getEmptyParkourData(@NotNull UUID arenaId) {
@NotNull
private static ParkourArenaData getEmptyParkourData(@NotNull UUID arenaId) {
Map<ArenaGameMode, ArenaRecordsRegistry> recordRegistries = new HashMap<>();
Map<ArenaGameMode, Set<UUID>> playersCompleted = new HashMap<>();
for (ArenaGameMode arenaGameMode : ParkourArenaGameMode.values()) {
@ -304,7 +304,8 @@ public final class ParkourArenaStorageHelper {
* @param arenaId <p>The id of the arena to get data for</p>
* @return <p>The loaded arena data</p>
*/
private static @Nullable ParkourArenaData loadParkourArenaData(@NotNull UUID arenaId) {
@Nullable
private static ParkourArenaData loadParkourArenaData(@NotNull UUID arenaId) {
File arenaDataFile = getParkourArenaDataFile(arenaId);
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(arenaDataFile);
return (ParkourArenaData) configuration.get(ParkourArenaStorageKey.DATA.getKey());
@ -326,7 +327,8 @@ public final class ParkourArenaStorageHelper {
* @param arenaId <p>The id of the arena to get a data file for</p>
* @return <p>The file the arena's data is/should be stored in</p>
*/
private static @NotNull File getParkourArenaDataFile(@NotNull UUID arenaId) {
@NotNull
private static File getParkourArenaDataFile(@NotNull UUID arenaId) {
return getArenaDataFile(parkourArenaDataFolder, arenaId);
}

View File

@ -9,6 +9,8 @@ import net.knarcraft.minigames.arena.dropper.DropperArenaEditableProperty;
import net.knarcraft.minigames.arena.parkour.ParkourArenaEditableProperty;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Tag;
import org.bukkit.event.entity.EntityDamageEvent;
import org.bukkit.permissions.Permission;
import org.jetbrains.annotations.NotNull;
@ -16,6 +18,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
/**
@ -112,11 +115,53 @@ public final class TabCompleteHelper {
tabCompleteSuggestions.put(EditablePropertyType.MATERIAL_LIST, getMaterialListSuggestions());
tabCompleteSuggestions.put(EditablePropertyType.DOUBLE, getDoubleSuggestions());
tabCompleteSuggestions.put(EditablePropertyType.INTEGER, getIntegerSuggestions());
tabCompleteSuggestions.put(EditablePropertyType.DAMAGE_CAUSE_LIST, getDamageCauseSuggestions());
}
return tabCompleteSuggestions.get(propertyType);
}
/**
* Gets tab-complete suggestions for list properties
*
* @param propertyType <p>Gets the type of property to show tab-completions for</p>
* @param input <p>The input string to use for filtering</p>
* @return <p>The tab-completions to suggest</p>
*/
@NotNull
public static List<String> getListCompleteSuggestions(@NotNull EditablePropertyType propertyType,
@NotNull String input) {
List<String> allValues;
if (propertyType == EditablePropertyType.DAMAGE_CAUSE_LIST) {
allValues = getTabCompleteSuggestions((EditablePropertyType.DAMAGE_CAUSE_LIST));
} else if (propertyType == EditablePropertyType.MATERIAL_LIST) {
allValues = getTabCompleteSuggestions((EditablePropertyType.MATERIAL_LIST));
} else {
throw new IllegalArgumentException("Invalid property type given!");
}
return TabCompletionHelper.getStringList(allValues, input, TabCompletionHelper::filterMatchingContains);
}
/**
* Gets tab-complete suggestions for a list of damage causes
*
* @return <p>Damage cause suggestions</p>
*/
@NotNull
private static List<String> getDamageCauseSuggestions() {
List<String> suggestions = new ArrayList<>();
for (EntityDamageEvent.DamageCause damageCause : EntityDamageEvent.DamageCause.values()) {
suggestions.add(damageCause.name());
}
return suggestions;
}
/**
* Gets tab-complete suggestions for an integer value
*
* @return <p>Integer suggestions</p>
*/
@NotNull
private static List<String> getIntegerSuggestions() {
List<String> suggestions = new ArrayList<>();
suggestions.add("-1");
@ -157,9 +202,14 @@ public final class TabCompleteHelper {
@NotNull
private static List<String> getMaterialListSuggestions() {
List<String> suggestions = new ArrayList<>();
suggestions.add("LAVA,MAGMA_BLOCK");
suggestions.add("WATER,MAGMA_BLOCK,LAVA,+BUTTONS,+CORALS");
suggestions.add("CHAIN,END_ROD,LIGHTNING_ROD");
for (Material material : Material.values()) {
if (material.isBlock()) {
suggestions.add(material.getKey().getKey());
}
}
for (Tag<Material> tag : getTags()) {
suggestions.add("+" + tag.getKey().getKey());
}
return suggestions;
}
@ -315,4 +365,28 @@ public final class TabCompleteHelper {
}
}
/**
* Gets all useful tags for physical blocks
*
* @return <p>All block tags</p>
*/
@NotNull
private static Set<Tag<Material>> getTags() {
// TODO: Find a way to get the tags programmatically
return Set.of(Tag.ACACIA_LOGS, Tag.ALL_HANGING_SIGNS, Tag.ALL_SIGNS, Tag.ANVIL, Tag.WALLS,
Tag.BAMBOO_BLOCKS, Tag.BANNERS, Tag.BEDS, Tag.BEEHIVES, Tag.BIRCH_LOGS,
Tag.BUTTONS, Tag.CAMPFIRES, Tag.CANDLE_CAKES, Tag.CANDLES, Tag.CAULDRONS, Tag.CAVE_VINES,
Tag.CEILING_HANGING_SIGNS, Tag.CHERRY_LOGS, Tag.CLIMBABLE, Tag.COAL_ORES, Tag.CONCRETE_POWDER,
Tag.COPPER_ORES, Tag.CORAL_BLOCKS, Tag.CORAL_PLANTS, Tag.CORALS, Tag.CRIMSON_STEMS, Tag.CROPS,
Tag.DARK_OAK_LOGS, Tag.DIAMOND_ORES, Tag.DIRT, Tag.DOORS, Tag.EMERALD_ORES, Tag.FENCE_GATES, Tag.FENCES,
Tag.FIRE, Tag.FLOWER_POTS, Tag.FLOWERS, Tag.GOLD_ORES, Tag.ICE, Tag.IRON_ORES, Tag.JUNGLE_LOGS,
Tag.LAPIS_ORES, Tag.LEAVES, Tag.LOGS, Tag.MANGROVE_LOGS, Tag.NYLIUM, Tag.OAK_LOGS, Tag.PLANKS,
Tag.PORTALS, Tag.PRESSURE_PLATES, Tag.RAILS, Tag.REDSTONE_ORES, Tag.SAND, Tag.SAPLINGS, Tag.WART_BLOCKS,
Tag.SHULKER_BOXES, Tag.SIGNS, Tag.SLABS, Tag.SMALL_FLOWERS, Tag.SNOW, Tag.SPRUCE_LOGS, Tag.STAIRS,
Tag.STANDING_SIGNS, Tag.STONE_BRICKS, Tag.STONE_BUTTONS, Tag.TALL_FLOWERS, Tag.TERRACOTTA, Tag.WOOL,
Tag.TRAPDOORS, Tag.WALL_CORALS, Tag.WALL_HANGING_SIGNS, Tag.WALL_SIGNS, Tag.WARPED_STEMS,
Tag.WOODEN_BUTTONS, Tag.WOODEN_DOORS, Tag.WOODEN_FENCES, Tag.WOODEN_PRESSURE_PLATES, Tag.WOODEN_STAIRS,
Tag.WOODEN_TRAPDOORS);
}
}

View File

@ -67,7 +67,7 @@ dropper:
- REDSTONE_WALL_TORCH
- +BANNERS
- +BUTTONS
- +CORALS
- +CORAL_PLANTS
- +WALL_CORALS
- LIGHT
shared: