remove: hatch (люк) and windows (окна) from code and config

This commit is contained in:
loki5512344 2026-06-15 12:45:56 +02:00
parent be86fe5960
commit bb293c5d03
15 changed files with 57 additions and 458 deletions

View file

@ -109,10 +109,6 @@ public class GameConfig {
setDefault("colors.progress-bar-incomplete", "&#FF0000");
setDefault("colors.progress-bar-background", "&#333333");
// Hatch
setDefault("hatch.min-survivors-for-open", 1);
setDefault("hatch.require-more-gens-than-survivors", true);
// Block types for spawning
setDefault("blocks.generator", "BLAST_FURNACE");
setDefault("blocks.chest", "CHEST");

View file

@ -34,9 +34,6 @@ public class LocationsConfig {
if (!config.contains("hooks") || config.getList("hooks") == null) {
config.set("hooks", new ArrayList<>());
}
if (!config.contains("windows") || config.getList("windows") == null) {
config.set("windows", new ArrayList<>());
}
if (!config.contains("chests") || config.getList("chests") == null) {
config.set("chests", new ArrayList<>());
}
@ -79,10 +76,6 @@ public class LocationsConfig {
return loadLocationList("hooks");
}
public List<Location> getWindows() {
return loadLocationList("windows");
}
public List<Location> getChests() {
return loadLocationList("chests");
}

View file

@ -115,7 +115,7 @@ public class GameSession {
if (currentPhase instanceof GameplayPhase gp) {
// Всегда переходим в EndgamePhase для корректного завершения игры
// (очистка скорборда, телепорт в лобби, показ результатов)
return new EndgamePhase(gateManager, null, null);
return new EndgamePhase(gateManager);
}
return null;
}

View file

@ -60,6 +60,7 @@ public class ExitGate {
World world = location.getWorld();
if (world == null) return;
// Place all iron bars first (no physics) then force update for connections
if (hasZone) {
for (int x = zoneMinX; x <= zoneMaxX; x++) {
for (int y = zoneMinY; y <= zoneMaxY; y++) {
@ -68,6 +69,14 @@ public class ExitGate {
}
}
}
// Force update all iron bars so they connect to neighbors
for (int x = zoneMinX; x <= zoneMaxX; x++) {
for (int y = zoneMinY; y <= zoneMaxY; y++) {
for (int z = zoneMinZ; z <= zoneMaxZ; z++) {
world.getBlockAt(x, y, z).getState().update(true, true);
}
}
}
} else {
// Fallback: 3x3x3 zone around gate location
int cx = location.getBlockX(), cy = location.getBlockY(), cz = location.getBlockZ();
@ -78,6 +87,13 @@ public class ExitGate {
}
}
}
for (int x = cx - 1; x <= cx + 1; x++) {
for (int y = cy; y <= cy + 2; y++) {
for (int z = cz - 1; z <= cz + 1; z++) {
world.getBlockAt(x, y, z).getState().update(true, true);
}
}
}
}
// Button at gate center + (1, 1, 1)

View file

@ -1,125 +0,0 @@
package dev.loki.hatch;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.block.Block;
public class Hatch {
private final Location location;
private HatchState state;
private Block hatchBlock;
public enum HatchState {
HIDDEN,
CLOSED,
OPEN
}
public Hatch(Location location) {
this.location = location;
this.state = HatchState.HIDDEN;
this.hatchBlock = location.getBlock();
}
public Location getLocation() {
return location;
}
public Location getJumpTarget() {
return location.clone().add(0.5, -2, 0.5);
}
public HatchState getState() {
return state;
}
public boolean isHidden() {
return state == HatchState.HIDDEN;
}
public boolean isClosed() {
return state == HatchState.CLOSED;
}
public boolean isOpen() {
return state == HatchState.OPEN;
}
public void reveal() {
if (state == HatchState.HIDDEN) {
state = HatchState.CLOSED;
updateVisuals();
playRevealEffect();
}
}
public void open() {
if (state == HatchState.CLOSED) {
state = HatchState.OPEN;
updateVisuals();
playOpenEffect();
}
}
public void hide() {
state = HatchState.HIDDEN;
clearVisuals();
}
public void reset() {
state = HatchState.HIDDEN;
clearVisuals();
}
private void updateVisuals() {
switch (state) {
case CLOSED -> hatchBlock.setType(Material.IRON_TRAPDOOR);
case OPEN -> hatchBlock.setType(Material.AIR);
default -> hatchBlock.setType(Material.AIR);
}
}
private void clearVisuals() {
hatchBlock.setType(Material.AIR);
}
public boolean isInRange(Location playerLoc, double radius) {
return location.distance(playerLoc) <= radius;
}
public boolean canEscape(Location playerLoc) {
return isOpen() && isInRange(playerLoc, 1.5);
}
private void playRevealEffect() {
World world = location.getWorld();
if (world == null) return;
world.playSound(location, Sound.BLOCK_METAL_PLACE, 1.0f, 0.8f);
world.spawnParticle(org.bukkit.Particle.SMOKE,
location.clone().add(0.5, 0.5, 0.5),
30, 0.3, 0.3, 0.3, 0.05);
}
private void playOpenEffect() {
World world = location.getWorld();
if (world == null) return;
world.playSound(location, Sound.BLOCK_IRON_TRAPDOOR_OPEN, 1.0f, 1.0f);
world.spawnParticle(org.bukkit.Particle.END_ROD,
location.clone().add(0.5, 0.5, 0.5),
20, 0.2, 0.2, 0.2, 0.02);
}
public void playEscapeEffect() {
World world = location.getWorld();
if (world == null) return;
world.playSound(location, Sound.ENTITY_PLAYER_SPLASH, 1.0f, 1.0f);
world.spawnParticle(org.bukkit.Particle.PORTAL,
location.clone().add(0.5, 0, 0.5),
100, 0.5, 0.5, 0.5, 0.5);
}
}

View file

@ -1,76 +0,0 @@
package dev.loki.hatch;
import dev.loki.player.BasePlayer;
import dev.loki.player.PlayerState;
import java.util.Collection;
public class HatchCondition {
private final int minSurvivorsForOpen;
public HatchCondition(int minSurvivorsForOpen) {
this.minSurvivorsForOpen = minSurvivorsForOpen;
}
public boolean shouldSpawn(Collection<BasePlayer> players, int repairedGenerators) {
int aliveSurvivors = countAliveSurvivors(players);
return aliveSurvivors <= repairedGenerators;
}
public boolean shouldOpen(Hatch hatch, Collection<BasePlayer> players, int repairedGenerators) {
if (hatch.isOpen()) return false;
if (!hatch.isClosed() && !hatch.isHidden()) return false;
return shouldSpawn(players, repairedGenerators);
}
public boolean canOpen(Hatch hatch, Collection<BasePlayer> players, int repairedGenerators) {
if (!hatch.isHidden() && !hatch.isClosed()) return false;
int aliveSurvivors = countAliveSurvivors(players);
int requiredGens = Math.max(minSurvivorsForOpen, aliveSurvivors);
return repairedGenerators >= requiredGens;
}
public int countAliveSurvivors(Collection<BasePlayer> players) {
return (int) players.stream()
.filter(p -> !p.isKiller())
.filter(this::isAlive)
.count();
}
private boolean isAlive(BasePlayer player) {
PlayerState state = player.getState();
return state != PlayerState.DEAD
&& state != PlayerState.ESCAPED
&& state != PlayerState.HOOKED;
}
public int getRemainingAlive(Collection<BasePlayer> players) {
return countAliveSurvivors(players);
}
public boolean isLastSurvivor(Collection<BasePlayer> players) {
return countAliveSurvivors(players) == 1;
}
public boolean checkAndReveal(Hatch hatch, Collection<BasePlayer> players, int repairedGenerators) {
if (hatch.isHidden() && shouldSpawn(players, repairedGenerators)) {
hatch.reveal();
return true;
}
return false;
}
public boolean checkAndOpen(Hatch hatch, Collection<BasePlayer> players, int repairedGenerators) {
if (canOpen(hatch, players, repairedGenerators)) {
if (hatch.isHidden()) {
hatch.reveal();
}
hatch.open();
return true;
}
return false;
}
}

View file

@ -1,117 +0,0 @@
package dev.loki.hatch;
import dev.loki.config.MessagesConfig;
import dev.loki.mdbd;
import dev.loki.player.PlayerState;
import dev.loki.player.Survivor;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.World;
import org.bukkit.entity.Player;
public class HatchEscape {
private static final double ESCAPE_RADIUS = 1.5;
private final Hatch hatch;
private final MessagesConfig messages;
public HatchEscape(Hatch hatch) {
this.hatch = hatch;
this.messages = mdbd.getInstance().getConfigManager().getMessages();
}
public boolean tryEscape(Survivor survivor) {
if (!hatch.isOpen()) {
return tryRevealAndCheck(survivor);
}
Player player = survivor.getPlayer();
if (player == null) return false;
if (!isAtHatch(player.getLocation())) {
player.sendMessage(messages.get("hatch.too-far"));
return false;
}
performEscape(survivor);
return true;
}
public boolean canEscape(Survivor survivor) {
if (!hatch.isOpen()) return false;
Player player = survivor.getPlayer();
if (player == null) return false;
return isAtHatch(player.getLocation());
}
private boolean tryRevealAndCheck(Survivor survivor) {
Player player = survivor.getPlayer();
if (player == null) return false;
if (hatch.isHidden()) {
player.sendMessage(messages.get("hatch.hidden"));
return false;
}
if (hatch.isClosed()) {
player.sendMessage(messages.get("hatch.closed"));
return false;
}
return false;
}
private boolean isAtHatch(Location location) {
return hatch.isInRange(location, ESCAPE_RADIUS);
}
private void performEscape(Survivor survivor) {
Player player = survivor.getPlayer();
if (player == null) return;
player.teleport(hatch.getJumpTarget());
hatch.playEscapeEffect();
player.getWorld().playSound(player.getLocation(),
Sound.ENTITY_PLAYER_LEVELUP, 1.0f, 1.0f);
player.sendTitle(
messages.getString("hatch.escape-title"),
messages.getString("hatch.escape-subtitle"),
10, 60, 10);
survivor.setState(PlayerState.ESCAPED);
giveEscapeReward(survivor);
}
private void giveEscapeReward(Survivor survivor) {
Player player = survivor.getPlayer();
if (player == null) return;
player.sendMessage(messages.get("hatch.reward"));
}
public void forceOpenForEscape() {
if (hatch.isHidden()) {
hatch.reveal();
}
if (!hatch.isOpen()) {
hatch.open();
}
}
public void notifyNearbyPlayers() {
Location hatchLoc = hatch.getLocation();
World world = hatchLoc.getWorld();
if (world == null) return;
for (Player player : world.getPlayers()) {
if (player.getLocation().distance(hatchLoc) < 30) {
player.sendMessage(messages.get("hatch.open-notify"));
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);
}
}
}
}

