406 lines
No EOL
16 KiB
Java
406 lines
No EOL
16 KiB
Java
package dev.loki.phase;
|
||
|
||
import dev.loki.chest.ChestManager;
|
||
import dev.loki.chest.ChestOpening;
|
||
import dev.loki.config.GameConfig;
|
||
import dev.loki.core.GameSession;
|
||
import dev.loki.gate.ExitGate;
|
||
import dev.loki.gate.GateManager;
|
||
import dev.loki.gate.GateOpening;
|
||
import dev.loki.hook.HookManager;
|
||
import dev.loki.hook.HookRecovery;
|
||
import dev.loki.hook.HookRescue;
|
||
import dev.loki.hook.HookSabotage;
|
||
import dev.loki.hook.HookSacrifice;
|
||
import dev.loki.hook.HookStruggle;
|
||
import dev.loki.item.ChestLoot;
|
||
import dev.loki.killer.GeneratorKick;
|
||
import dev.loki.mdbd;
|
||
import dev.loki.minigames.GeneratorMinigame;
|
||
import dev.loki.minigames.generator.GuiRouletteMinigame;
|
||
import dev.loki.objective.GeneratorManager;
|
||
import dev.loki.player.BasePlayer;
|
||
import dev.loki.player.PlayerState;
|
||
import dev.loki.player.Survivor;
|
||
import dev.loki.ui.ActionBar;
|
||
import dev.loki.ui.TitleUI;
|
||
import net.kyori.adventure.text.Component;
|
||
import org.bukkit.Bukkit;
|
||
import org.bukkit.GameMode;
|
||
import org.bukkit.Location;
|
||
import org.bukkit.Material;
|
||
import org.bukkit.Sound;
|
||
import org.bukkit.block.Block;
|
||
import org.bukkit.entity.Player;
|
||
|
||
import java.util.ArrayList;
|
||
import java.util.List;
|
||
|
||
public class GameplayPhase implements Phase {
|
||
|
||
private final GeneratorManager generatorManager;
|
||
private int tickCounter;
|
||
private boolean shouldEnd;
|
||
private boolean allGensDone;
|
||
|
||
// Sub-handlers
|
||
private GameplayHookHandler hookHandler;
|
||
private GameplayWinConditions winConditions;
|
||
private GameplayDamageSystem damageSystem;
|
||
private GameplayUI ui;
|
||
private GameplayInteraction interaction;
|
||
|
||
// Managers
|
||
private HookManager hookManager;
|
||
private GateManager gateManager;
|
||
private GateOpening gateOpening;
|
||
private HookRescue hookRescue;
|
||
private HookSabotage hookSabotage;
|
||
private HookStruggle hookStruggle;
|
||
private TitleUI titleUI;
|
||
private ChestManager chestManager;
|
||
private ChestOpening chestOpening;
|
||
private GeneratorKick generatorKick;
|
||
private dev.loki.objective.GeneratorRepair generatorRepair;
|
||
private dev.loki.objective.GeneratorHologram generatorHologram;
|
||
private dev.loki.hook.HookHologram hookHologram;
|
||
private dev.loki.chest.ChestHologram chestHologram;
|
||
|
||
public GameplayPhase(GeneratorManager generatorManager) {
|
||
this.generatorManager = generatorManager;
|
||
}
|
||
|
||
// =========================================================================
|
||
// Phase lifecycle
|
||
// =========================================================================
|
||
|
||
@Override
|
||
public void enter(GameSession session) {
|
||
mdbd plugin = session.getPlugin();
|
||
GameConfig cfg = session.getConfig();
|
||
tickCounter = 0;
|
||
shouldEnd = false;
|
||
allGensDone = false;
|
||
winConditions = new GameplayWinConditions();
|
||
|
||
initManagers(session, plugin, cfg);
|
||
initSubHandlers(session, plugin, cfg);
|
||
initListeners(session, plugin);
|
||
|
||
// Устанавливаем ночь на всех мирах
|
||
for (var world : Bukkit.getWorlds()) {
|
||
world.setGameRule(org.bukkit.GameRule.DO_DAYLIGHT_CYCLE, false);
|
||
world.setTime(18000); // Полночь
|
||
world.setStorm(false);
|
||
}
|
||
|
||
Bukkit.broadcast(session.getPlugin().getConfigManager().getMessages().get("game.start"));
|
||
}
|
||
|
||
@Override
|
||
public void tick(GameSession session) {
|
||
tickCounter++;
|
||
generatorManager.tick();
|
||
if (generatorRepair != null) generatorRepair.tick();
|
||
if (interaction != null) interaction.tickSurvivors();
|
||
hookHandler.tick();
|
||
boolean timeExpired = ui.tickMatchTimer(session);
|
||
|
||
// Particle effects around generators (every 10 ticks)
|
||
if (tickCounter % 10 == 0 && generatorManager != null) {
|
||
for (var gen : generatorManager.getAllGenerators()) {
|
||
if (gen.isCompleted()) continue;
|
||
Location gloc = gen.getLocation().clone().add(0.5, 0.5, 0.5);
|
||
gloc.getWorld().spawnParticle(org.bukkit.Particle.ELECTRIC_SPARK,
|
||
gloc, 2, 0.8, 0.5, 0.8, 0.01);
|
||
gloc.getWorld().spawnParticle(org.bukkit.Particle.END_ROD,
|
||
gloc, 1, 0.6, 0.3, 0.6, 0.01);
|
||
}
|
||
}
|
||
|
||
if (tickCounter % 20 == 0) {
|
||
checkGeneratorProgress(session);
|
||
winConditions.checkWinConditions(session);
|
||
if (winConditions.isShouldEnd() || timeExpired) {
|
||
shouldEnd = true;
|
||
}
|
||
ui.updateSidebar(session, generatorManager);
|
||
}
|
||
|
||
if (timeExpired) {
|
||
showGameResults(session);
|
||
teleportAllToLobby(session);
|
||
session.stop();
|
||
return;
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public void exit(GameSession session) {
|
||
clearListeners();
|
||
clearManagers(session);
|
||
ui.clear(session);
|
||
ui.resetPlayers(session);
|
||
damageSystem.clear();
|
||
damageSystem.clearManagers();
|
||
|
||
Bukkit.broadcast(session.getPlugin().getConfigManager().getMessages().get("game.end"));
|
||
}
|
||
|
||
@Override
|
||
public boolean isComplete() {
|
||
return shouldEnd;
|
||
}
|
||
|
||
/**
|
||
* Returns the reason the gameplay phase ended, or null if not yet ended.
|
||
*/
|
||
public String getWinReason() {
|
||
return winConditions != null ? winConditions.getWinReason() : null;
|
||
}
|
||
|
||
// =========================================================================
|
||
// Init helpers
|
||
// =========================================================================
|
||
|
||
private void initManagers(GameSession session, mdbd plugin, GameConfig cfg) {
|
||
hookManager = new HookManager(plugin);
|
||
hookManager.loadHooks(plugin.getConfigManager().getLocations());
|
||
hookManager.setMaxHooks(cfg.getMaxHooks());
|
||
hookManager.startTicking();
|
||
|
||
gateManager = new GateManager(plugin.getConfigManager().getLocations());
|
||
gateOpening = new GateOpening(gateManager, cfg);
|
||
|
||
ActionBar ab = new ActionBar();
|
||
HookRecovery hookRecovery = new HookRecovery(plugin, cfg);
|
||
titleUI = new TitleUI(plugin);
|
||
|
||
String genMinigameType = cfg.getGeneratorMinigame();
|
||
GeneratorMinigame genMinigame;
|
||
if ("gui".equalsIgnoreCase(genMinigameType)) {
|
||
genMinigame = new GuiRouletteMinigame(plugin);
|
||
} else if ("text".equalsIgnoreCase(genMinigameType)) {
|
||
genMinigame = new dev.loki.minigames.generator.TextSkillCheckMinigame(plugin);
|
||
} else {
|
||
plugin.getLogger().warning("Unknown generator minigame: " + genMinigameType + ", using gui");
|
||
genMinigame = new GuiRouletteMinigame(plugin);
|
||
}
|
||
|
||
hookStruggle = new HookStruggle(plugin, titleUI, cfg);
|
||
HookSacrifice hookSacrifice = new HookSacrifice(plugin, null);
|
||
hookSabotage = new HookSabotage(plugin, cfg, hookManager);
|
||
|
||
hookRescue = new HookRescue(plugin, ab, cfg);
|
||
hookHandler = new GameplayHookHandler(hookManager, hookSacrifice, hookStruggle, hookRecovery);
|
||
hookRescue.setOnRescueComplete((rescuer, victim) -> hookHandler.onRescued(victim));
|
||
|
||
chestManager = new ChestManager(plugin, plugin.getConfigManager().getLocations(), cfg);
|
||
chestManager.startLootResetTimer();
|
||
chestOpening = new ChestOpening(plugin);
|
||
chestOpening.setChestLoot(new ChestLoot(cfg));
|
||
|
||
var gameWorld = plugin.getLobbyManager().getGameWorld();
|
||
if (gameWorld != null) {
|
||
for (var h : hookManager.getAllHooks()) h.getLocation().setWorld(gameWorld);
|
||
for (var c : chestManager.getAllChests()) c.getLocation().setWorld(gameWorld);
|
||
for (var g : generatorManager.getAllGenerators()) g.getLocation().setWorld(gameWorld);
|
||
}
|
||
|
||
spawnGameBlocks(plugin, cfg);
|
||
|
||
generatorKick = new GeneratorKick(plugin, cfg, generatorManager);
|
||
|
||
generatorRepair = new dev.loki.objective.GeneratorRepair(plugin, generatorManager, genMinigame);
|
||
// Голограммы ВСЕГДА включены
|
||
generatorHologram = new dev.loki.objective.GeneratorHologram(plugin, generatorManager);
|
||
generatorHologram.start();
|
||
plugin.getLogger().info("[GameplayPhase] Generator holograms started");
|
||
|
||
hookHologram = new dev.loki.hook.HookHologram(plugin, hookManager);
|
||
hookHologram.start();
|
||
plugin.getLogger().info("[GameplayPhase] Hook holograms: " + hookManager.getAllHooks().size());
|
||
|
||
chestHologram = new dev.loki.chest.ChestHologram(plugin, chestManager);
|
||
chestHologram.start();
|
||
plugin.getLogger().info("[GameplayPhase] Chest holograms: " + chestManager.getAllChests().size());
|
||
|
||
session.setHookManager(hookManager);
|
||
session.setGateManager(gateManager);
|
||
}
|
||
|
||
private void initSubHandlers(GameSession session, mdbd plugin, GameConfig cfg) {
|
||
damageSystem = new GameplayDamageSystem();
|
||
damageSystem.init(session, plugin, cfg);
|
||
|
||
ui = new GameplayUI();
|
||
ui.init(session, plugin, cfg);
|
||
ui.setWinConditions(winConditions);
|
||
|
||
interaction = new GameplayInteraction();
|
||
interaction.setMoveListener(plugin.getMoveListener());
|
||
interaction.setInteractListener(plugin.getInteractListener());
|
||
// ВАЖНО: переустановить generatorManager, поскольку он может быть null после exit()
|
||
plugin.getInteractListener().setGeneratorManager(generatorManager);
|
||
interaction.setManagers(gateManager, hookManager, hookRescue,
|
||
hookSabotage, generatorKick, damageSystem,
|
||
chestManager, chestOpening, gateOpening, generatorRepair);
|
||
}
|
||
|
||
private void initListeners(GameSession session, mdbd plugin) {
|
||
for (BasePlayer bp : session.getPlayers().values()) {
|
||
Player p = bp.getPlayer();
|
||
if (p != null && p.isOnline()) p.getInventory().clear();
|
||
}
|
||
|
||
GeneratorMinigame gm = generatorRepair.getMinigame();
|
||
if (gm instanceof GuiRouletteMinigame gui) {
|
||
dev.loki.listener.SkillCheckListener listener =
|
||
new dev.loki.listener.SkillCheckListener(plugin, gui);
|
||
listener.register();
|
||
}
|
||
|
||
interaction.wire(session);
|
||
}
|
||
|
||
// =========================================================================
|
||
// Tick helpers
|
||
// =========================================================================
|
||
|
||
private void checkGeneratorProgress(GameSession session) {
|
||
if (allGensDone) return;
|
||
if (generatorManager.areAllRequiredCompleted()) {
|
||
allGensDone = true;
|
||
interaction.setAllGensDone(true);
|
||
gateManager.activateEndgame();
|
||
Bukkit.broadcast(session.getPlugin().getConfigManager().getMessages().get("generator.repair.all-done"));
|
||
for (BasePlayer bp : session.getPlayers().values()) {
|
||
Player p = bp.getPlayer();
|
||
if (p != null) p.playSound(p.getLocation(), Sound.ENTITY_FIREWORK_ROCKET_BLAST, 1.0f, 1.0f);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void spawnGameBlocks(mdbd plugin, GameConfig cfg) {
|
||
// Спавн блоков на местах генераторов
|
||
Material genMat = Material.matchMaterial(cfg.getGeneratorBlock());
|
||
if (genMat == null) genMat = Material.BLAST_FURNACE;
|
||
for (var gen : generatorManager.getAllGenerators()) {
|
||
Location loc = gen.getLocation();
|
||
if (loc.getWorld() == null) continue;
|
||
Block b = loc.getBlock();
|
||
if (b.getType() != genMat) b.setType(genMat, false);
|
||
}
|
||
|
||
// Спавн блоков на местах сундуков + лут (resetAllChests уже устанавливает лут)
|
||
Material chestMat = Material.matchMaterial(cfg.getChestBlock());
|
||
if (chestMat == null) chestMat = Material.CHEST;
|
||
chestManager.resetAllChests();
|
||
// Убедимся, что у каждого сундука есть лут (не null и не AIR)
|
||
for (dev.loki.chest.Chest chest : chestManager.getAllChests()) {
|
||
Location loc = chest.getLocation();
|
||
if (loc.getWorld() == null) continue;
|
||
Block block = loc.getBlock();
|
||
if (block.getType() != chestMat) block.setType(chestMat, false);
|
||
if (!chest.hasLoot()) {
|
||
ChestLoot chestLoot = new ChestLoot(cfg);
|
||
chest.setLoot(chestLoot.rollLoot());
|
||
}
|
||
}
|
||
|
||
plugin.getLogger().info("[GameplayPhase] Spawned " + chestManager.getTotalChests() + " chests with loot");
|
||
|
||
// Спавн блоков на местах крюков
|
||
Material hookMat = Material.matchMaterial(cfg.getHookBlock());
|
||
if (hookMat == null) hookMat = Material.IRON_BARS;
|
||
for (var hook : hookManager.getAllHooks()) {
|
||
Location loc = hook.getLocation();
|
||
if (loc.getWorld() == null) continue;
|
||
Block b = loc.getBlock();
|
||
if (b.getType() != hookMat) b.setType(hookMat, false);
|
||
}
|
||
plugin.getLogger().info("[GameplayPhase] Spawned blocks for " + hookManager.getAllHooks().size() + " hooks");
|
||
|
||
// Спавн блоков на местах ворот (Hopper — для открытия через ПКМ)
|
||
for (ExitGate gate : gateManager.getAllGates()) {
|
||
gate.spawnGateBlock();
|
||
}
|
||
plugin.getLogger().info("[GameplayPhase] Spawned blocks for " + gateManager.getAllGates().size() + " gates");
|
||
}
|
||
|
||
// =========================================================================
|
||
// Cleanup
|
||
// =========================================================================
|
||
|
||
private void showGameResults(GameSession session) {
|
||
List<String> results = new ArrayList<>();
|
||
results.add("§6§l=== РЕЗУЛЬТАТЫ ===");
|
||
int escaped = 0, died = 0;
|
||
for (BasePlayer player : session.getPlayers().values()) {
|
||
if (player.isKiller()) {
|
||
Player p = player.getPlayer();
|
||
String name = p != null ? p.getName() : "Unknown";
|
||
results.add("§c☠ " + name + " — Маньяк");
|
||
continue;
|
||
}
|
||
Player p = player.getPlayer();
|
||
String name = p != null ? p.getName() : "Unknown";
|
||
if (player.getState() == PlayerState.ESCAPED) {
|
||
results.add("§a✔ " + name + " — Сбежал");
|
||
escaped++;
|
||
} else {
|
||
results.add("§c✘ " + name + " — Погиб");
|
||
died++;
|
||
}
|
||
}
|
||
results.add("§7Выживших: §a" + escaped + " §7/ Погибших: §c" + died);
|
||
String msg = String.join("\n", results);
|
||
for (BasePlayer player : session.getPlayers().values()) {
|
||
Player p = player.getPlayer();
|
||
if (p != null) p.sendMessage(msg);
|
||
}
|
||
}
|
||
|
||
private void teleportAllToLobby(GameSession session) {
|
||
for (BasePlayer bp : session.getPlayers().values()) {
|
||
Player p = bp.getPlayer();
|
||
if (p != null && p.isOnline()) {
|
||
p.setGameMode(org.bukkit.GameMode.ADVENTURE);
|
||
p.getInventory().clear();
|
||
p.setWalkSpeed(0.2f);
|
||
p.setFlySpeed(0.1f);
|
||
p.setHealth(20.0);
|
||
p.setFoodLevel(20);
|
||
session.getPlugin().getLobbyManager().teleportToLobby(p);
|
||
}
|
||
}
|
||
}
|
||
|
||
private void clearListeners() {
|
||
if (interaction != null) interaction.clear();
|
||
}
|
||
|
||
private void clearManagers(GameSession session) {
|
||
if (hookHandler != null) hookHandler.clear();
|
||
if (hookRescue != null) hookRescue.clear();
|
||
if (hookSabotage != null) hookSabotage.shutdown();
|
||
if (hookManager != null) hookManager.clear();
|
||
if (chestOpening != null) chestOpening.cancelAll();
|
||
if (chestManager != null) chestManager.resetAllChests();
|
||
if (generatorKick != null) generatorKick.shutdown();
|
||
if (generatorHologram != null) generatorHologram.stop();
|
||
if (hookHologram != null) hookHologram.stop();
|
||
if (titleUI != null) titleUI.clearSkillChecks();
|
||
if (hookStruggle != null) hookStruggle.clear();
|
||
if (chestManager != null) chestManager.stopLootResetTimer();
|
||
if (chestHologram != null) chestHologram.stop();
|
||
// Clean up gate blocks
|
||
if (gateManager != null) {
|
||
for (ExitGate gate : gateManager.getAllGates()) {
|
||
gate.removeGateBlock();
|
||
}
|
||
gateManager.resetAll();
|
||
}
|
||
session.getPlugin().getInteractListener().setGeneratorManager(null);
|
||
}
|
||
} |