feat(gameplay): implement hook sabotage, generator kick, jump listener, and boss bar UI
- Add HookSabotage system for survivors to disable hooks temporarily - Add GeneratorKick killer ability to regress generator progress - Add PlayerJumpListener to track survivor jump actions - Add BossBarManager for displaying game state and objectives - Update generator regression logic to support killer kick mechanic - Update KillerController to integrate generator kick ability - Update PlayerInteractListener to handle new interaction types - Refactor game phases to use BossBarManager for UI updates - Update .gitignore to exclude compiled bin/ directory - Simplify PLAN.md documentation for easier maintenance
This commit is contained in:
parent
3d6204cc83
commit
89dc9eb00d
78 changed files with 2293 additions and 1210 deletions
175
src/main/java/dev/loki/killer/GeneratorKick.java
Normal file
175
src/main/java/dev/loki/killer/GeneratorKick.java
Normal file
|
|
@ -0,0 +1,175 @@
|
|||
package dev.loki.killer;
|
||||
|
||||
import dev.loki.config.GameConfig;
|
||||
import dev.loki.objective.Generator;
|
||||
import dev.loki.objective.GeneratorManager;
|
||||
import dev.loki.player.Killer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Sound;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Killer generator kick: Killer holds Shift near a generator for
|
||||
* kick-charge-seconds (default 3), then the generator loses
|
||||
* kick-regression-percent (default 20%) progress.
|
||||
*
|
||||
* A shared cooldown of kick-cooldown-seconds applies to ALL generators.
|
||||
*/
|
||||
public class GeneratorKick {
|
||||
|
||||
private final Plugin plugin;
|
||||
private final GameConfig config;
|
||||
private final GeneratorManager generatorManager;
|
||||
|
||||
/** UUID → ongoing charging task */
|
||||
private final Map<UUID, BukkitTask> chargingTasks = new HashMap<>();
|
||||
|
||||
/** Shared cooldown across all generators: remaining ticks */
|
||||
private int globalCooldownTicks = 0;
|
||||
private BukkitTask cooldownTask;
|
||||
|
||||
public GeneratorKick(Plugin plugin, GameConfig config, GeneratorManager generatorManager) {
|
||||
this.plugin = plugin;
|
||||
this.config = config;
|
||||
this.generatorManager = generatorManager;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Public API — called when killer starts/stops sneaking near a generator
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
/** Call when killer starts holding Shift near a generator. */
|
||||
public boolean startCharging(Killer killer) {
|
||||
UUID uuid = killer.getUuid();
|
||||
if (chargingTasks.containsKey(uuid)) return false; // already charging
|
||||
if (globalCooldownTicks > 0) {
|
||||
Player p = killer.getPlayer();
|
||||
if (p != null) {
|
||||
int secsLeft = (int) Math.ceil(globalCooldownTicks / 20.0);
|
||||
p.sendMessage(Component.text("[DBD] Кулдаун удара: " + secsLeft + " сек", NamedTextColor.RED));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Player p = killer.getPlayer();
|
||||
if (p == null) return false;
|
||||
|
||||
Generator gen = generatorManager.getNearestGenerator(p.getLocation(),
|
||||
config.getGeneratorInteractionRange());
|
||||
if (gen == null || !gen.canBeRepaired()) return false;
|
||||
|
||||
int chargeTicks = config.getKickChargeSeconds() * 20;
|
||||
|
||||
BukkitTask task = new BukkitRunnable() {
|
||||
int elapsed = 0;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Player player = killer.getPlayer();
|
||||
if (player == null || !player.isOnline() || !player.isSneaking()) {
|
||||
stopCharging(killer.getUuid());
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Still near the same generator?
|
||||
Generator g = generatorManager.getNearestGenerator(player.getLocation(),
|
||||
config.getGeneratorInteractionRange());
|
||||
if (g == null || g != gen) {
|
||||
stopCharging(killer.getUuid());
|
||||
cancel();
|
||||
return;
|
||||
}
|
||||
|
||||
elapsed++;
|
||||
double pct = (double) elapsed / chargeTicks * 100;
|
||||
player.sendActionBar(Component.text("§c⚡ Разрушение: " + (int) pct + "%"));
|
||||
|
||||
if (elapsed >= chargeTicks) {
|
||||
executeKick(killer, gen);
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 1L, 1L);
|
||||
|
||||
chargingTasks.put(uuid, task);
|
||||
if (p.isOnline()) p.sendMessage(Component.text("[DBD] Держите Shift для разрушения генератора...", NamedTextColor.RED));
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Call when killer releases Shift or moves away. */
|
||||
public void stopCharging(UUID killerUuid) {
|
||||
BukkitTask task = chargingTasks.remove(killerUuid);
|
||||
if (task != null) {
|
||||
task.cancel();
|
||||
Player p = Bukkit.getPlayer(killerUuid);
|
||||
if (p != null && p.isOnline()) {
|
||||
p.sendActionBar(Component.empty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCharging(UUID killerUuid) {
|
||||
return chargingTasks.containsKey(killerUuid);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
for (BukkitTask t : chargingTasks.values()) t.cancel();
|
||||
chargingTasks.clear();
|
||||
if (cooldownTask != null) { cooldownTask.cancel(); cooldownTask = null; }
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Private
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void executeKick(Killer killer, Generator gen) {
|
||||
chargingTasks.remove(killer.getUuid());
|
||||
|
||||
// Remove progress
|
||||
double regPct = config.getKickRegressionPercent();
|
||||
double current = gen.getProgress();
|
||||
double newProg = Math.max(0, current - regPct);
|
||||
gen.setProgress(newProg);
|
||||
gen.setUnderRegression(true, config.getKickDuration() * 20);
|
||||
|
||||
Player p = killer.getPlayer();
|
||||
if (p != null && p.isOnline()) {
|
||||
p.sendActionBar(Component.empty());
|
||||
p.sendMessage(Component.text("[DBD] Генератор поврежден! -" + (int) regPct + "% прогресса", NamedTextColor.RED));
|
||||
p.getWorld().playSound(p.getLocation(), Sound.ENTITY_GENERIC_EXPLODE, 0.6f, 1.3f);
|
||||
}
|
||||
|
||||
// Notify all players nearby
|
||||
Bukkit.broadcast(Component.text("[DBD] ⚡ Маньяк разрушил генератор!", NamedTextColor.DARK_RED));
|
||||
|
||||
// Start global cooldown
|
||||
startGlobalCooldown();
|
||||
}
|
||||
|
||||
private void startGlobalCooldown() {
|
||||
globalCooldownTicks = config.getKickCooldownSeconds() * 20;
|
||||
if (cooldownTask != null) { cooldownTask.cancel(); }
|
||||
cooldownTask = new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (globalCooldownTicks > 0) {
|
||||
globalCooldownTicks -= 20;
|
||||
} else {
|
||||
globalCooldownTicks = 0;
|
||||
cancel();
|
||||
cooldownTask = null;
|
||||
}
|
||||
}
|
||||
}.runTaskTimer(plugin, 20L, 20L);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue