feat: DBD custom items - medkit/flashlight/toolbox/key/map with charges, ItemUseListener, starting items for survivors
This commit is contained in:
parent
371384ca8a
commit
f9847dbb73
4 changed files with 495 additions and 57 deletions
|
|
@ -13,65 +13,14 @@ public class ChestLoot {
|
||||||
this.random = ThreadLocalRandom.current();
|
this.random = ThreadLocalRandom.current();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Roll a random item with weighted probabilities. */
|
||||||
public ItemStack rollLoot() {
|
public ItemStack rollLoot() {
|
||||||
int roll = random.nextInt(100);
|
int roll = random.nextInt(100);
|
||||||
int currentWeight = 0;
|
|
||||||
|
|
||||||
currentWeight += 30;
|
if (roll < 30) return DBDItems.medkit(12 + random.nextInt(5)); // 30%
|
||||||
if (roll < currentWeight) {
|
if (roll < 55) return DBDItems.flashlight(6 + random.nextInt(4)); // 25%
|
||||||
return createMedkit();
|
if (roll < 80) return DBDItems.toolbox(10 + random.nextInt(7)); // 25%
|
||||||
}
|
if (roll < 92) return DBDItems.key(24 + random.nextInt(9)); // 12%
|
||||||
|
return DBDItems.map(8 + random.nextInt(9)); // 8%
|
||||||
currentWeight += 25;
|
|
||||||
if (roll < currentWeight) {
|
|
||||||
return createFlashlight();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentWeight += 25;
|
|
||||||
if (roll < currentWeight) {
|
|
||||||
return createToolbox();
|
|
||||||
}
|
|
||||||
|
|
||||||
currentWeight += 12;
|
|
||||||
if (roll < currentWeight) {
|
|
||||||
return createKey();
|
|
||||||
}
|
|
||||||
|
|
||||||
return createMap();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ItemStack createMedkit() {
|
|
||||||
int charges = 12 + random.nextInt(8);
|
|
||||||
return new ItemMedkit(charges).createItemStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ItemStack createFlashlight() {
|
|
||||||
int charges = 6 + random.nextInt(6);
|
|
||||||
return new ItemFlashlight(charges).createItemStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ItemStack createToolbox() {
|
|
||||||
int charges = 10 + random.nextInt(10);
|
|
||||||
return new ItemToolbox(charges).createItemStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ItemStack createKey() {
|
|
||||||
int charges = 24 + random.nextInt(16);
|
|
||||||
return new ItemKey(charges).createItemStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ItemStack createMap() {
|
|
||||||
int charges = 8 + random.nextInt(12);
|
|
||||||
return new ItemMap(charges).createItemStack();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRarity(ItemType type) {
|
|
||||||
return switch (type) {
|
|
||||||
case MEDKIT -> 30;
|
|
||||||
case FLASHLIGHT -> 25;
|
|
||||||
case TOOLBOX -> 25;
|
|
||||||
case KEY -> 12;
|
|
||||||
case MAP -> 8;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
198
src/main/java/dev/loki/item/DBDItems.java
Normal file
198
src/main/java/dev/loki/item/DBDItems.java
Normal file
|
|
@ -0,0 +1,198 @@
|
||||||
|
package dev.loki.item;
|
||||||
|
|
||||||
|
import dev.loki.util.TextUtil;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import org.bukkit.Material;
|
||||||
|
import org.bukkit.NamespacedKey;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Central factory for all DBD custom items.
|
||||||
|
* Items are identified by a PersistentDataContainer tag "mdbd:item_type".
|
||||||
|
* Charges are stored as "mdbd:charges".
|
||||||
|
*/
|
||||||
|
public final class DBDItems {
|
||||||
|
|
||||||
|
public static final NamespacedKey KEY_TYPE = new NamespacedKey("mdbd", "item_type");
|
||||||
|
public static final NamespacedKey KEY_CHARGES = new NamespacedKey("mdbd", "charges");
|
||||||
|
|
||||||
|
private DBDItems() {}
|
||||||
|
|
||||||
|
// ── Factories ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Медкит — лечит одно состояние здоровья. */
|
||||||
|
public static ItemStack medkit(int charges) {
|
||||||
|
return build(
|
||||||
|
Material.PAPER,
|
||||||
|
"<#00FF7F>Медкит",
|
||||||
|
charges, ItemType.MEDKIT.getDefaultCharges(),
|
||||||
|
List.of(
|
||||||
|
"<gray>Лечит одно состояние здоровья.",
|
||||||
|
"<yellow>ПКМ <gray>— начать лечение"
|
||||||
|
),
|
||||||
|
"MEDKIT"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack medkit() { return medkit(ItemType.MEDKIT.getDefaultCharges()); }
|
||||||
|
|
||||||
|
/** Фонарик — ослепляет киллера при попадании. */
|
||||||
|
public static ItemStack flashlight(int charges) {
|
||||||
|
return build(
|
||||||
|
Material.TORCH,
|
||||||
|
"<#FFD700>Фонарик",
|
||||||
|
charges, ItemType.FLASHLIGHT.getDefaultCharges(),
|
||||||
|
List.of(
|
||||||
|
"<gray>Ослепляет киллера при попадании.",
|
||||||
|
"<yellow>ПКМ <gray>— направить луч на киллера"
|
||||||
|
),
|
||||||
|
"FLASHLIGHT"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack flashlight() { return flashlight(ItemType.FLASHLIGHT.getDefaultCharges()); }
|
||||||
|
|
||||||
|
/** Ящик с инструментами — ускоряет ремонт генератора. */
|
||||||
|
public static ItemStack toolbox(int charges) {
|
||||||
|
return build(
|
||||||
|
Material.CHEST,
|
||||||
|
"<#00CED1>Ящик с инструментами",
|
||||||
|
charges, ItemType.TOOLBOX.getDefaultCharges(),
|
||||||
|
List.of(
|
||||||
|
"<gray>Ускоряет ремонт генераторов.",
|
||||||
|
"<yellow>Держите в руке <gray>во время ремонта"
|
||||||
|
),
|
||||||
|
"TOOLBOX"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack toolbox() { return toolbox(ItemType.TOOLBOX.getDefaultCharges()); }
|
||||||
|
|
||||||
|
/** Ключ — открывает люк (hatch). */
|
||||||
|
public static ItemStack key(int charges) {
|
||||||
|
return build(
|
||||||
|
Material.TRIPWIRE_HOOK,
|
||||||
|
"<#FFA500>Ключ",
|
||||||
|
charges, ItemType.KEY.getDefaultCharges(),
|
||||||
|
List.of(
|
||||||
|
"<gray>Открывает люк немедленно.",
|
||||||
|
"<yellow>ПКМ у люка <gray>— открыть"
|
||||||
|
),
|
||||||
|
"KEY"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack key() { return key(ItemType.KEY.getDefaultCharges()); }
|
||||||
|
|
||||||
|
/** Карта — показывает объекты на карте. */
|
||||||
|
public static ItemStack map(int charges) {
|
||||||
|
return build(
|
||||||
|
Material.MAP,
|
||||||
|
"<#87CEEB>Карта",
|
||||||
|
charges, ItemType.MAP.getDefaultCharges(),
|
||||||
|
List.of(
|
||||||
|
"<gray>Раскрывает объекты поблизости.",
|
||||||
|
"<yellow>ПКМ <gray>— активировать"
|
||||||
|
),
|
||||||
|
"MAP"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ItemStack map() { return map(ItemType.MAP.getDefaultCharges()); }
|
||||||
|
|
||||||
|
// ── Helpers ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private static ItemStack build(Material mat, String name, int charges, int maxCharges,
|
||||||
|
List<String> loreLines, String typeId) {
|
||||||
|
var item = new ItemStack(mat);
|
||||||
|
item.editMeta(meta -> {
|
||||||
|
meta.displayName(TextUtil.color(name));
|
||||||
|
|
||||||
|
// Build lore: description lines + charges bar
|
||||||
|
var lore = new java.util.ArrayList<Component>();
|
||||||
|
for (String line : loreLines) {
|
||||||
|
lore.add(TextUtil.color(line));
|
||||||
|
}
|
||||||
|
lore.add(Component.empty());
|
||||||
|
lore.add(TextUtil.color(chargesBar(charges, maxCharges)));
|
||||||
|
meta.lore(lore);
|
||||||
|
|
||||||
|
meta.getPersistentDataContainer().set(KEY_TYPE, PersistentDataType.STRING, typeId);
|
||||||
|
meta.getPersistentDataContainer().set(KEY_CHARGES, PersistentDataType.INTEGER, charges);
|
||||||
|
});
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String chargesBar(int charges, int max) {
|
||||||
|
int filled = max > 0 ? (int) (8.0 * charges / max) : 0;
|
||||||
|
int empty = 8 - filled;
|
||||||
|
return "<#FFD700>Заряды: <#00FF00>" + "█".repeat(filled) + "<gray>" + "░".repeat(empty)
|
||||||
|
+ " <white>" + charges + "/" + max;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Identification ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
public static String getType(ItemStack item) {
|
||||||
|
if (item == null || !item.hasItemMeta()) return null;
|
||||||
|
return item.getItemMeta().getPersistentDataContainer()
|
||||||
|
.get(KEY_TYPE, PersistentDataType.STRING);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getCharges(ItemStack item) {
|
||||||
|
if (item == null || !item.hasItemMeta()) return 0;
|
||||||
|
Integer c = item.getItemMeta().getPersistentDataContainer()
|
||||||
|
.get(KEY_CHARGES, PersistentDataType.INTEGER);
|
||||||
|
return c != null ? c : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Обновить заряды на существующем предмете и вернуть обновлённый стак. */
|
||||||
|
public static ItemStack setCharges(ItemStack item, int newCharges) {
|
||||||
|
if (item == null || !item.hasItemMeta()) return item;
|
||||||
|
String type = getType(item);
|
||||||
|
if (type == null) return item;
|
||||||
|
int max = getMaxCharges(type);
|
||||||
|
return build(item.getType(),
|
||||||
|
getDisplayNameRaw(type), newCharges, max,
|
||||||
|
getLoreRaw(type), type);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isMedkit(ItemStack item) { return "MEDKIT".equals(getType(item)); }
|
||||||
|
public static boolean isFlashlight(ItemStack item) { return "FLASHLIGHT".equals(getType(item)); }
|
||||||
|
public static boolean isToolbox(ItemStack item) { return "TOOLBOX".equals(getType(item)); }
|
||||||
|
public static boolean isKey(ItemStack item) { return "KEY".equals(getType(item)); }
|
||||||
|
public static boolean isMap(ItemStack item) { return "MAP".equals(getType(item)); }
|
||||||
|
public static boolean isDBDItem(ItemStack item) { return getType(item) != null; }
|
||||||
|
|
||||||
|
private static int getMaxCharges(String type) {
|
||||||
|
return switch (type) {
|
||||||
|
case "MEDKIT" -> ItemType.MEDKIT.getDefaultCharges();
|
||||||
|
case "FLASHLIGHT" -> ItemType.FLASHLIGHT.getDefaultCharges();
|
||||||
|
case "TOOLBOX" -> ItemType.TOOLBOX.getDefaultCharges();
|
||||||
|
case "KEY" -> ItemType.KEY.getDefaultCharges();
|
||||||
|
default -> ItemType.MAP.getDefaultCharges();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getDisplayNameRaw(String type) {
|
||||||
|
return switch (type) {
|
||||||
|
case "MEDKIT" -> "<#00FF7F>Медкит";
|
||||||
|
case "FLASHLIGHT" -> "<#FFD700>Фонарик";
|
||||||
|
case "TOOLBOX" -> "<#00CED1>Ящик с инструментами";
|
||||||
|
case "KEY" -> "<#FFA500>Ключ";
|
||||||
|
default -> "<#87CEEB>Карта";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<String> getLoreRaw(String type) {
|
||||||
|
return switch (type) {
|
||||||
|
case "MEDKIT" -> List.of("<gray>Лечит одно состояние здоровья.", "<yellow>ПКМ <gray>— начать лечение");
|
||||||
|
case "FLASHLIGHT" -> List.of("<gray>Ослепляет киллера при попадании.", "<yellow>ПКМ <gray>— направить луч на киллера");
|
||||||
|
case "TOOLBOX" -> List.of("<gray>Ускоряет ремонт генераторов.", "<yellow>Держите в руке <gray>во время ремонта");
|
||||||
|
case "KEY" -> List.of("<gray>Открывает люк немедленно.", "<yellow>ПКМ у люка <gray>— открыть");
|
||||||
|
default -> List.of("<gray>Раскрывает объекты поблизости.", "<yellow>ПКМ <gray>— активировать");
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
264
src/main/java/dev/loki/item/ItemUseListener.java
Normal file
264
src/main/java/dev/loki/item/ItemUseListener.java
Normal file
|
|
@ -0,0 +1,264 @@
|
||||||
|
package dev.loki.item;
|
||||||
|
|
||||||
|
import dev.loki.core.GameSession;
|
||||||
|
import dev.loki.player.BasePlayer;
|
||||||
|
import dev.loki.player.PlayerState;
|
||||||
|
import dev.loki.player.Survivor;
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.bukkit.Sound;
|
||||||
|
import org.bukkit.entity.Player;
|
||||||
|
import org.bukkit.event.EventHandler;
|
||||||
|
import org.bukkit.event.EventPriority;
|
||||||
|
import org.bukkit.event.HandlerList;
|
||||||
|
import org.bukkit.event.Listener;
|
||||||
|
import org.bukkit.event.block.Action;
|
||||||
|
import org.bukkit.event.player.PlayerInteractEvent;
|
||||||
|
import org.bukkit.inventory.EquipmentSlot;
|
||||||
|
import org.bukkit.inventory.ItemStack;
|
||||||
|
import org.bukkit.plugin.Plugin;
|
||||||
|
import org.bukkit.potion.PotionEffect;
|
||||||
|
import org.bukkit.potion.PotionEffectType;
|
||||||
|
import org.bukkit.scheduler.BukkitRunnable;
|
||||||
|
import org.bukkit.scheduler.BukkitTask;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles right-click usage of DBD custom items during gameplay.
|
||||||
|
*/
|
||||||
|
public class ItemUseListener implements Listener {
|
||||||
|
|
||||||
|
private final Plugin plugin;
|
||||||
|
private final GameSession session;
|
||||||
|
|
||||||
|
// Medkit heal: survivor UUID → task
|
||||||
|
private final Map<UUID, BukkitTask> activeHeals = new HashMap<>();
|
||||||
|
private static final int MEDKIT_TICKS = 160; // 8 seconds
|
||||||
|
|
||||||
|
public ItemUseListener(Plugin plugin, GameSession session) {
|
||||||
|
this.plugin = plugin;
|
||||||
|
this.session = session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register() {
|
||||||
|
org.bukkit.Bukkit.getPluginManager().registerEvents(this, plugin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregister() {
|
||||||
|
HandlerList.unregisterAll(this);
|
||||||
|
activeHeals.values().forEach(BukkitTask::cancel);
|
||||||
|
activeHeals.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
@EventHandler(priority = EventPriority.HIGH)
|
||||||
|
public void onInteract(PlayerInteractEvent event) {
|
||||||
|
if (event.getHand() != EquipmentSlot.HAND) return;
|
||||||
|
Action action = event.getAction();
|
||||||
|
if (action != Action.RIGHT_CLICK_AIR && action != Action.RIGHT_CLICK_BLOCK) return;
|
||||||
|
|
||||||
|
Player player = event.getPlayer();
|
||||||
|
if (!session.getPlayers().containsKey(player.getUniqueId())) return;
|
||||||
|
|
||||||
|
BasePlayer bp = session.getPlayer(player.getUniqueId());
|
||||||
|
if (bp == null || bp.isKiller()) return;
|
||||||
|
|
||||||
|
ItemStack hand = player.getInventory().getItemInMainHand();
|
||||||
|
if (!DBDItems.isDBDItem(hand)) return;
|
||||||
|
|
||||||
|
String type = DBDItems.getType(hand);
|
||||||
|
if (type == null) return;
|
||||||
|
|
||||||
|
event.setCancelled(true); // prevent block interaction
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case "MEDKIT" -> useMedkit(player, (Survivor) bp, hand);
|
||||||
|
case "FLASHLIGHT" -> useFlashlight(player, hand);
|
||||||
|
case "TOOLBOX" -> { /* passive — handled in repair */ }
|
||||||
|
case "KEY" -> useKey(player, (Survivor) bp, hand);
|
||||||
|
case "MAP" -> useMap(player, hand);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Medkit ───────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void useMedkit(Player player, Survivor survivor, ItemStack item) {
|
||||||
|
UUID uuid = player.getUniqueId();
|
||||||
|
|
||||||
|
// Toggle: if already healing, cancel
|
||||||
|
if (activeHeals.containsKey(uuid)) {
|
||||||
|
cancelHeal(player);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PlayerState state = survivor.getState();
|
||||||
|
if (state != PlayerState.INJURED) {
|
||||||
|
player.sendMessage(Component.text("Вы не ранены!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int charges = DBDItems.getCharges(item);
|
||||||
|
if (charges <= 0) {
|
||||||
|
player.sendMessage(Component.text("Медкит пуст!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.sendMessage(Component.text("Начинаете самолечение...", NamedTextColor.GREEN));
|
||||||
|
player.sendActionBar(Component.text("⚕ Самолечение...", NamedTextColor.GREEN));
|
||||||
|
|
||||||
|
BukkitTask task = new BukkitRunnable() {
|
||||||
|
int ticks = 0;
|
||||||
|
int chargesLeft = charges;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
if (!player.isOnline() || survivor.getState() != PlayerState.INJURED) {
|
||||||
|
cancelHeal(player);
|
||||||
|
cancel();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ticks++;
|
||||||
|
chargesLeft = Math.max(0, charges - ticks);
|
||||||
|
|
||||||
|
// Update action bar progress
|
||||||
|
int filled = (int)(8.0 * ticks / MEDKIT_TICKS);
|
||||||
|
int empty = 8 - filled;
|
||||||
|
player.sendActionBar(Component.text(
|
||||||
|
"⚕ " + "█".repeat(filled) + "░".repeat(empty) +
|
||||||
|
" " + (int)(100.0 * ticks / MEDKIT_TICKS) + "%",
|
||||||
|
NamedTextColor.GREEN));
|
||||||
|
|
||||||
|
// Use 1 charge per 10 ticks
|
||||||
|
if (ticks % 10 == 0) {
|
||||||
|
updateItemCharges(player, chargesLeft);
|
||||||
|
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_HURT, 0.2f, 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticks >= MEDKIT_TICKS) {
|
||||||
|
// Heal done
|
||||||
|
survivor.setState(PlayerState.HEALTHY);
|
||||||
|
player.sendMessage(Component.text("✔ Вы вылечились!", NamedTextColor.GREEN));
|
||||||
|
player.sendActionBar(Component.empty());
|
||||||
|
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.5f, 1.5f);
|
||||||
|
updateItemCharges(player, Math.max(0, charges - MEDKIT_TICKS / 10));
|
||||||
|
activeHeals.remove(uuid);
|
||||||
|
cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.runTaskTimer(plugin, 0L, 1L);
|
||||||
|
|
||||||
|
activeHeals.put(uuid, task);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cancelHeal(Player player) {
|
||||||
|
BukkitTask task = activeHeals.remove(player.getUniqueId());
|
||||||
|
if (task != null) {
|
||||||
|
task.cancel();
|
||||||
|
player.sendActionBar(Component.empty());
|
||||||
|
player.sendMessage(Component.text("Лечение прервано.", NamedTextColor.YELLOW));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Flashlight ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void useFlashlight(Player player, ItemStack item) {
|
||||||
|
int charges = DBDItems.getCharges(item);
|
||||||
|
if (charges <= 0) {
|
||||||
|
player.sendMessage(Component.text("Батарейки сели!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ray-trace to find killer within 15 blocks
|
||||||
|
var result = player.getWorld().rayTraceEntities(
|
||||||
|
player.getEyeLocation(),
|
||||||
|
player.getEyeLocation().getDirection(),
|
||||||
|
15.0,
|
||||||
|
e -> e instanceof Player && !e.equals(player)
|
||||||
|
);
|
||||||
|
|
||||||
|
boolean hitKiller = false;
|
||||||
|
if (result != null && result.getHitEntity() instanceof Player target) {
|
||||||
|
BasePlayer bp = session.getPlayer(target.getUniqueId());
|
||||||
|
if (bp != null && bp.isKiller()) {
|
||||||
|
target.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 60, 1, false, false));
|
||||||
|
target.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, 60, 3, false, false));
|
||||||
|
target.playSound(target.getLocation(), Sound.ENTITY_BLAZE_HURT, 1.0f, 0.5f);
|
||||||
|
player.sendMessage(Component.text("✔ Ослепили киллера!", NamedTextColor.YELLOW));
|
||||||
|
target.sendMessage(Component.text("☀ Вас ослепили фонариком!", NamedTextColor.YELLOW));
|
||||||
|
hitKiller = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hitKiller) {
|
||||||
|
player.sendMessage(Component.text("Мимо...", NamedTextColor.GRAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Spend 1 charge per use
|
||||||
|
updateItemCharges(player, charges - 1);
|
||||||
|
player.playSound(player.getLocation(), Sound.BLOCK_REDSTONE_TORCH_BURNOUT, 0.3f, 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Key ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void useKey(Player player, Survivor survivor, ItemStack item) {
|
||||||
|
int charges = DBDItems.getCharges(item);
|
||||||
|
if (charges <= 0) {
|
||||||
|
player.sendMessage(Component.text("Ключ сломан!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Actual hatch opening logic is handled by hatch system
|
||||||
|
// This just plays the animation and spends a charge
|
||||||
|
player.sendMessage(Component.text("Вы используете ключ...", NamedTextColor.GOLD));
|
||||||
|
player.playSound(player.getLocation(), Sound.BLOCK_IRON_DOOR_OPEN, 1.0f, 0.8f);
|
||||||
|
updateItemCharges(player, charges - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Map ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void useMap(Player player, ItemStack item) {
|
||||||
|
int charges = DBDItems.getCharges(item);
|
||||||
|
if (charges <= 0) {
|
||||||
|
player.sendMessage(Component.text("Карта использована!", NamedTextColor.RED));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show nearby generators / hooks in chat
|
||||||
|
player.sendMessage(Component.text("═══ Карта (ближайшие объекты) ═══", NamedTextColor.AQUA));
|
||||||
|
|
||||||
|
var genManager = session.getGeneratorManager();
|
||||||
|
if (genManager != null) {
|
||||||
|
genManager.getIncompleteGenerators().stream()
|
||||||
|
.filter(g -> g.getLocation().getWorld().equals(player.getWorld()))
|
||||||
|
.sorted((a, b) -> Double.compare(
|
||||||
|
a.getLocation().distance(player.getLocation()),
|
||||||
|
b.getLocation().distance(player.getLocation())))
|
||||||
|
.limit(3)
|
||||||
|
.forEach(g -> {
|
||||||
|
int dist = (int) g.getLocation().distance(player.getLocation());
|
||||||
|
player.sendMessage(Component.text(
|
||||||
|
" ⚙ Генератор [" + (int)g.getProgress() + "%] — " + dist + " блоков",
|
||||||
|
NamedTextColor.YELLOW));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
updateItemCharges(player, charges - 1);
|
||||||
|
player.playSound(player.getLocation(), Sound.ITEM_ARMOR_EQUIP_LEATHER, 0.5f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Utils ─────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
private void updateItemCharges(Player player, int newCharges) {
|
||||||
|
ItemStack item = player.getInventory().getItemInMainHand();
|
||||||
|
if (!DBDItems.isDBDItem(item)) return;
|
||||||
|
|
||||||
|
if (newCharges <= 0) {
|
||||||
|
player.getInventory().setItemInMainHand(null);
|
||||||
|
player.sendMessage(Component.text("Предмет израсходован.", NamedTextColor.RED));
|
||||||
|
} else {
|
||||||
|
player.getInventory().setItemInMainHand(DBDItems.setCharges(item, newCharges));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -66,6 +66,7 @@ public class GameplayPhase implements Phase {
|
||||||
private KillerController killerController;
|
private KillerController killerController;
|
||||||
private AttackListener attackListener;
|
private AttackListener attackListener;
|
||||||
private Listener vanillaDamageBlocker;
|
private Listener vanillaDamageBlocker;
|
||||||
|
private dev.loki.item.ItemUseListener itemUseListener;
|
||||||
|
|
||||||
// Listener references grabbed from plugin
|
// Listener references grabbed from plugin
|
||||||
private PlayerMoveListener moveListener;
|
private PlayerMoveListener moveListener;
|
||||||
|
|
@ -155,6 +156,12 @@ public class GameplayPhase implements Phase {
|
||||||
|
|
||||||
killerController.initialize();
|
killerController.initialize();
|
||||||
|
|
||||||
|
// --- Give starting items to survivors & register item use ---
|
||||||
|
itemUseListener = new dev.loki.item.ItemUseListener(plugin, session);
|
||||||
|
itemUseListener.register();
|
||||||
|
|
||||||
|
giveStartingItems(session);
|
||||||
|
|
||||||
// --- Wire listeners ---
|
// --- Wire listeners ---
|
||||||
moveListener = plugin.getMoveListener();
|
moveListener = plugin.getMoveListener();
|
||||||
interactListener = plugin.getInteractListener();
|
interactListener = plugin.getInteractListener();
|
||||||
|
|
@ -215,6 +222,9 @@ public class GameplayPhase implements Phase {
|
||||||
if (killerController != null) {
|
if (killerController != null) {
|
||||||
killerController.shutdown();
|
killerController.shutdown();
|
||||||
}
|
}
|
||||||
|
if (itemUseListener != null) {
|
||||||
|
itemUseListener.unregister();
|
||||||
|
}
|
||||||
if (playerManager != null) {
|
if (playerManager != null) {
|
||||||
playerManager.clear();
|
playerManager.clear();
|
||||||
}
|
}
|
||||||
|
|
@ -234,6 +244,23 @@ public class GameplayPhase implements Phase {
|
||||||
return shouldEnd;
|
return shouldEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
// Starting items
|
||||||
|
// -------------------------------------------------------------------------
|
||||||
|
|
||||||
|
private void giveStartingItems(GameSession session) {
|
||||||
|
var loot = new dev.loki.item.ChestLoot();
|
||||||
|
for (BasePlayer bp : session.getPlayers().values()) {
|
||||||
|
if (bp.isKiller()) continue;
|
||||||
|
Player p = bp.getPlayer();
|
||||||
|
if (p == null || !p.isOnline()) continue;
|
||||||
|
// Clear hotbar slots 0-3, give a random item in slot 0
|
||||||
|
p.getInventory().clear();
|
||||||
|
p.getInventory().setItem(0, loot.rollLoot());
|
||||||
|
p.sendMessage(Component.text("[DBD] Вы получили стартовый предмет!", NamedTextColor.YELLOW));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
// Hook ticking
|
// Hook ticking
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue