Stage 2-3: Player states and Killer system

This commit is contained in:
loki5512344 2026-05-31 20:49:28 +02:00
parent 5a53620b3a
commit d1f854fc79
14 changed files with 1095 additions and 0 deletions

View file

@ -0,0 +1,79 @@
package dev.loki.perk;
import dev.loki.player.BasePlayer;
import dev.loki.mdbd;
public abstract class BasePerk implements IPerk {
protected final mdbd plugin;
protected final String id;
protected final String name;
protected final String description;
protected final PerkType type;
protected final boolean passive;
protected final int cooldownSeconds;
protected long cooldownEndTime = 0;
protected BasePerk(mdbd plugin, String id, String name, String description,
PerkType type, boolean passive, int cooldownSeconds) {
this.plugin = plugin;
this.id = id;
this.name = name;
this.description = description;
this.type = type;
this.passive = passive;
this.cooldownSeconds = cooldownSeconds;
}
@Override
public String getId() {
return id;
}
@Override
public String getName() {
return name;
}
@Override
public String getDescription() {
return description;
}
@Override
public PerkType getType() {
return type;
}
@Override
public boolean isPassive() {
return passive;
}
@Override
public int getCooldownSeconds() {
return cooldownSeconds;
}
@Override
public boolean isOnCooldown() {
return System.currentTimeMillis() < cooldownEndTime;
}
@Override
public void startCooldown() {
if (cooldownSeconds > 0) {
cooldownEndTime = System.currentTimeMillis() + (cooldownSeconds * 1000L);
}
}
@Override
public void resetCooldown() {
cooldownEndTime = 0;
}
protected void sendPerkMessage(BasePlayer player, String message) {
if (player.getPlayer() != null) {
player.getPlayer().sendMessage("§6[" + name + "] §7" + message);
}
}
}

View file

@ -0,0 +1,60 @@
package dev.loki.perk;
import dev.loki.mdbd;
import dev.loki.perk.killer.BBQChiliPerk;
import dev.loki.perk.killer.CorruptInterventionPerk;
import dev.loki.perk.killer.HexRuinPerk;
import dev.loki.perk.survivor.BorrowedTimePerk;
import dev.loki.perk.survivor.DeadHardPerk;
import dev.loki.perk.survivor.SelfCarePerk;
import dev.loki.perk.survivor.SprintBurstPerk;
import java.util.HashMap;
import java.util.Map;
public class PerkRegistry {
private final Map<String, IPerk> perks = new HashMap<>();
private final mdbd plugin;
public PerkRegistry(mdbd plugin) {
this.plugin = plugin;
registerDefaultPerks();
}
private void registerDefaultPerks() {
register(new SelfCarePerk(plugin));
register(new DeadHardPerk(plugin));
register(new SprintBurstPerk(plugin));
register(new BorrowedTimePerk(plugin));
register(new HexRuinPerk(plugin));
register(new CorruptInterventionPerk(plugin));
register(new BBQChiliPerk(plugin));
}
public void register(IPerk perk) {
perks.put(perk.getId().toLowerCase(), perk);
plugin.getLogger().info("Registered perk: " + perk.getName());
}
public IPerk getPerk(String id) {
return perks.get(id.toLowerCase());
}
public Map<String, IPerk> getAllPerks() {
return new HashMap<>(perks);
}
public Map<String, IPerk> getPerksByType(PerkType type) {
Map<String, IPerk> result = new HashMap<>();
for (Map.Entry<String, IPerk> entry : perks.entrySet()) {
if (entry.getValue().getType() == type) {
result.put(entry.getKey(), entry.getValue());
}
}
return result;
}
public boolean hasPerk(String id) {
return perks.containsKey(id.toLowerCase());
}
}

View file

@ -0,0 +1,105 @@
package dev.loki.perk.killer;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import dev.loki.player.Survivor;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class BBQChiliPerk extends BasePerk {
private static final int REVEAL_DURATION_SECONDS = 4;
private static final int AURA_RANGE = 100;
private static final int BONUS_STACKS = 4;
private final Map<UUID, Integer> hookedPlayers = new HashMap<>();
private int currentStacks = 0;
public BBQChiliPerk(mdbd plugin) {
super(plugin, "bbq_chili", "Барбекю и Чили",
"После подвешивания видите ауру всех выживших на 4 секунды",
PerkType.KILLER, true, 0);
}
@Override
public void onEquip(BasePlayer player) {
currentStacks = 0;
sendPerkMessage(player, "Активировано: ауры видны после подвешивания");
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
if (trigger == PerkTrigger.ON_KILLER_HOOK) {
if (context instanceof Survivor survivor) {
revealSurvivorAuras(player, survivor);
}
}
}
private void revealSurvivorAuras(BasePlayer killer, Survivor hookedSurvivor) {
Player killerPlayer = killer.getPlayer();
if (killerPlayer == null) return;
currentStacks = Math.min(currentStacks + 1, BONUS_STACKS);
hookedPlayers.put(hookedSurvivor.getUuid(), currentStacks);
Collection<? extends Player> allPlayers = Bukkit.getOnlinePlayers();
for (Player player : allPlayers) {
if (player.equals(killerPlayer)) continue;
if (player.getLocation().distance(killerPlayer.getLocation()) > AURA_RANGE) {
continue;
}
revealAura(killerPlayer, player);
}
sendPerkMessage(killer, "§cАуры выживших видны! (x" + currentStacks + " стаков)");
new BukkitRunnable() {
@Override
public void run() {
sendPerkMessage(killer, "§7Эффект ауры закончился");
}
}.runTaskLater(plugin, REVEAL_DURATION_SECONDS * 20L);
}
private void revealAura(Player killer, Player survivor) {
Location survivorLoc = survivor.getLocation();
for (int i = 0; i < 3; i++) {
Location particleLoc = survivorLoc.clone().add(0, i * 0.5, 0);
killer.spawnParticle(Particle.GLOW, particleLoc, 5, 0.3, 0.5, 0.3, 0.01);
}
}
@Override
public void onRemove(BasePlayer player) {
currentStacks = 0;
hookedPlayers.clear();
sendPerkMessage(player, "Перк деактивирован");
}
public int getStacks() {
return currentStacks;
}
public static int getRevealDuration() {
return REVEAL_DURATION_SECONDS;
}
public static int getAuraRange() {
return AURA_RANGE;
}
public static int getMaxStacks() {
return BONUS_STACKS;
}
}

View file

@ -0,0 +1,97 @@
package dev.loki.perk.killer;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public class CorruptInterventionPerk extends BasePerk {
private static final int BLOCK_DURATION_SECONDS = 120;
private static final int FARTHEST_GENERATORS = 3;
private final Set<UUID> blockedGenerators = new HashSet<>();
private boolean effectActive = false;
public CorruptInterventionPerk(mdbd plugin) {
super(plugin, "corrupt_intervention", "Вмешательство Порчи",
"3 самых дальних генератора от киллера заблокированы на 120 секунд",
PerkType.KILLER, true, 0);
}
@Override
public void onEquip(BasePlayer player) {
effectActive = true;
sendPerkMessage(player, "§cВмешательство активировано! Дальние генераторы заблокированы");
startEffectTimer();
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
if (trigger == PerkTrigger.ON_GENERATOR_START && effectActive) {
if (context instanceof UUID genId) {
if (blockedGenerators.contains(genId)) {
preventRepair(player, genId);
}
}
}
}
private void preventRepair(BasePlayer player, UUID genId) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer == null) return;
bukkitPlayer.sendMessage("§cЭтот генератор заблокирован Порчей!");
spawnBlockEffect(bukkitPlayer);
}
private void spawnBlockEffect(Player player) {
Location loc = player.getLocation();
player.getWorld().spawnParticle(Particle.BARRIER, loc.add(0, 1, 0), 5, 0.5, 0.5, 0.5, 0);
}
private void startEffectTimer() {
new BukkitRunnable() {
@Override
public void run() {
effectActive = false;
blockedGenerators.clear();
}
}.runTaskLater(plugin, BLOCK_DURATION_SECONDS * 20L);
}
@Override
public void onRemove(BasePlayer player) {
effectActive = false;
blockedGenerators.clear();
sendPerkMessage(player, "Перк деактивирован");
}
public void blockGenerator(UUID generatorId) {
if (blockedGenerators.size() < FARTHEST_GENERATORS) {
blockedGenerators.add(generatorId);
}
}
public boolean isGeneratorBlocked(UUID generatorId) {
return effectActive && blockedGenerators.contains(generatorId);
}
public boolean isEffectActive() {
return effectActive;
}
public static int getBlockDuration() {
return BLOCK_DURATION_SECONDS;
}
public static int getFarthestGeneratorCount() {
return FARTHEST_GENERATORS;
}
}

View file

@ -0,0 +1,94 @@
package dev.loki.perk.killer;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class HexRuinPerk extends BasePerk {
private static final double REGRESSION_RATE = 0.25;
private static final double KICK_REGRESSION = 2.5;
private final Map<UUID, Double> affectedGenerators = new HashMap<>();
private boolean hexActive = true;
public HexRuinPerk(mdbd plugin) {
super(plugin, "hex_ruin", "Порча: Губительность",
"Генераторы автоматически теряют прогресс когда не чинятся",
PerkType.KILLER, true, 0);
}
@Override
public void onEquip(BasePlayer player) {
hexActive = true;
sendPerkMessage(player, "§cПорча активирована! Генераторы регрессируют");
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
switch (trigger) {
case ON_GENERATOR_START -> handleGeneratorStart(player, context);
case ON_GENERATOR_COMPLETE -> handleGeneratorComplete(player, context);
case ON_TOTEM_CLEANSED -> handleTotemCleansed(player);
}
}
private void handleGeneratorStart(BasePlayer player, Object context) {
if (!hexActive) return;
if (context instanceof UUID genId) {
affectedGenerators.put(genId, 0.0);
spawnHexParticles(player);
}
}
private void handleGeneratorComplete(BasePlayer player, Object context) {
if (context instanceof UUID genId) {
affectedGenerators.remove(genId);
}
}
private void handleTotemCleansed(BasePlayer player) {
hexActive = false;
affectedGenerators.clear();
sendPerkMessage(player, "§cПорча очищена! Губительность деактивирована");
}
private void spawnHexParticles(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer == null) return;
for (int i = 0; i < 5; i++) {
Location loc = bukkitPlayer.getLocation().add(
Math.random() * 4 - 2,
Math.random() * 2,
Math.random() * 4 - 2
);
bukkitPlayer.getWorld().spawnParticle(Particle.SOUL_FIRE_FLAME, loc, 3, 0.2, 0.2, 0.2, 0.01);
}
}
@Override
public void onRemove(BasePlayer player) {
hexActive = false;
affectedGenerators.clear();
sendPerkMessage(player, "Перк деактивирован");
}
public static double getRegressionRate() {
return REGRESSION_RATE;
}
public static double getKickRegression() {
return KICK_REGRESSION;
}
public boolean isHexActive() {
return hexActive;
}
}

View file

@ -0,0 +1,76 @@
package dev.loki.perk.survivor;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
public class BorrowedTimePerk extends BasePerk {
private static final int ENDURANCE_DURATION_SECONDS = 15;
private static final int SPEED_BOOST_SECONDS = 2;
private static final int SPEED_AMPLIFIER = 1;
public BorrowedTimePerk(mdbd plugin) {
super(plugin, "borrowed_time", "Заемное Время",
"После снятия с крюка получите 15 сек неуязвимости и рывок скорости",
PerkType.SURVIVOR, true, 0);
}
@Override
public void onEquip(BasePlayer player) {
sendPerkMessage(player, "Активировано: защита после спасения");
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
if (trigger == PerkTrigger.ON_UNHOOKED) {
applyEndurance(player);
}
}
private void applyEndurance(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer == null) return;
bukkitPlayer.addPotionEffect(new PotionEffect(
PotionEffectType.DAMAGE_RESISTANCE,
ENDURANCE_DURATION_SECONDS * 20,
4,
false, false, true));
bukkitPlayer.addPotionEffect(new PotionEffect(
PotionEffectType.SPEED,
SPEED_BOOST_SECONDS * 20,
SPEED_AMPLIFIER,
false, false, true));
sendPerkMessage(player, "§aЗаемное Время активировано! 15 сек защиты");
new BukkitRunnable() {
@Override
public void run() {
if (player.getPlayer() != null) {
player.getPlayer().sendMessage("§cЗаемное Время закончилось");
}
}
}.runTaskLater(plugin, ENDURANCE_DURATION_SECONDS * 20L);
}
@Override
public void onRemove(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer != null) {
bukkitPlayer.removePotionEffect(PotionEffectType.DAMAGE_RESISTANCE);
}
sendPerkMessage(player, "Перк деактивирован");
}
public static int getEnduranceDuration() {
return ENDURANCE_DURATION_SECONDS;
}
}

View file

@ -0,0 +1,76 @@
package dev.loki.perk.survivor;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import dev.loki.player.PlayerState;
import org.bukkit.Location;
import org.bukkit.Particle;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.Vector;
public class DeadHardPerk extends BasePerk {
private static final int DASH_DISTANCE = 4;
private static final int INVULNERABILITY_TICKS = 20;
private static final int EXHAUSTION_SECONDS = 60;
public DeadHardPerk(mdbd plugin) {
super(plugin, "dead_hard", "Тяжелый Рывок",
"При ранении активируйте для рывка и неуязвимости",
PerkType.SURVIVOR, false, EXHAUSTION_SECONDS);
}
@Override
public void onEquip(BasePlayer player) {
sendPerkMessage(player, "Готов к использованию");
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
if (trigger == PerkTrigger.ON_KILLER_HIT && !isOnCooldown()) {
if (shouldActivate(player)) {
performDash(player);
}
}
}
private boolean shouldActivate(BasePlayer player) {
return player.getState() == PlayerState.INJURED;
}
private void performDash(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer == null) return;
Vector direction = bukkitPlayer.getLocation().getDirection();
direction.setY(0).normalize().multiply(DASH_DISTANCE);
Location target = bukkitPlayer.getLocation().add(direction);
bukkitPlayer.teleport(target);
bukkitPlayer.getWorld().spawnParticle(Particle.CLOUD, bukkitPlayer.getLocation(), 10);
startCooldown();
sendPerkMessage(player, "Рывок выполнен!");
new BukkitRunnable() {
@Override
public void run() {
if (player.getPlayer() != null) {
player.getPlayer().sendMessage("§aТяжелый Рывок готов к использованию!");
}
}
}.runTaskLater(plugin, cooldownSeconds * 20L);
}
@Override
public void onRemove(BasePlayer player) {
sendPerkMessage(player, "Перк деактивирован");
}
public static int getInvulnerabilityTicks() {
return INVULNERABILITY_TICKS;
}
}

View file

@ -0,0 +1,53 @@
package dev.loki.perk.survivor;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import dev.loki.player.PlayerState;
public class SelfCarePerk extends BasePerk {
private static final double HEAL_RATE = 0.5;
private static final int SELF_HEAL_TIME = 24;
public SelfCarePerk(mdbd plugin) {
super(plugin, "self_care", "Самолечение",
"Позволяет лечить себя без аптечки на 50% скорости",
PerkType.SURVIVOR, true, 0);
}
@Override
public void onEquip(BasePlayer player) {
sendPerkMessage(player, "Активировано: вы можете лечить себя без аптечки");
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
if (trigger == PerkTrigger.ON_HEAL_START) {
handleSelfHeal(player, context);
}
}
private void handleSelfHeal(BasePlayer player, Object context) {
if (player.getState() != PlayerState.INJURED) {
return;
}
if (context instanceof Boolean && (Boolean) context) {
sendPerkMessage(player, "Вы начали лечить себя...");
}
}
@Override
public void onRemove(BasePlayer player) {
sendPerkMessage(player, "Перк деактивирован");
}
public static double getHealRate() {
return HEAL_RATE;
}
public static int getSelfHealTime() {
return SELF_HEAL_TIME;
}
}

View file

@ -0,0 +1,80 @@
package dev.loki.perk.survivor;
import dev.loki.mdbd;
import dev.loki.perk.BasePerk;
import dev.loki.perk.PerkTrigger;
import dev.loki.perk.PerkType;
import dev.loki.player.BasePlayer;
import org.bukkit.entity.Player;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
public class SprintBurstPerk extends BasePerk {
private static final int SPRINT_DURATION_SECONDS = 3;
private static final int EXHAUSTION_SECONDS = 40;
private static final int SPEED_AMPLIFIER = 2;
public SprintBurstPerk(mdbd plugin) {
super(plugin, "sprint_burst", "Рывок Скорости",
"При старте бега получите 150% скорости на 3 секунды",
PerkType.SURVIVOR, false, EXHAUSTION_SECONDS);
}
@Override
public void onEquip(BasePlayer player) {
sendPerkMessage(player, "Готов к использованию");
}
@Override
public void onTrigger(BasePlayer player, PerkTrigger trigger, Object context) {
if (trigger == PerkTrigger.ON_SPRINT_START && !isOnCooldown()) {
activateSprint(player);
}
}
private void activateSprint(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer == null) return;
bukkitPlayer.addPotionEffect(new PotionEffect(
PotionEffectType.SPEED,
SPRINT_DURATION_SECONDS * 20,
SPEED_AMPLIFIER,
false, false, true));
startCooldown();
sendPerkMessage(player, "§aРывок активирован!");
new BukkitRunnable() {
@Override
public void run() {
if (player.getPlayer() != null) {
player.getPlayer().sendMessage("§cИзнурение... Рывок на перезарядке");
}
}
}.runTaskLater(plugin, SPRINT_DURATION_SECONDS * 20L);
new BukkitRunnable() {
@Override
public void run() {
if (player.getPlayer() != null) {
player.getPlayer().sendMessage("§aРывок Скорости снова готов!");
}
}
}.runTaskLater(plugin, cooldownSeconds * 20L);
}
@Override
public void onRemove(BasePlayer player) {
Player bukkitPlayer = player.getPlayer();
if (bukkitPlayer != null) {
bukkitPlayer.removePotionEffect(PotionEffectType.SPEED);
}
sendPerkMessage(player, "Перк деактивирован");
}
public static int getSprintDuration() {
return SPRINT_DURATION_SECONDS;
}
}