- Updated LoLib dependency to version 3.0.0 in build.gradle.kts - Added HikariCP and MySQL connector dependencies for improved database handling - Enhanced jar minimization process to exclude JDBC pool and driver - Refactored configuration management by introducing ConfigAccessor and ConfigLoader for better file handling and validation - Removed deprecated GenerationOptions class and integrated its functionality into the new structure - Improved command error handling and added cooldown hints for admin commands
246 lines
No EOL
8.5 KiB
Java
246 lines
No EOL
8.5 KiB
Java
package dev.loki.loparkour.mode;
|
|
|
|
import dev.loki.loparkour.LoParkour;
|
|
import dev.loki.loparkour.generator.ParkourGenerator;
|
|
import dev.loki.loparkour.mode.elytra.*;
|
|
import dev.loki.loparkour.player.ParkourPlayer;
|
|
import dev.loki.loparkour.session.Session;
|
|
import dev.loki.loparkour.util.ColorUtil;
|
|
import org.bukkit.*;
|
|
import org.bukkit.entity.Firework;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.event.EventHandler;
|
|
import org.bukkit.event.EventPriority;
|
|
import org.bukkit.event.HandlerList;
|
|
import org.bukkit.event.Listener;
|
|
import org.bukkit.event.player.PlayerInteractEvent;
|
|
import org.bukkit.inventory.ItemStack;
|
|
import org.bukkit.inventory.meta.FireworkMeta;
|
|
import org.bukkit.scheduler.BukkitTask;
|
|
import org.bukkit.util.Vector;
|
|
import org.jetbrains.annotations.NotNull;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
/**
|
|
* Refactored Elytra mode generator using composition pattern.
|
|
* Coordinates ring generation, physics, and rendering components.
|
|
*/
|
|
public class ElytraGenerator extends ParkourGenerator implements Listener {
|
|
|
|
private final ElytraConfig config;
|
|
private final ElytraRingGenerator ringGenerator;
|
|
private final ElytraPhysics physics;
|
|
private final ElytraRenderer renderer;
|
|
|
|
private final List<ElytraRing> rings = new ArrayList<>();
|
|
private final Map<ParkourPlayer, Integer> playerRingIndex = new HashMap<>();
|
|
|
|
private BukkitTask renderTask;
|
|
private BukkitTask physicsTask;
|
|
|
|
public ElytraGenerator(@NotNull Session session) {
|
|
super(session);
|
|
|
|
this.config = new ElytraConfig();
|
|
this.ringGenerator = new ElytraRingGenerator(config);
|
|
this.physics = new ElytraPhysics(config);
|
|
this.renderer = new ElytraRenderer();
|
|
|
|
Bukkit.getPluginManager().registerEvents(this, LoParkour.getPlugin());
|
|
}
|
|
|
|
@Override
|
|
public void generateFirst(Location spawn, Location blockSpawn) {
|
|
// Generate initial rings
|
|
Vector initialDirection = new Vector(1, 0, 0); // East direction
|
|
List<ElytraRing> initialRings = ringGenerator.generateRings(spawn, initialDirection, config.getRingLead());
|
|
rings.addAll(initialRings);
|
|
|
|
// Initialize player data
|
|
for (ParkourPlayer player : getPlayers()) {
|
|
playerRingIndex.put(player, 0);
|
|
giveElytraAndFireworks(player);
|
|
}
|
|
|
|
startTasks();
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
checkRingsAndPhysics();
|
|
generateMoreRings();
|
|
cleanupOldRings();
|
|
}
|
|
|
|
@Override
|
|
public void reset(boolean regenerate) {
|
|
cleanup(); // Call cleanup BEFORE super.reset to cancel tasks first
|
|
super.reset(regenerate);
|
|
}
|
|
|
|
private void checkRingsAndPhysics() {
|
|
for (ParkourPlayer player : getPlayers()) {
|
|
int currentIndex = playerRingIndex.getOrDefault(player, 0);
|
|
|
|
// Check ring crossings
|
|
int newIndex = physics.checkRingCrossings(player, rings, currentIndex);
|
|
if (newIndex > currentIndex) {
|
|
onRingPassed(player, rings.get(newIndex - 1));
|
|
playerRingIndex.put(player, newIndex);
|
|
}
|
|
|
|
// Check for falls
|
|
ElytraPhysics.FallCheckResult fallResult = physics.checkFall(player, rings, currentIndex);
|
|
if (fallResult.shouldFall()) {
|
|
triggerFall(player, fallResult.getReason());
|
|
}
|
|
|
|
updateActionBar(player);
|
|
}
|
|
}
|
|
|
|
private void generateMoreRings() {
|
|
if (rings.isEmpty()) {
|
|
return;
|
|
}
|
|
if (rings.size() < config.getRingLead() * 2) {
|
|
ElytraRing lastRing = rings.get(rings.size() - 1);
|
|
Vector direction = calculateNextDirection(lastRing);
|
|
|
|
List<ElytraRing> newRings = ringGenerator.generateRings(
|
|
lastRing.getCenter(), direction, config.getRingLead()
|
|
);
|
|
rings.addAll(newRings);
|
|
}
|
|
}
|
|
|
|
private void onRingPassed(@NotNull ParkourPlayer player, @NotNull ElytraRing ring) {
|
|
boolean centered = physics.isPlayerCentered(player.player.getLocation(), ring);
|
|
int points = centered ? 10 : 5;
|
|
|
|
player.session.generator.state.score += points;
|
|
|
|
// Play sound and show message
|
|
player.player.playSound(player.player.getLocation(), Sound.ENTITY_EXPERIENCE_ORB_PICKUP, 1.0f, 1.0f);
|
|
|
|
String message = centered ? "Perfect! +" + points : "Good! +" + points;
|
|
player.player.sendMessage(ColorUtil.color("&a" + message));
|
|
}
|
|
|
|
private void triggerFall(@NotNull ParkourPlayer player, @NotNull String reason) {
|
|
fall();
|
|
player.player.sendMessage(ColorUtil.color("&cFell: " + reason));
|
|
}
|
|
|
|
@SuppressWarnings("deprecation") // Spigot Chat API: action bar via TextComponent
|
|
private void updateActionBar(@NotNull ParkourPlayer player) {
|
|
int currentIndex = playerRingIndex.getOrDefault(player, 0);
|
|
int totalRings = rings.size();
|
|
int score = state.score;
|
|
|
|
String message = String.format("&eRing: %d/%d &7| &aScore: %d",
|
|
currentIndex + 1, totalRings, score);
|
|
|
|
player.player.spigot().sendMessage(
|
|
net.md_5.bungee.api.ChatMessageType.ACTION_BAR,
|
|
net.md_5.bungee.api.chat.TextComponent.fromLegacyText(ColorUtil.color(message))
|
|
);
|
|
}
|
|
|
|
@EventHandler(priority = EventPriority.HIGH)
|
|
public void onPlayerInteract(@NotNull PlayerInteractEvent event) {
|
|
Player player = event.getPlayer();
|
|
ParkourPlayer pp = findParkourPlayer(player);
|
|
if (pp == null) return;
|
|
|
|
ItemStack item = event.getItem();
|
|
if (item != null && item.getType() == Material.FIREWORK_ROCKET) {
|
|
if (physics.applyFireworkBoost(pp)) {
|
|
spawnBoostFirework(player);
|
|
item.setAmount(item.getAmount() - 1);
|
|
}
|
|
event.setCancelled(true);
|
|
}
|
|
}
|
|
|
|
private void startTasks() {
|
|
renderTask = Bukkit.getScheduler().runTaskTimer(LoParkour.getPlugin(), () -> {
|
|
for (ParkourPlayer player : getPlayers()) {
|
|
int currentIndex = playerRingIndex.getOrDefault(player, 0);
|
|
renderer.renderRings(rings, currentIndex);
|
|
}
|
|
}, 0L, 2L); // Every 2 ticks
|
|
}
|
|
|
|
private void cleanup() {
|
|
// Cancel and nullify tasks to prevent memory leaks
|
|
if (renderTask != null) {
|
|
renderTask.cancel();
|
|
renderTask = null;
|
|
}
|
|
if (physicsTask != null) {
|
|
physicsTask.cancel();
|
|
physicsTask = null;
|
|
}
|
|
|
|
// Unregister event listener to prevent memory leaks
|
|
HandlerList.unregisterAll(this);
|
|
|
|
// Clear all data structures
|
|
rings.forEach(ElytraRing::remove);
|
|
rings.clear();
|
|
playerRingIndex.clear();
|
|
|
|
for (ParkourPlayer player : getPlayers()) {
|
|
physics.cleanup(player.player.getUniqueId());
|
|
}
|
|
}
|
|
|
|
private void giveElytraAndFireworks(@NotNull ParkourPlayer player) {
|
|
Player p = player.player;
|
|
p.getInventory().setChestplate(new ItemStack(Material.ELYTRA));
|
|
p.getInventory().addItem(makeFirework(config.getStartingFireworks()));
|
|
}
|
|
|
|
private void spawnBoostFirework(@NotNull Player player) {
|
|
Firework firework = player.getWorld().spawn(player.getLocation(), Firework.class);
|
|
FireworkMeta meta = firework.getFireworkMeta();
|
|
meta.setPower(1);
|
|
firework.setFireworkMeta(meta);
|
|
firework.detonate();
|
|
}
|
|
|
|
@NotNull
|
|
private Vector calculateNextDirection(@NotNull ElytraRing lastRing) {
|
|
// Simple forward direction for now
|
|
return new Vector(1, 0, 0);
|
|
}
|
|
|
|
private void cleanupOldRings() {
|
|
// Remove rings that are far behind all players
|
|
// Implementation depends on specific requirements
|
|
}
|
|
|
|
@NotNull
|
|
private ItemStack makeFirework(int amount) {
|
|
ItemStack firework = new ItemStack(Material.FIREWORK_ROCKET, amount);
|
|
FireworkMeta meta = (FireworkMeta) firework.getItemMeta();
|
|
if (meta != null) {
|
|
meta.setPower(1);
|
|
firework.setItemMeta(meta);
|
|
}
|
|
return firework;
|
|
}
|
|
|
|
private ParkourPlayer findParkourPlayer(@NotNull Player player) {
|
|
return getPlayers().stream()
|
|
.filter(pp -> pp.player.equals(player))
|
|
.findFirst()
|
|
.orElse(null);
|
|
}
|
|
} |