feat: wire pallet/hook/gate/chest systems into gameplay
This commit is contained in:
parent
b339f4c029
commit
fcf2debbd4
4 changed files with 383 additions and 75 deletions
|
|
@ -1,8 +1,11 @@
|
||||||
package dev.loki.core;
|
package dev.loki.core;
|
||||||
|
|
||||||
import dev.loki.config.GameConfig;
|
import dev.loki.config.GameConfig;
|
||||||
|
import dev.loki.gate.GateManager;
|
||||||
|
import dev.loki.hook.HookManager;
|
||||||
import dev.loki.mdbd;
|
import dev.loki.mdbd;
|
||||||
import dev.loki.objective.GeneratorManager;
|
import dev.loki.objective.GeneratorManager;
|
||||||
|
import dev.loki.pallet.PalletManager;
|
||||||
import dev.loki.phase.EndgamePhase;
|
import dev.loki.phase.EndgamePhase;
|
||||||
import dev.loki.phase.GameplayPhase;
|
import dev.loki.phase.GameplayPhase;
|
||||||
import dev.loki.phase.LobbyPhase;
|
import dev.loki.phase.LobbyPhase;
|
||||||
|
|
@ -27,6 +30,11 @@ public class GameSession {
|
||||||
private RoleAssigner roleAssigner;
|
private RoleAssigner roleAssigner;
|
||||||
private GeneratorManager generatorManager;
|
private GeneratorManager generatorManager;
|
||||||
|
|
||||||
|
// Optional managers set by GameplayPhase
|
||||||
|
private PalletManager palletManager;
|
||||||
|
private HookManager hookManager;
|
||||||
|
private GateManager gateManager;
|
||||||
|
|
||||||
public GameSession(mdbd plugin) {
|
public GameSession(mdbd plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
this.eventBus = new GameEventBus();
|
this.eventBus = new GameEventBus();
|
||||||
|
|
@ -146,6 +154,19 @@ public class GameSession {
|
||||||
return generatorManager;
|
return generatorManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Gameplay-phase managers (set by GameplayPhase.enter())
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public PalletManager getPalletManager() { return palletManager; }
|
||||||
|
public void setPalletManager(PalletManager pm) { this.palletManager = pm; }
|
||||||
|
|
||||||
|
public HookManager getHookManager() { return hookManager; }
|
||||||
|
public void setHookManager(HookManager hm) { this.hookManager = hm; }
|
||||||
|
|
||||||
|
public GateManager getGateManager() { return gateManager; }
|
||||||
|
public void setGateManager(GateManager gm) { this.gateManager = gm; }
|
||||||
|
|
||||||
public void forceStart() {
|
public void forceStart() {
|
||||||
if (currentPhase instanceof LobbyPhase lobby) {
|
if (currentPhase instanceof LobbyPhase lobby) {
|
||||||
lobby.forceStart();
|
lobby.forceStart();
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import dev.loki.core.GameManager;
|
||||||
import dev.loki.data.GeneratorDataManager;
|
import dev.loki.data.GeneratorDataManager;
|
||||||
import dev.loki.listener.GeneratorSelectListener;
|
import dev.loki.listener.GeneratorSelectListener;
|
||||||
import dev.loki.listener.PlayerInteractListener;
|
import dev.loki.listener.PlayerInteractListener;
|
||||||
|
import dev.loki.listener.PlayerMoveListener;
|
||||||
import dev.loki.listener.PlayerQuitListener;
|
import dev.loki.listener.PlayerQuitListener;
|
||||||
import dev.loki.map.MapBuilder;
|
import dev.loki.map.MapBuilder;
|
||||||
import dev.loki.map.TemplateManager;
|
import dev.loki.map.TemplateManager;
|
||||||
|
|
@ -25,6 +26,8 @@ public final class mdbd extends JavaPlugin {
|
||||||
private DBDAdminCommand dbdAdminCommand;
|
private DBDAdminCommand dbdAdminCommand;
|
||||||
private SetupCommand setupCommand;
|
private SetupCommand setupCommand;
|
||||||
private LobbyManager lobbyManager;
|
private LobbyManager lobbyManager;
|
||||||
|
private PlayerInteractListener interactListener;
|
||||||
|
private PlayerMoveListener moveListener;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
|
@ -62,12 +65,17 @@ public final class mdbd extends JavaPlugin {
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(
|
getServer().getPluginManager().registerEvents(
|
||||||
new GeneratorSelectListener(dbdAdminCommand, generatorData), this);
|
new GeneratorSelectListener(dbdAdminCommand, generatorData), this);
|
||||||
|
getServer().getPluginManager().registerEvents(
|
||||||
|
new dev.loki.listener.SetupBlockBreakListener(setupCommand), this);
|
||||||
getServer().getPluginManager().registerEvents(
|
getServer().getPluginManager().registerEvents(
|
||||||
new PlayerQuitListener(this, gameManager), this);
|
new PlayerQuitListener(this, gameManager), this);
|
||||||
var interactListener = new PlayerInteractListener(this, gameManager, null);
|
this.interactListener = new PlayerInteractListener(this, gameManager, null);
|
||||||
interactListener.setSetupCommand(setupCommand);
|
interactListener.setSetupCommand(setupCommand);
|
||||||
getServer().getPluginManager().registerEvents(interactListener, this);
|
getServer().getPluginManager().registerEvents(interactListener, this);
|
||||||
|
|
||||||
|
this.moveListener = new PlayerMoveListener(this, gameManager);
|
||||||
|
getServer().getPluginManager().registerEvents(moveListener, this);
|
||||||
|
|
||||||
getLogger().info("Mdbd Plugin enabled successfully!");
|
getLogger().info("Mdbd Plugin enabled successfully!");
|
||||||
getLogger().info("Loaded " + templateManager.getAllTemplates().size() + " templates");
|
getLogger().info("Loaded " + templateManager.getAllTemplates().size() + " templates");
|
||||||
getLogger().info("Loaded " + generatorData.getCount() + " generators from data");
|
getLogger().info("Loaded " + generatorData.getCount() + " generators from data");
|
||||||
|
|
@ -120,6 +128,14 @@ public final class mdbd extends JavaPlugin {
|
||||||
return lobbyManager;
|
return lobbyManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PlayerInteractListener getInteractListener() {
|
||||||
|
return interactListener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PlayerMoveListener getMoveListener() {
|
||||||
|
return moveListener;
|
||||||
|
}
|
||||||
|
|
||||||
public void reloadConfigs() {
|
public void reloadConfigs() {
|
||||||
configManager.loadConfigs();
|
configManager.loadConfigs();
|
||||||
templateManager.loadAllTemplates();
|
templateManager.loadAllTemplates();
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,11 @@ package dev.loki.pallet;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.Material;
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.Particle;
|
||||||
|
import org.bukkit.Sound;
|
||||||
import org.bukkit.block.Block;
|
import org.bukkit.block.Block;
|
||||||
|
|
||||||
public class Pallet {
|
public class Pallet {
|
||||||
private final Location location;
|
|
||||||
private PalletState state;
|
|
||||||
private Block topBlock;
|
|
||||||
private Block bottomBlock;
|
|
||||||
|
|
||||||
public enum PalletState {
|
public enum PalletState {
|
||||||
STANDING,
|
STANDING,
|
||||||
|
|
@ -16,70 +14,111 @@ public class Pallet {
|
||||||
BROKEN
|
BROKEN
|
||||||
}
|
}
|
||||||
|
|
||||||
public Pallet(Location location) {
|
private final Location center; // The OAK_LOG position saved in locations.yml
|
||||||
this.location = location;
|
private Block leftBlock;
|
||||||
|
private Block centerBlock;
|
||||||
|
private Block rightBlock;
|
||||||
|
private PalletState state;
|
||||||
|
|
||||||
|
public Pallet(Location center) {
|
||||||
|
this.center = center.clone();
|
||||||
this.state = PalletState.STANDING;
|
this.state = PalletState.STANDING;
|
||||||
this.topBlock = location.clone().add(0, 1, 0).getBlock();
|
detectStructure();
|
||||||
this.bottomBlock = location.getBlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isPlanks(Block block) {
|
||||||
|
return block.getType() == Material.OAK_PLANKS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Detects which axis the PLP structure lies on (X or Z) by checking
|
||||||
|
* neighboring blocks. Falls back to X axis if neither looks right.
|
||||||
|
*/
|
||||||
|
private void detectStructure() {
|
||||||
|
Block c = center.getBlock();
|
||||||
|
centerBlock = c;
|
||||||
|
|
||||||
|
Block xLeft = center.clone().add(-1, 0, 0).getBlock();
|
||||||
|
Block xRight = center.clone().add( 1, 0, 0).getBlock();
|
||||||
|
Block zLeft = center.clone().add( 0, 0, -1).getBlock();
|
||||||
|
Block zRight = center.clone().add( 0, 0, 1).getBlock();
|
||||||
|
|
||||||
|
// Prefer the axis where both neighbours are OAK_PLANKS
|
||||||
|
boolean xPlpValid = isPlanks(xLeft) && isPlanks(xRight);
|
||||||
|
boolean zPlpValid = isPlanks(zLeft) && isPlanks(zRight);
|
||||||
|
|
||||||
|
if (xPlpValid) {
|
||||||
|
leftBlock = xLeft;
|
||||||
|
rightBlock = xRight;
|
||||||
|
} else if (zPlpValid) {
|
||||||
|
leftBlock = zLeft;
|
||||||
|
rightBlock = zRight;
|
||||||
|
} else {
|
||||||
|
// Fallback: use X axis regardless (blocks may have been loaded before
|
||||||
|
// the world is fully available; state ops will still work).
|
||||||
|
leftBlock = xLeft;
|
||||||
|
rightBlock = xRight;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// State transitions
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
public void setDropped() {
|
||||||
|
if (state != PalletState.STANDING) return;
|
||||||
|
state = PalletState.DROPPED;
|
||||||
|
|
||||||
|
// Replace the OAK_LOG center with OAK_PLANKS (sides stay as-is)
|
||||||
|
centerBlock.setType(Material.OAK_PLANKS);
|
||||||
|
|
||||||
|
// Sound + particles at center
|
||||||
|
Location loc = center.clone().add(0.5, 0.5, 0.5);
|
||||||
|
if (loc.getWorld() != null) {
|
||||||
|
loc.getWorld().playSound(loc, Sound.BLOCK_WOOD_BREAK, 1.0f, 0.7f);
|
||||||
|
loc.getWorld().spawnParticle(Particle.BLOCK, loc, 30, 0.5, 0.3, 0.5, 0.1,
|
||||||
|
Material.OAK_PLANKS.createBlockData());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBroken() {
|
||||||
|
state = PalletState.BROKEN;
|
||||||
|
leftBlock.setType(Material.AIR);
|
||||||
|
centerBlock.setType(Material.AIR);
|
||||||
|
rightBlock.setType(Material.AIR);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
state = PalletState.STANDING;
|
||||||
|
// Restore PLP structure
|
||||||
|
leftBlock.setType(Material.OAK_PLANKS);
|
||||||
|
centerBlock.setType(Material.OAK_LOG);
|
||||||
|
rightBlock.setType(Material.OAK_PLANKS);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Accessors
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
public Location getLocation() {
|
public Location getLocation() {
|
||||||
return location;
|
return center.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Centre of the pallet block for distance calculations. */
|
||||||
|
public Location getCenter() {
|
||||||
|
return center.clone().add(0.5, 0.5, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PalletState getState() {
|
public PalletState getState() {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isStanding() {
|
public boolean isStanding() { return state == PalletState.STANDING; }
|
||||||
return state == PalletState.STANDING;
|
public boolean isDropped() { return state == PalletState.DROPPED; }
|
||||||
}
|
public boolean isBroken() { return state == PalletState.BROKEN; }
|
||||||
|
|
||||||
public boolean isDropped() {
|
|
||||||
return state == PalletState.DROPPED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isBroken() {
|
|
||||||
return state == PalletState.BROKEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDropped() {
|
|
||||||
this.state = PalletState.DROPPED;
|
|
||||||
updateVisuals();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBroken() {
|
|
||||||
this.state = PalletState.BROKEN;
|
|
||||||
clearBlocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
this.state = PalletState.STANDING;
|
|
||||||
restoreBlocks();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateVisuals() {
|
|
||||||
if (state == PalletState.DROPPED) {
|
|
||||||
bottomBlock.setType(Material.OAK_PLANKS);
|
|
||||||
topBlock.setType(Material.OAK_PLANKS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void clearBlocks() {
|
|
||||||
bottomBlock.setType(Material.AIR);
|
|
||||||
topBlock.setType(Material.AIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restoreBlocks() {
|
|
||||||
bottomBlock.setType(Material.OAK_FENCE);
|
|
||||||
topBlock.setType(Material.AIR);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Location getCenter() {
|
|
||||||
return location.clone().add(0.5, 0.5, 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
public double distanceTo(Location loc) {
|
public double distanceTo(Location loc) {
|
||||||
|
if (!getCenter().getWorld().equals(loc.getWorld())) return Double.MAX_VALUE;
|
||||||
return getCenter().distance(loc);
|
return getCenter().distance(loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,29 @@
|
||||||
package dev.loki.phase;
|
package dev.loki.phase;
|
||||||
|
|
||||||
|
import dev.loki.chest.ChestManager;
|
||||||
|
import dev.loki.chest.ChestOpening;
|
||||||
import dev.loki.core.GameSession;
|
import dev.loki.core.GameSession;
|
||||||
|
import dev.loki.gate.ExitGate;
|
||||||
|
import dev.loki.gate.GateManager;
|
||||||
|
import dev.loki.hook.Hook;
|
||||||
|
import dev.loki.hook.HookManager;
|
||||||
|
import dev.loki.hook.HookRescue;
|
||||||
|
import dev.loki.hook.HookSacrifice;
|
||||||
|
import dev.loki.hook.HookStage;
|
||||||
|
import dev.loki.listener.PlayerInteractListener;
|
||||||
|
import dev.loki.listener.PlayerMoveListener;
|
||||||
|
import dev.loki.mdbd;
|
||||||
import dev.loki.objective.GeneratorManager;
|
import dev.loki.objective.GeneratorManager;
|
||||||
|
import dev.loki.pallet.Pallet;
|
||||||
|
import dev.loki.pallet.PalletBreak;
|
||||||
|
import dev.loki.pallet.PalletDrop;
|
||||||
|
import dev.loki.pallet.PalletManager;
|
||||||
|
import dev.loki.pallet.PalletStun;
|
||||||
import dev.loki.player.BasePlayer;
|
import dev.loki.player.BasePlayer;
|
||||||
|
import dev.loki.player.Killer;
|
||||||
import dev.loki.player.PlayerState;
|
import dev.loki.player.PlayerState;
|
||||||
|
import dev.loki.player.Survivor;
|
||||||
|
import dev.loki.ui.ActionBar;
|
||||||
import net.kyori.adventure.text.Component;
|
import net.kyori.adventure.text.Component;
|
||||||
import net.kyori.adventure.text.format.NamedTextColor;
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
@ -11,23 +31,71 @@ import org.bukkit.Sound;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
||||||
public class GameplayPhase implements Phase {
|
public class GameplayPhase implements Phase {
|
||||||
|
|
||||||
private final GeneratorManager generatorManager;
|
private final GeneratorManager generatorManager;
|
||||||
private int tickCounter;
|
private int tickCounter;
|
||||||
private boolean shouldEnd;
|
private boolean shouldEnd;
|
||||||
private boolean allGensDone;
|
private boolean allGensDone;
|
||||||
|
|
||||||
|
// Managers initialised in enter()
|
||||||
|
private PalletManager palletManager;
|
||||||
|
private HookManager hookManager;
|
||||||
|
private GateManager gateManager;
|
||||||
|
private HookRescue hookRescue;
|
||||||
|
private HookSacrifice hookSacrifice;
|
||||||
|
private PalletDrop palletDrop;
|
||||||
|
private PalletBreak palletBreak;
|
||||||
|
private PalletStun palletStun;
|
||||||
|
private ChestManager chestManager;
|
||||||
|
private ChestOpening chestOpening;
|
||||||
|
|
||||||
|
// Listener references grabbed from plugin
|
||||||
|
private PlayerMoveListener moveListener;
|
||||||
|
private PlayerInteractListener interactListener;
|
||||||
|
|
||||||
public GameplayPhase(GeneratorManager generatorManager) {
|
public GameplayPhase(GeneratorManager generatorManager) {
|
||||||
this.generatorManager = generatorManager;
|
this.generatorManager = generatorManager;
|
||||||
this.tickCounter = 0;
|
|
||||||
this.shouldEnd = false;
|
|
||||||
this.allGensDone = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Phase lifecycle
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void enter(GameSession session) {
|
public void enter(GameSession session) {
|
||||||
Bukkit.broadcast(Component.text("[DBD] Игра началась! Выжившие, чините генераторы!", NamedTextColor.GREEN));
|
mdbd plugin = session.getPlugin();
|
||||||
tickCounter = 0;
|
tickCounter = 0;
|
||||||
// Teleport is handled by RoleAssigner.assignRoles — no double-teleport needed here
|
shouldEnd = false;
|
||||||
|
allGensDone = false;
|
||||||
|
|
||||||
|
// --- Initialise managers ---
|
||||||
|
palletManager = new PalletManager(plugin.getConfigManager().getLocations());
|
||||||
|
hookManager = new HookManager(plugin);
|
||||||
|
hookManager.loadHooks(plugin.getConfigManager().getLocations());
|
||||||
|
gateManager = new GateManager(plugin.getConfigManager().getLocations());
|
||||||
|
ActionBar ab = new ActionBar();
|
||||||
|
hookRescue = new HookRescue(plugin, ab);
|
||||||
|
hookSacrifice = new HookSacrifice(plugin, null);
|
||||||
|
palletDrop = new PalletDrop(palletManager);
|
||||||
|
palletBreak = new PalletBreak(session.getConfig());
|
||||||
|
palletStun = new PalletStun(session.getConfig());
|
||||||
|
chestManager = new ChestManager(plugin, plugin.getConfigManager().getLocations());
|
||||||
|
chestOpening = new ChestOpening(plugin);
|
||||||
|
|
||||||
|
// Store managers on session so other code can reach them
|
||||||
|
session.setPalletManager(palletManager);
|
||||||
|
session.setHookManager(hookManager);
|
||||||
|
session.setGateManager(gateManager);
|
||||||
|
|
||||||
|
// --- Wire listeners ---
|
||||||
|
moveListener = plugin.getMoveListener();
|
||||||
|
interactListener = plugin.getInteractListener();
|
||||||
|
|
||||||
|
wireMoveListener(session);
|
||||||
|
wireInteractListener(session);
|
||||||
|
|
||||||
|
Bukkit.broadcast(Component.text("[DBD] Игра началась! Выжившие, чините генераторы!",
|
||||||
|
NamedTextColor.GREEN));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -35,19 +103,92 @@ public class GameplayPhase implements Phase {
|
||||||
tickCounter++;
|
tickCounter++;
|
||||||
generatorManager.tick();
|
generatorManager.tick();
|
||||||
|
|
||||||
|
// Tick hook timers every game tick
|
||||||
|
tickHooks(session);
|
||||||
|
|
||||||
if (tickCounter % 20 == 0) {
|
if (tickCounter % 20 == 0) {
|
||||||
checkGeneratorProgress(session);
|
checkGeneratorProgress(session);
|
||||||
checkWinConditions(session);
|
checkWinConditions(session);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void exit(GameSession session) {
|
||||||
|
// Clean up listeners
|
||||||
|
if (moveListener != null) {
|
||||||
|
moveListener.setOnSurvivorMove(null);
|
||||||
|
}
|
||||||
|
if (interactListener != null) {
|
||||||
|
interactListener.setOnPalletInteract(null);
|
||||||
|
interactListener.setOnRescueAttempt(null);
|
||||||
|
interactListener.setOnChestInteract(null);
|
||||||
|
interactListener.setOnAttack(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop hooks
|
||||||
|
if (hookManager != null) {
|
||||||
|
hookManager.clear();
|
||||||
|
}
|
||||||
|
if (hookRescue != null) {
|
||||||
|
hookRescue.clear();
|
||||||
|
}
|
||||||
|
if (chestOpening != null) {
|
||||||
|
chestOpening.cancelAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Bukkit.broadcast(Component.text("[DBD] Игровая фаза завершена!", NamedTextColor.GOLD));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isComplete() {
|
||||||
|
return shouldEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Hook ticking
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void tickHooks(GameSession session) {
|
||||||
|
for (Hook hook : hookManager.getAllHooks()) {
|
||||||
|
if (!hook.isOccupied()) continue;
|
||||||
|
|
||||||
|
hook.tick();
|
||||||
|
|
||||||
|
Survivor victim = hook.getHookedSurvivor();
|
||||||
|
if (victim == null) continue;
|
||||||
|
|
||||||
|
// Show action bar progress to the hooked survivor
|
||||||
|
Player vp = victim.getPlayer();
|
||||||
|
if (vp != null && vp.isOnline()) {
|
||||||
|
// ActionBar shows stage progress
|
||||||
|
double pct = hook.getStageProgressPercent();
|
||||||
|
String label = hook.getCurrentStage() == HookStage.STAGE_2
|
||||||
|
? "§cStage 2 - struggle!"
|
||||||
|
: "§eOn hook";
|
||||||
|
vp.sendActionBar(Component.text(label + " " + (int) pct + "%"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sacrifice when timer expires
|
||||||
|
if (hook.getCurrentStage() == HookStage.SACRIFICE) {
|
||||||
|
hookSacrifice.sacrificeIfExpired(victim, hook);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Generator / win-condition checks
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
private void checkGeneratorProgress(GameSession session) {
|
private void checkGeneratorProgress(GameSession session) {
|
||||||
if (allGensDone) return;
|
if (allGensDone) return;
|
||||||
|
|
||||||
if (generatorManager.areAllRequiredCompleted()) {
|
if (generatorManager.areAllRequiredCompleted()) {
|
||||||
allGensDone = true;
|
allGensDone = true;
|
||||||
|
gateManager.activateEndgame();
|
||||||
|
|
||||||
Bukkit.broadcast(Component.text(
|
Bukkit.broadcast(Component.text(
|
||||||
"[DBD] Все генераторы починены! Открывайте ворота!", NamedTextColor.GREEN));
|
"[DBD] Все генераторы починены! Открывайте ворота!", NamedTextColor.GREEN));
|
||||||
|
|
||||||
for (BasePlayer bp : session.getPlayers().values()) {
|
for (BasePlayer bp : session.getPlayers().values()) {
|
||||||
Player p = bp.getPlayer();
|
Player p = bp.getPlayer();
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
|
|
@ -65,29 +206,120 @@ public class GameplayPhase implements Phase {
|
||||||
for (BasePlayer bp : session.getPlayers().values()) {
|
for (BasePlayer bp : session.getPlayers().values()) {
|
||||||
if (bp.isKiller()) continue;
|
if (bp.isKiller()) continue;
|
||||||
totalSurvivors++;
|
totalSurvivors++;
|
||||||
PlayerState state = bp.getState();
|
PlayerState st = bp.getState();
|
||||||
if (state == PlayerState.ESCAPED) {
|
if (st == PlayerState.ESCAPED) {
|
||||||
escapedSurvivors++;
|
escapedSurvivors++;
|
||||||
} else if (state != PlayerState.DEAD && state != PlayerState.HOOKED) {
|
} else if (st != PlayerState.DEAD && st != PlayerState.HOOKED) {
|
||||||
aliveSurvivors++;
|
aliveSurvivors++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean allSurvivorsDead = (aliveSurvivors == 0 && escapedSurvivors == 0 && totalSurvivors > 0);
|
boolean allDead = (aliveSurvivors == 0 && escapedSurvivors == 0 && totalSurvivors > 0);
|
||||||
boolean allEscaped = (escapedSurvivors == totalSurvivors && totalSurvivors > 0);
|
boolean allEscaped = (escapedSurvivors == totalSurvivors && totalSurvivors > 0);
|
||||||
|
|
||||||
if (allSurvivorsDead || allEscaped) {
|
if (allDead || allEscaped) {
|
||||||
shouldEnd = true;
|
shouldEnd = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
// -------------------------------------------------------------------------
|
||||||
public void exit(GameSession session) {
|
// Listener wiring
|
||||||
Bukkit.broadcast(Component.text("[DBD] Игровая фаза завершена!", NamedTextColor.GOLD));
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void wireMoveListener(GameSession session) {
|
||||||
|
if (moveListener == null) return;
|
||||||
|
|
||||||
|
// Gate escape: survivor walks into open gate zone after all gens done
|
||||||
|
moveListener.setOnSurvivorMove((survivor, loc) -> {
|
||||||
|
if (!allGensDone) return;
|
||||||
|
if (survivor.getState() == PlayerState.ESCAPED) return;
|
||||||
|
|
||||||
|
ExitGate gate = gateManager.getGateAt(loc, 3.0);
|
||||||
|
if (gate != null && gate.isOpen()) {
|
||||||
|
survivor.setState(PlayerState.ESCAPED);
|
||||||
|
Player p = survivor.getPlayer();
|
||||||
|
if (p != null) {
|
||||||
|
p.playSound(loc, Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.2f);
|
||||||
|
Bukkit.broadcast(Component.text(
|
||||||
|
"[DBD] " + p.getName() + " сбежал через ворота!",
|
||||||
|
NamedTextColor.GOLD));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void wireInteractListener(GameSession session) {
|
||||||
public boolean isComplete() {
|
if (interactListener == null) return;
|
||||||
return shouldEnd;
|
|
||||||
|
// Pallet interact: survivor right-click = drop, killer left-click = break
|
||||||
|
interactListener.setOnPalletInteract((player, isRightClick) -> {
|
||||||
|
BasePlayer bp = session.getPlayer(player.getUniqueId());
|
||||||
|
if (bp == null) return;
|
||||||
|
|
||||||
|
if (isRightClick && !bp.isKiller()) {
|
||||||
|
palletDrop.tryDrop((Survivor) bp);
|
||||||
|
|
||||||
|
} else if (!isRightClick && bp.isKiller()) {
|
||||||
|
Killer killer = (Killer) bp;
|
||||||
|
Pallet nearby = palletManager.getPalletAt(player.getLocation(), 3.0);
|
||||||
|
if (nearby != null && nearby.isDropped()) {
|
||||||
|
palletBreak.startBreaking(killer, nearby, session.getPlugin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Rescue attempt: survivor right-clicks near a hook
|
||||||
|
interactListener.setOnRescueAttempt((player, ignored) -> {
|
||||||
|
BasePlayer bp = session.getPlayer(player.getUniqueId());
|
||||||
|
if (bp == null || bp.isKiller()) return;
|
||||||
|
Survivor rescuer = (Survivor) bp;
|
||||||
|
if (rescuer.getState() == PlayerState.HOOKED) return;
|
||||||
|
|
||||||
|
Hook hook = hookManager.findNearestOccupiedHook(player.getLocation(), 3.0);
|
||||||
|
if (hook != null) {
|
||||||
|
hookRescue.startRescue(rescuer, hook);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Killer right-click: hook a carried survivor
|
||||||
|
interactListener.setOnAttack((player, isRightClick) -> {
|
||||||
|
if (!isRightClick) return; // left-click handled by damage system
|
||||||
|
BasePlayer bp = session.getPlayer(player.getUniqueId());
|
||||||
|
if (bp == null || !bp.isKiller()) return;
|
||||||
|
Killer killer = (Killer) bp;
|
||||||
|
|
||||||
|
if (killer.getState() == PlayerState.CARRIED || killer.isCarrying()) {
|
||||||
|
// Find the carried survivor
|
||||||
|
for (BasePlayer other : session.getPlayers().values()) {
|
||||||
|
if (!other.isKiller() && other.getState() == PlayerState.CARRIED) {
|
||||||
|
Survivor carried = (Survivor) other;
|
||||||
|
if (hookManager.tryHookSurvivor(carried, player.getLocation())) {
|
||||||
|
killer.setCarrying(false);
|
||||||
|
// Teleport survivor to hook
|
||||||
|
Hook nearestHook = hookManager.findNearestOccupiedHook(
|
||||||
|
player.getLocation(), 5.0);
|
||||||
|
if (nearestHook != null) {
|
||||||
|
Player vp = carried.getPlayer();
|
||||||
|
if (vp != null) {
|
||||||
|
vp.teleport(nearestHook.getLocation());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Chest interact
|
||||||
|
interactListener.setOnChestInteract((player, ignored) -> {
|
||||||
|
BasePlayer bp = session.getPlayer(player.getUniqueId());
|
||||||
|
if (bp == null || bp.isKiller()) return;
|
||||||
|
|
||||||
|
var chest = chestManager.getNearestChest(player.getLocation(), 3.0);
|
||||||
|
if (chest != null) {
|
||||||
|
chestOpening.startOpening(player, chest);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue