Stage 13: Perks and Totems

This commit is contained in:
loki5512344 2026-05-31 20:49:37 +02:00
parent d1f854fc79
commit 37e67ce97d
23 changed files with 2070 additions and 25 deletions

View file

@ -0,0 +1,125 @@
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

@ -0,0 +1,76 @@
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

@ -0,0 +1,110 @@
package dev.loki.hatch;
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;
public HatchEscape(Hatch hatch) {
this.hatch = hatch;
}
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("§cПодойдите ближе к люку!");
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("§7Люк скрыт...");
return false;
}
if (hatch.isClosed()) {
player.sendMessage("§cЛюк закрыт!");
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("§a§lПОБЕГ!", "§7Вы сбежали через люк!", 10, 60, 10);
survivor.setState(PlayerState.ESCAPED);
giveEscapeReward(survivor);
}
private void giveEscapeReward(Survivor survivor) {
Player player = survivor.getPlayer();
if (player == null) return;
player.sendMessage("§a+5000 Очков — Побег через люк!");
}
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("§e§lЛюк открыт!");
player.playSound(player.getLocation(), Sound.BLOCK_NOTE_BLOCK_PLING, 1.0f, 2.0f);
}
}
}
}