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:
loki5512344 2026-06-04 12:11:04 +02:00
parent 3d6204cc83
commit 89dc9eb00d
78 changed files with 2293 additions and 1210 deletions

View file

@ -11,42 +11,42 @@ public class GameConfig {
}
private void setDefaults() {
// Lobby settings
setDefault("lobby.min-players", 5);
// Lobby
setDefault("lobby.min-players", 4);
setDefault("lobby.max-players", 5);
setDefault("lobby.countdown-seconds", 60);
setDefault("lobby.countdown-4-players", 45);
setDefault("lobby.countdown-5-players", 15);
setDefault("lobby.role-assign-delay", 5);
setDefault("lobby.server-lobby-count", 3);
setDefault("lobby.persist-lobbies", true);
// Generator settings
// Players scale
setDefault("players.survivor-scale", 1.0);
setDefault("players.killer-scale", 1.5);
// Generators
setDefault("generators.total-count", 7);
setDefault("generators.required-count", 5);
setDefault("generators.repair-seconds", 80);
setDefault("generators.repair-seconds", 90);
setDefault("generators.regression-rate", 0.25);
setDefault("generators.kick-regression-rate", 2.5);
setDefault("generators.kick-regression-percent", 20.0);
setDefault("generators.kick-duration-seconds", 30);
setDefault("generators.kick-cooldown-seconds", 30);
setDefault("generators.kick-charge-seconds", 3);
setDefault("generators.interaction-range", 3.0);
// Skill check settings (legacy - moved to skillcheck.yml)
setDefault("skillcheck.zone-size", 0.15);
setDefault("skillcheck.rotation-speed", 3.0);
setDefault("skillcheck.success-bonus", 5.0);
setDefault("skillcheck.fail-penalty", 10.0);
// Colors for UI (&#rrggbb format support)
setDefault("colors.progress-bar-complete", "&#00FF00");
setDefault("colors.progress-bar-incomplete", "&#FF0000");
setDefault("colors.progress-bar-background", "&#333333");
setDefault("colors.hp-healthy", "&#00FF00");
setDefault("colors.hp-injured", "&#FFA500");
setDefault("colors.hp-downed", "&#FF0000");
setDefault("colors.hp-dead", "&#8B0000");
// Hook settings
// Hooks
setDefault("hooks.stage1-seconds", 60);
setDefault("hooks.stage2-seconds", 60);
setDefault("hooks.struggle-interval", 2);
setDefault("hooks.max-hooks", 3);
setDefault("hooks.interaction-range", 3.0);
setDefault("hooks.rescue-seconds", 3);
setDefault("hooks.sabotage-seconds", 10);
setDefault("hooks.sabotage-recovery-seconds", 60);
// Movement speeds (% of normal)
// Movement
setDefault("movement.killer", 115);
setDefault("movement.killer-carry", 92);
setDefault("movement.survivor-walk", 100);
@ -55,30 +55,66 @@ public class GameConfig {
setDefault("movement.exhaustion-duration", 3);
setDefault("movement.exhaustion-cooldown", 5);
// Healing settings
// Healing
setDefault("healing.other-heal-seconds", 15);
setDefault("healing.self-heal-seconds", 30);
setDefault("healing.other-heal-seconds", 16);
setDefault("healing.medkit-bonus", 0.5);
setDefault("healing.interrupt-on-move", true);
setDefault("healing.max-heals-per-match", 4);
setDefault("healing.heal-range", 3.0);
// Killer settings
// Carry / escape
setDefault("carry.escape-jumps", 15);
setDefault("carry.escape-speed-seconds", 3);
setDefault("carry.jump-fill-percent", 6.67);
// Killer
setDefault("killer.attack-cooldown", 2.5);
setDefault("killer.attack-range", 3.0);
setDefault("killer.pallet-stun-seconds", 2.5);
setDefault("killer.pallet-stun-seconds", 2.0);
setDefault("killer.pallet-break-seconds", 3.0);
setDefault("killer.terror-radius", 32);
setDefault("killer.pallet-interaction-range", 3.0);
// BossBar
setDefault("bossbar.enabled", true);
setDefault("bossbar.title", "<red>☠ МАНЬЯК РЯДОМ ☠</red>");
setDefault("bossbar.color", "RED");
setDefault("bossbar.style", "SOLID");
setDefault("bossbar.appear-range", 32);
setDefault("bossbar.full-range", 8);
// Window vault
setDefault("window.vault-survivor-fast", 1.0);
setDefault("window.vault-survivor-slow", 1.5);
setDefault("window.vault-killer", 1.5);
setDefault("window.vault-block-seconds", 15);
// Endgame
setDefault("endgame.gate-open-seconds", 20);
setDefault("endgame.collapse-seconds", 120);
setDefault("endgame.chest-open-seconds", 3);
setDefault("endgame.gate-interaction-range", 3.0);
// Killer notification
setDefault("killer-notification.compass-duration-seconds", 5);
// Scoreboard
setDefault("scoreboard.show-hearts", true);
setDefault("scoreboard.heart-symbol", "♥");
setDefault("scoreboard.color-healthy", "WHITE");
setDefault("scoreboard.color-injured", "RED");
setDefault("scoreboard.color-healing", "GREEN");
setDefault("scoreboard.color-hooked", "DARK_RED");
// Colors for UI
setDefault("colors.progress-bar-complete", "&#00FF00");
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);
}
private void setDefault(String path, Object value) {
@ -87,99 +123,130 @@ public class GameConfig {
}
}
public int getMinPlayers() {
return config.getInt("lobby.min-players", 5);
}
// -------------------------------------------------------------------------
// Lobby
// -------------------------------------------------------------------------
public int getMinPlayers() { return config.getInt("lobby.min-players", 4); }
public int getMaxPlayers() { return config.getInt("lobby.max-players", 5); }
public int getCountdown4Players() { return config.getInt("lobby.countdown-4-players", 45); }
public int getCountdown5Players() { return config.getInt("lobby.countdown-5-players", 15); }
/** @deprecated Use getCountdown4Players() / getCountdown5Players() */
@Deprecated
public int getLobbyCountdown() { return getCountdown4Players(); }
public int getRoleAssignDelay() { return config.getInt("lobby.role-assign-delay", 5); }
public int getServerLobbyCount() { return config.getInt("lobby.server-lobby-count", 3); }
public boolean isPersistLobbies() { return config.getBoolean("lobby.persist-lobbies", true); }
public int getMaxPlayers() {
return config.getInt("lobby.max-players", 5);
}
// -------------------------------------------------------------------------
// Players
// -------------------------------------------------------------------------
public double getSurvivorScale() { return config.getDouble("players.survivor-scale", 1.0); }
public double getKillerScale() { return config.getDouble("players.killer-scale", 1.5); }
public int getLobbyCountdown() {
return config.getInt("lobby.countdown-seconds", 60);
}
// -------------------------------------------------------------------------
// Generators
// -------------------------------------------------------------------------
public int getGeneratorTotal() { return config.getInt("generators.total-count", 7); }
public int getGeneratorRequired() { return config.getInt("generators.required-count", 5); }
public int getGeneratorRepairSeconds() { return config.getInt("generators.repair-seconds", 90); }
public double getRegressionRate() { return config.getDouble("generators.regression-rate", 0.25); }
public double getKickRegressionRate() { return config.getDouble("generators.kick-regression-rate", 2.5); }
public double getKickRegressionPercent() { return config.getDouble("generators.kick-regression-percent", 20.0); }
public int getKickDuration() { return config.getInt("generators.kick-duration-seconds", 30); }
public int getKickCooldownSeconds() { return config.getInt("generators.kick-cooldown-seconds", 30); }
public int getKickChargeSeconds() { return config.getInt("generators.kick-charge-seconds", 3); }
public double getGeneratorInteractionRange() { return config.getDouble("generators.interaction-range", 3.0); }
public int getRoleAssignDelay() {
return config.getInt("lobby.role-assign-delay", 5);
}
// -------------------------------------------------------------------------
// Hooks
// -------------------------------------------------------------------------
public int getStage1Seconds() { return config.getInt("hooks.stage1-seconds", 60); }
public int getStage2Seconds() { return config.getInt("hooks.stage2-seconds", 60); }
public int getStruggleInterval() { return config.getInt("hooks.struggle-interval", 2); }
public int getMaxHooks() { return config.getInt("hooks.max-hooks", 3); }
public double getHookInteractionRange() { return config.getDouble("hooks.interaction-range", 3.0); }
public int getRescueSeconds() { return config.getInt("hooks.rescue-seconds", 3); }
public int getSabotageSeconds() { return config.getInt("hooks.sabotage-seconds", 10); }
public int getSabotageRecoverySeconds() { return config.getInt("hooks.sabotage-recovery-seconds", 60); }
public int getGeneratorTotal() {
return config.getInt("generators.total-count", 7);
}
// -------------------------------------------------------------------------
// Movement
// -------------------------------------------------------------------------
public double getKillerSpeed() { return config.getDouble("movement.killer", 115) / 100.0; }
public double getKillerCarrySpeed() { return config.getDouble("movement.killer-carry", 92) / 100.0; }
public double getSurvivorWalkSpeed() { return config.getDouble("movement.survivor-walk", 100) / 100.0; }
public double getSurvivorSprintSpeed() { return config.getDouble("movement.survivor-sprint", 150) / 100.0; }
public double getSurvivorCrawlSpeed() { return config.getDouble("movement.survivor-crawl", 30) / 100.0; }
public int getExhaustionDuration() { return config.getInt("movement.exhaustion-duration", 3); }
public int getExhaustionCooldown() { return config.getInt("movement.exhaustion-cooldown", 5); }
public int getGeneratorRequired() {
return config.getInt("generators.required-count", 5);
}
// -------------------------------------------------------------------------
// Healing
// -------------------------------------------------------------------------
public int getOtherHealSeconds() { return config.getInt("healing.other-heal-seconds", 15); }
public int getSelfHealSeconds() { return config.getInt("healing.self-heal-seconds", 30); }
public double getMedkitBonus() { return config.getDouble("healing.medkit-bonus", 0.5); }
public boolean isHealInterruptOnMove() { return config.getBoolean("healing.interrupt-on-move", true); }
public int getMaxHealsPerMatch() { return config.getInt("healing.max-heals-per-match", 4); }
public double getHealRange() { return config.getDouble("healing.heal-range", 3.0); }
public int getGeneratorRepairSeconds() {
return config.getInt("generators.repair-seconds", 80);
}
// -------------------------------------------------------------------------
// Carry / Escape
// -------------------------------------------------------------------------
public int getCarryEscapeJumps() { return config.getInt("carry.escape-jumps", 15); }
public int getCarryEscapeSpeedSeconds() { return config.getInt("carry.escape-speed-seconds", 3); }
public double getCarryJumpFillPercent() { return config.getDouble("carry.jump-fill-percent", 6.67); }
public double getRegressionRate() {
return config.getDouble("generators.regression-rate", 0.25);
}
// -------------------------------------------------------------------------
// Killer
// -------------------------------------------------------------------------
public int getAttackCooldown() { return (int)(config.getDouble("killer.attack-cooldown", 2.5) * 20); }
public double getAttackRange() { return config.getDouble("killer.attack-range", 3.0); }
public int getPalletStunTicks() { return (int)(config.getDouble("killer.pallet-stun-seconds", 2.0) * 20); }
public int getPalletBreakTicks() { return (int)(config.getDouble("killer.pallet-break-seconds", 3.0) * 20); }
public int getTerrorRadius() { return config.getInt("killer.terror-radius", 32); }
public double getPalletInteractionRange() { return config.getDouble("killer.pallet-interaction-range", 3.0); }
public double getKickRegressionRate() {
return config.getDouble("generators.kick-regression-rate", 2.5);
}
// -------------------------------------------------------------------------
// BossBar
// -------------------------------------------------------------------------
public boolean isBossBarEnabled() { return config.getBoolean("bossbar.enabled", true); }
public String getBossBarTitle() { return config.getString("bossbar.title", "<red>☠ МАНЬЯК РЯДОМ ☠</red>"); }
public String getBossBarColor() { return config.getString("bossbar.color", "RED"); }
public String getBossBarStyle() { return config.getString("bossbar.style", "SOLID"); }
public int getBossBarAppearRange() { return config.getInt("bossbar.appear-range", 32); }
public int getBossBarFullRange() { return config.getInt("bossbar.full-range", 8); }
public int getKickDuration() {
return config.getInt("generators.kick-duration-seconds", 30);
}
// -------------------------------------------------------------------------
// Window
// -------------------------------------------------------------------------
public int getVaultSurvivorFastTicks() { return (int)(config.getDouble("window.vault-survivor-fast", 1.0) * 20); }
public int getVaultSurvivorSlowTicks() { return (int)(config.getDouble("window.vault-survivor-slow", 1.5) * 20); }
public int getVaultKillerTicks() { return (int)(config.getDouble("window.vault-killer", 1.5) * 20); }
public int getVaultBlockSeconds() { return config.getInt("window.vault-block-seconds", 15); }
public int getStage1Seconds() {
return config.getInt("hooks.stage1-seconds", 60);
}
// -------------------------------------------------------------------------
// Endgame
// -------------------------------------------------------------------------
public int getGateOpenSeconds() { return config.getInt("endgame.gate-open-seconds", 20); }
public int getCollapseSeconds() { return config.getInt("endgame.collapse-seconds", 120); }
public int getChestOpenSeconds() { return config.getInt("endgame.chest-open-seconds", 3); }
public double getGateInteractionRange() { return config.getDouble("endgame.gate-interaction-range", 3.0); }
public int getStage2Seconds() {
return config.getInt("hooks.stage2-seconds", 60);
}
// -------------------------------------------------------------------------
// Killer notification
// -------------------------------------------------------------------------
public int getCompassDurationSeconds() { return config.getInt("killer-notification.compass-duration-seconds", 5); }
public int getMaxHooks() {
return config.getInt("hooks.max-hooks", 3);
}
// -------------------------------------------------------------------------
// Scoreboard
// -------------------------------------------------------------------------
public boolean isShowHearts() { return config.getBoolean("scoreboard.show-hearts", true); }
public String getHeartSymbol() { return config.getString("scoreboard.heart-symbol", "♥"); }
public String getColorHealthy() { return config.getString("scoreboard.color-healthy", "WHITE"); }
public String getColorInjured() { return config.getString("scoreboard.color-injured", "RED"); }
public String getColorHealing() { return config.getString("scoreboard.color-healing", "GREEN"); }
public String getColorHooked() { return config.getString("scoreboard.color-hooked", "DARK_RED"); }
public double getKillerSpeed() {
return config.getDouble("movement.killer", 115) / 100.0;
}
public double getKillerCarrySpeed() {
return config.getDouble("movement.killer-carry", 92) / 100.0;
}
public double getSurvivorWalkSpeed() {
return config.getDouble("movement.survivor-walk", 100) / 100.0;
}
public double getSurvivorSprintSpeed() {
return config.getDouble("movement.survivor-sprint", 150) / 100.0;
}
public double getSurvivorCrawlSpeed() {
return config.getDouble("movement.survivor-crawl", 30) / 100.0;
}
public int getAttackCooldown() {
return (int) (config.getDouble("killer.attack-cooldown", 2.5) * 20);
}
public double getAttackRange() {
return config.getDouble("killer.attack-range", 3.0);
}
public int getPalletStunTicks() {
return (int) (config.getDouble("killer.pallet-stun-seconds", 2.5) * 20);
}
public int getPalletBreakTicks() {
return (int) (config.getDouble("killer.pallet-break-seconds", 3.0) * 20);
}
public int getTerrorRadius() {
return config.getInt("killer.terror-radius", 32);
}
public FileConfiguration getConfig() {
return config;
}
public FileConfiguration getConfig() { return config; }
}