View file

@ -32,7 +32,7 @@ public class PlayerInteractListener implements Listener {
private BiConsumer<Player, Boolean> onAttack;
private BiConsumer<Player, Boolean> onRescueAttempt;
private BiConsumer<Player, Boolean> onChestInteract;
private BiConsumer<Player, Boolean> onHatchInteract;
private BiConsumer<Player, Boolean> onGateInteract;
/** Called when killer toggles sneak: (player, isSneaking) */
private BiConsumer<Player, Boolean> onKillerSneak;
/** Called when a survivor sneaks near another injured/downed survivor: (healer, target) */
@ -56,7 +56,7 @@ public class PlayerInteractListener implements Listener {
public void setOnAttack(BiConsumer<Player, Boolean> h) { this.onAttack = h; }
public void setOnRescueAttempt(BiConsumer<Player, Boolean> h) { this.onRescueAttempt = h; }
public void setOnChestInteract(BiConsumer<Player, Boolean> h) { this.onChestInteract = h; }
public void setOnHatchInteract(BiConsumer<Player, Boolean> h) { this.onHatchInteract = h; }
public void setOnGateInteract(BiConsumer<Player, Boolean> h) { this.onGateInteract = h; }
public void setOnKillerSneak(BiConsumer<Player, Boolean> h) { this.onKillerSneak = h; }
public void setOnHealSneak(BiConsumer<Player, Player> h) { this.onHealSneak = h; }
private BiConsumer<Player, Boolean> onCarriedShift;
@ -245,12 +245,12 @@ public class PlayerInteractListener implements Listener {
}
// Gate opening — проверяем ДО генератора, чтобы ворота можно было открыть рядом с геной
if (!bp.isKiller() && onHatchInteract != null) {
if (!bp.isKiller() && onGateInteract != null) {
var s = gameManager.getPlayerSession(player.getUniqueId());
if (s != null && s.getGateManager() != null) {
var gate = s.getGateManager().getGateAt(player.getLocation(), 3.0);
if (gate != null && !gate.isOpen()) {
onHatchInteract.accept(player, true);
onGateInteract.accept(player, true);
return;
}
}

View file

@ -95,7 +95,6 @@ public class MapConfig {
applyList("generators", locs);
applyList("hooks", locs);
applyList("chests", locs);
applyList("windows", locs);
}
// -------------------------------------------------------------------------

View file

@ -83,10 +83,6 @@ public class TemplateManager {
// Chest preset (1x1x1)
presets.put("chest_default", new PresetMap("chest_default",
"chest", new Location(null, 0, 0, 0)));
// Hatch preset (2x1x2)
presets.put("hatch_default", new PresetMap("hatch_default",
"hatch", new Location(null, 1, 0, 1)));
}
public boolean placeObject(String templateName, Location location) {

View file

@ -2,12 +2,8 @@ package dev.loki.phase;
import dev.loki.core.GameSession;
import dev.loki.gate.GateManager;
import dev.loki.hatch.Hatch;
import dev.loki.hatch.HatchCondition;
import dev.loki.hatch.HatchEscape;
import dev.loki.player.BasePlayer;
import dev.loki.player.PlayerState;
import dev.loki.player.Survivor;
import dev.loki.ui.Sidebar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
@ -27,9 +23,6 @@ public class EndgamePhase implements Phase {
private boolean gatesActivated;
private boolean killerWin; // true если маньяк убил всех — пропускаем геймплей эндгейма
private GateManager gateManager;
private Hatch hatch;
private HatchCondition hatchCondition;
private HatchEscape hatchEscape;
private Sidebar sidebar;
private int endgameTimer = -1; // секунд до конца эндгейма (-1 = не активно)
@ -37,13 +30,9 @@ public class EndgamePhase implements Phase {
this.openTicks = 20 * 20;
}
public EndgamePhase(GateManager gateManager,
Hatch hatch, HatchCondition hatchCondition) {
public EndgamePhase(GateManager gateManager) {
this.openTicks = 20 * 20;
this.gateManager = gateManager;
this.hatch = hatch;
this.hatchCondition = hatchCondition;
this.hatchEscape = new HatchEscape(hatch);
}
// -------------------------------------------------------------------------
@ -120,7 +109,7 @@ public class EndgamePhase implements Phase {
}
}
updateHatch(session);
updateGates(session);
checkWinConditions(session);
}
@ -137,10 +126,6 @@ public class EndgamePhase implements Phase {
sidebar = null;
}
if (hatch != null) {
hatch.reset();
}
if (gateManager != null) {
gateManager.resetAll();
}
@ -229,29 +214,9 @@ public class EndgamePhase implements Phase {
gateManager.activateEndgame();
gatesActivated = true;
}
checkHatchConditions(session);
session.getPlugin().getLogger().info("Endgame started!");
}
private void checkHatchConditions(GameSession session) {
if (hatch == null || hatchCondition == null) return;
int repairedGens = countRepairedGenerators(session);
hatchCondition.checkAndReveal(hatch, session.getPlayers().values(), repairedGens);
if (hatchCondition.isLastSurvivor(session.getPlayers().values())) {
hatchCondition.checkAndOpen(hatch, session.getPlayers().values(), repairedGens);
if (hatch.isOpen() && hatchEscape != null) {
hatchEscape.notifyNearbyPlayers();
}
}
}
private int countRepairedGenerators(GameSession session) {
if (session.getGeneratorManager() != null) {
return session.getGeneratorManager().getCompletedCount();
}
return 0;
}
private void announceEndgameStart(GameSession session) {
for (BasePlayer bp : session.getPlayers().values()) {
Player p = bp.getPlayer();
@ -271,18 +236,6 @@ public class EndgamePhase implements Phase {
gateManager.updateAllGates(1.0 / openTicks);
}
private void updateHatch(GameSession session) {
if (hatch == null || !hatch.isOpen() || hatchEscape == null) return;
for (BasePlayer player : session.getPlayers().values()) {
if (player.isKiller()) continue;
PlayerState st = player.getState();
if (st != PlayerState.HEALTHY && st != PlayerState.INJURED) continue;
if (hatchEscape.canEscape((Survivor) player)) {
hatchEscape.tryEscape((Survivor) player);
}
}
}
private void checkWinConditions(GameSession session) {
if (complete) return;
int survivorsAlive = 0;

View file

@ -301,8 +301,8 @@ public class GameplayInteraction {
damageSystem.getKillerController().getCarry().onSurvivorShift(survivor.getUuid());
});
// Gate (uses hatch callback - triggered on PORTAL/HOPPER interaction)
interactListener.setOnHatchInteract((player, ignored) -> {
// Gate - triggered on HOPPER interaction
interactListener.setOnGateInteract((player, ignored) -> {
BasePlayer bp = session.getPlayer(player.getUniqueId());
if (bp == null || bp.isKiller()) return;
Survivor survivor = (Survivor) bp;
@ -337,7 +337,7 @@ public class GameplayInteraction {
interactListener.setOnCarriedShift(null);
interactListener.setOnGeneratorSkillCheckSneak(null);
interactListener.setOnGeneratorInteract(null);
interactListener.setOnHatchInteract(null);
interactListener.setOnGateInteract(null);
}
}
}