Add Hardcore mode with player elimination mechanics.
- Introduced Hardcore mode toggle via `/skyblock hardcore` command. - Implemented player elimination and spectator mode handling upon death. - Added mechanics to clear Hardcore status during player reset. - Enhanced respawn logic to respect island or Hardcore mode settings. - Registered `HardcoreHandler` for relevant events.
This commit is contained in:
parent
3edf908b89
commit
7936a525b8
5 changed files with 172 additions and 5 deletions
|
@ -33,4 +33,7 @@ public class Reference {
|
||||||
|
|
||||||
public static IDataContainerRef<Boolean, Item> ITEM_LAVA_IMMUNE = new AnonymousDataContainerRef<>(key("item.lava.immune"), PersistentDataType.BOOLEAN);
|
public static IDataContainerRef<Boolean, Item> ITEM_LAVA_IMMUNE = new AnonymousDataContainerRef<>(key("item.lava.immune"), PersistentDataType.BOOLEAN);
|
||||||
|
|
||||||
|
public static IDataContainerRef<Boolean, Player> HARDCORE_ENABLED = new AnonymousDataContainerRef<>(key("player.hardcore.enabled"), PersistentDataType.BOOLEAN);
|
||||||
|
public static IDataContainerRef<Boolean, Player> HARDCORE_ELIMINATED = new AnonymousDataContainerRef<>(key("player.hardcore.eliminated"), PersistentDataType.BOOLEAN);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,6 +42,10 @@ public final class UsefulSkyblock extends JavaPlugin implements Listener {
|
||||||
dialogMaps = new java.util.concurrent.ConcurrentHashMap<>();
|
dialogMaps = new java.util.concurrent.ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static UsefulSkyblock instance() {
|
||||||
|
return getPlugin(UsefulSkyblock.class);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
saveDefaultConfig();
|
saveDefaultConfig();
|
||||||
|
@ -64,6 +68,7 @@ public final class UsefulSkyblock extends JavaPlugin implements Listener {
|
||||||
pluginManager.registerEvents(new InitialisationHandler(), this);
|
pluginManager.registerEvents(new InitialisationHandler(), this);
|
||||||
pluginManager.registerEvents(new TeamProgressHandler(), this);
|
pluginManager.registerEvents(new TeamProgressHandler(), this);
|
||||||
pluginManager.registerEvents(new FishingHandler(), this);
|
pluginManager.registerEvents(new FishingHandler(), this);
|
||||||
|
pluginManager.registerEvents(new HardcoreHandler(), this);
|
||||||
|
|
||||||
Server server = Bukkit.getServer();
|
Server server = Bukkit.getServer();
|
||||||
for (int i = recipeProviders.length-1; i >= 0; i--) {
|
for (int i = recipeProviders.length-1; i >= 0; i--) {
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.ncguy.usefulskyblock.command;
|
||||||
import com.mojang.brigadier.StringReader;
|
import com.mojang.brigadier.StringReader;
|
||||||
import com.mojang.brigadier.arguments.ArgumentType;
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
import com.mojang.brigadier.context.CommandContext;
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
import com.mojang.brigadier.suggestion.Suggestions;
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
@ -10,11 +11,14 @@ import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
import com.mojang.brigadier.tree.LiteralCommandNode;
|
import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import com.ncguy.usefulskyblock.*;
|
import com.ncguy.usefulskyblock.*;
|
||||||
import com.ncguy.usefulskyblock.data.BiomedStructure;
|
import com.ncguy.usefulskyblock.data.BiomedStructure;
|
||||||
|
import com.ncguy.usefulskyblock.pdc.IDataContainerRef;
|
||||||
import com.ncguy.usefulskyblock.utils.Components;
|
import com.ncguy.usefulskyblock.utils.Components;
|
||||||
import com.ncguy.usefulskyblock.utils.TextUtils;
|
import com.ncguy.usefulskyblock.utils.TextUtils;
|
||||||
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
import io.papermc.paper.command.brigadier.CommandSourceStack;
|
||||||
import io.papermc.paper.command.brigadier.Commands;
|
import io.papermc.paper.command.brigadier.Commands;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.ArgumentTypes;
|
||||||
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
|
import io.papermc.paper.command.brigadier.argument.CustomArgumentType;
|
||||||
|
import io.papermc.paper.command.brigadier.argument.resolvers.selector.PlayerSelectorArgumentResolver;
|
||||||
import io.papermc.paper.registry.data.dialog.body.DialogBody;
|
import io.papermc.paper.registry.data.dialog.body.DialogBody;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.Style;
|
import net.kyori.adventure.text.format.Style;
|
||||||
|
@ -39,6 +43,7 @@ import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import static com.ncguy.usefulskyblock.Reference.key;
|
import static com.ncguy.usefulskyblock.Reference.key;
|
||||||
|
@ -132,7 +137,8 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
|
||||||
teamSpawn.set(tpLoc.toVector().toBlockVector());
|
teamSpawn.set(tpLoc.toVector().toBlockVector());
|
||||||
worldTeamCount.set(count + 1);
|
worldTeamCount.set(count + 1);
|
||||||
|
|
||||||
executor.teleport(tpLoc);
|
player.teleport(tpLoc);
|
||||||
|
player.setGameMode(GameMode.SURVIVAL);
|
||||||
islandHomeLoc.set(tpLoc.toVector().toBlockVector());
|
islandHomeLoc.set(tpLoc.toVector().toBlockVector());
|
||||||
|
|
||||||
Advancement a = Advancements.skyblock.SKYBLOCK_BEGIN;
|
Advancement a = Advancements.skyblock.SKYBLOCK_BEGIN;
|
||||||
|
@ -244,7 +250,9 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
|
||||||
b.body(List.of(
|
b.body(List.of(
|
||||||
DialogBody.plainMessage(Component.text("Know that this action is irreversible")),
|
DialogBody.plainMessage(Component.text("Know that this action is irreversible")),
|
||||||
DialogBody.plainMessage(Component.newline()),
|
DialogBody.plainMessage(Component.newline()),
|
||||||
DialogBody.plainMessage(Component.text("To skip this notice in future, use the command variant ").append(Components.Command("/skyblock confirm")).append(Component.text(".")))
|
DialogBody.plainMessage(Component.text("To skip this notice in future, use the command variant ")
|
||||||
|
.append(Components.Command("/skyblock confirm"))
|
||||||
|
.append(Component.text(".")))
|
||||||
));
|
));
|
||||||
}).thenAccept(res -> {
|
}).thenAccept(res -> {
|
||||||
if (res) {
|
if (res) {
|
||||||
|
@ -255,8 +263,8 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
doTeleport.run();
|
doTeleport.run();
|
||||||
|
};
|
||||||
|
|
||||||
if (islandHomeLoc.has()) {
|
if (islandHomeLoc.has()) {
|
||||||
tryTeleport.run();
|
tryTeleport.run();
|
||||||
|
@ -335,10 +343,70 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
|
||||||
root.then(team);
|
root.then(team);
|
||||||
root.executes(cmd::executeRootNoConfirm);
|
root.executes(cmd::executeRootNoConfirm);
|
||||||
root.then(Commands.literal("confirm").executes(cmd::executeRootConfirm));
|
root.then(Commands.literal("confirm").executes(cmd::executeRootConfirm));
|
||||||
|
LiteralArgumentBuilder<CommandSourceStack> hardcore = Commands.literal("hardcore");
|
||||||
|
|
||||||
|
hardcore.executes(cmd::executeToggleHardcore);
|
||||||
|
hardcore.then(Commands.argument("player", ArgumentTypes.player()).requires(cmd::auth).executes(cmd::executeToggleHardcoreForPlayer));
|
||||||
|
|
||||||
|
root.then(hardcore);
|
||||||
|
|
||||||
return root.build();
|
return root.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void toggleHardcoreForPlayer(Player player, boolean skipConfirmationForEnable) {
|
||||||
|
if (Reference.HARDCORE_ENABLED.assign(player).getOrDefault(false))
|
||||||
|
disableHardcore(player);
|
||||||
|
else
|
||||||
|
enableHardcore(player, skipConfirmationForEnable);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disableHardcore(Player player) {
|
||||||
|
player.sendMessage(Component.text("Hardcore mode disabled"));
|
||||||
|
Reference.HARDCORE_ENABLED.assign(player).set(false);
|
||||||
|
Reference.HARDCORE_ELIMINATED.assign(player).set(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void enableHardcore(Player player, boolean skipConfirmation) {
|
||||||
|
Runnable _enableHardcore = () -> {
|
||||||
|
player.sendMessage(Component.text("Hardcore mode enabled"));
|
||||||
|
Reference.HARDCORE_ENABLED.assign(player).set(true);
|
||||||
|
Reference.HARDCORE_ELIMINATED.assign(player).set(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
if(skipConfirmation) {
|
||||||
|
_enableHardcore.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
UsefulSkyblock.instance().confirm(player, Component.text("Enabling hardcore mode"), b -> {})
|
||||||
|
.thenAccept(res -> {
|
||||||
|
if (res) {
|
||||||
|
_enableHardcore.run();
|
||||||
|
} else {
|
||||||
|
player.sendMessage(Component.text("Hardcore mode not enabled"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeToggleHardcoreForPlayer(CommandContext<CommandSourceStack> ctx) {
|
||||||
|
PlayerSelectorArgumentResolver selector = ctx.getArgument("player", PlayerSelectorArgumentResolver.class);
|
||||||
|
Player player = null;
|
||||||
|
try {
|
||||||
|
player = selector.resolve(ctx.getSource()).getFirst();
|
||||||
|
toggleHardcoreForPlayer(player, true);
|
||||||
|
} catch (CommandSyntaxException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int executeToggleHardcore(CommandContext<CommandSourceStack> ctx) {
|
||||||
|
Entity executor = ctx.getSource().getExecutor();
|
||||||
|
if (!(executor instanceof Player player)) return -1;
|
||||||
|
toggleHardcoreForPlayer(player, false);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private int executeRootConfirm(CommandContext<CommandSourceStack> ctx) {
|
private int executeRootConfirm(CommandContext<CommandSourceStack> ctx) {
|
||||||
return executeRoot(ctx, true);
|
return executeRoot(ctx, true);
|
||||||
}
|
}
|
||||||
|
@ -589,6 +657,13 @@ public class SkyblockGenCommand extends AbstractSkyblockCommand {
|
||||||
prg.getAwardedCriteria().forEach(prg::revokeCriteria);
|
prg.getAwardedCriteria().forEach(prg::revokeCriteria);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear hardcore status
|
||||||
|
IDataContainerRef<Boolean, Player> assign = Reference.HARDCORE_ENABLED.assign(player);
|
||||||
|
if(assign.getOrDefault(false))
|
||||||
|
player.sendMessage("Hardcore status cleared.");
|
||||||
|
assign.remove();
|
||||||
|
Reference.HARDCORE_ELIMINATED.assign(player).remove();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.ncguy.usefulskyblock.handlers;
|
||||||
|
|
||||||
|
import com.ncguy.usefulskyblock.Reference;
|
||||||
|
import com.ncguy.usefulskyblock.UsefulSkyblock;
|
||||||
|
import org.bukkit.Bukkit;
|
||||||
|
import org.bukkit.GameMode;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
|
import org.bukkit.event.player.PlayerGameModeChangeEvent;
|
||||||
|
import org.bukkit.event.player.PlayerRespawnEvent;
|
||||||
|
|
||||||
|
public class HardcoreHandler implements Listener {
|
||||||
|
|
||||||
|
private boolean isPlayerHardcore(Player player) {
|
||||||
|
return Reference.HARDCORE_ENABLED.assign(player).getOrDefault(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isPlayerEliminated(Player player) {
|
||||||
|
return Reference.HARDCORE_ELIMINATED.assign(player).getOrDefault(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void eliminate(Player player) {
|
||||||
|
Reference.HARDCORE_ELIMINATED.assign(player).set(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
if(!isPlayerHardcore(player)) return;
|
||||||
|
eliminate(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onRespawn(PlayerRespawnEvent e) {
|
||||||
|
Player p = e.getPlayer();
|
||||||
|
if (isPlayerHardcore(p) && isPlayerEliminated(p) && p.getGameMode() != GameMode.SPECTATOR) {
|
||||||
|
// Delay gamemode change by 1 tick to avoid race conflicts
|
||||||
|
Bukkit.getScheduler().runTask(UsefulSkyblock.instance(), () -> p.setGameMode(GameMode.SPECTATOR));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onGamemodeChange(PlayerGameModeChangeEvent e) {
|
||||||
|
Player p = e.getPlayer();
|
||||||
|
if (isPlayerHardcore(p) && isPlayerEliminated(p) && e.getNewGameMode() != GameMode.SPECTATOR) {
|
||||||
|
e.setCancelled(true);
|
||||||
|
p.sendMessage("You are eliminated in Hardcore and cannot change gamemode.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ import org.bukkit.block.structure.StructureRotation;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.bukkit.event.EventHandler;
|
import org.bukkit.event.EventHandler;
|
||||||
import org.bukkit.event.Listener;
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.entity.PlayerDeathEvent;
|
||||||
import org.bukkit.event.player.PlayerJoinEvent;
|
import org.bukkit.event.player.PlayerJoinEvent;
|
||||||
import org.bukkit.event.player.PlayerTeleportEvent;
|
import org.bukkit.event.player.PlayerTeleportEvent;
|
||||||
import org.bukkit.event.server.ServerLoadEvent;
|
import org.bukkit.event.server.ServerLoadEvent;
|
||||||
|
@ -23,6 +24,7 @@ import org.bukkit.persistence.PersistentDataType;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static com.ncguy.usefulskyblock.Reference.key;
|
import static com.ncguy.usefulskyblock.Reference.key;
|
||||||
|
@ -45,19 +47,47 @@ public class InitialisationHandler implements Listener {
|
||||||
player.setRespawnLocation(world.getSpawnLocation());
|
player.setRespawnLocation(world.getSpawnLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@EventHandler
|
||||||
|
public void onPlayerDeath(PlayerDeathEvent event) {
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
Location resLoc = player.getRespawnLocation();
|
||||||
|
if(resLoc != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var islandHome = Reference.ISLAND_HOME_LOC.assign(player);
|
||||||
|
if(!islandHome.has())
|
||||||
|
return;
|
||||||
|
|
||||||
|
World overworld = Bukkit.getWorld(NamespacedKey.minecraft("overworld"));
|
||||||
|
player.setRespawnLocation(islandHome.get().toLocation(Objects.requireNonNull(overworld)));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPlayerPostRespawn(PlayerPostRespawnEvent event) {
|
public void onPlayerPostRespawn(PlayerPostRespawnEvent event) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
player.sendMessage(Component.text("Hello, " + player.getName() + "!"));
|
player.sendMessage(Component.text("Hello, " + player.getName() + "!"));
|
||||||
|
|
||||||
|
World overworld = Objects.requireNonNull(Bukkit.getWorld(NamespacedKey.minecraft("overworld")));
|
||||||
|
|
||||||
var islandHome = Reference.ISLAND_HOME_LOC.assign(player);
|
var islandHome = Reference.ISLAND_HOME_LOC.assign(player);
|
||||||
if(islandHome.has()) {
|
if(islandHome.has()) {
|
||||||
// TODO Handle respawning and such, especially when bed is missing
|
// TODO Handle respawning and such, especially when bed is missing
|
||||||
|
Location respawnLocation = player.getRespawnLocation();
|
||||||
|
if(respawnLocation == null) {
|
||||||
|
player.teleport(islandHome.get().toLocation(overworld), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("Respawn location: " + respawnLocation);
|
||||||
|
System.out.println("Island home: " + islandHome.get());
|
||||||
|
System.out.println("World respawn location: " + player.getWorld().getSpawnLocation());
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the player doesn't have a home island, respawn them in the server lobby
|
// If the player doesn't have a home island, respawn them in the server lobby
|
||||||
World world = Bukkit.getWorld(key("void"));
|
World world = Objects.requireNonNull(Bukkit.getWorld(key("void")));
|
||||||
player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
player.teleport(world.getSpawnLocation(), PlayerTeleportEvent.TeleportCause.PLUGIN);
|
||||||
player.setRespawnLocation(world.getSpawnLocation());
|
player.setRespawnLocation(world.getSpawnLocation());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue