remove: key/toolbox/map items, keep only medkit and flashlight

This commit is contained in:
loki5512344 2026-06-03 23:33:19 +02:00
parent f9847dbb73
commit 3d6204cc83
156 changed files with 583 additions and 517 deletions

View file

@ -13,14 +13,12 @@ public class ChestLoot {
this.random = ThreadLocalRandom.current();
}
/** Roll a random item with weighted probabilities. */
/** 55% медкит, 45% фонарик. Заряды чуть рандомные. */
public ItemStack rollLoot() {
int roll = random.nextInt(100);
if (roll < 30) return DBDItems.medkit(12 + random.nextInt(5)); // 30%
if (roll < 55) return DBDItems.flashlight(6 + random.nextInt(4)); // 25%
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%
if (random.nextInt(100) < 55) {
return DBDItems.medkit(12 + random.nextInt(5));
} else {
return DBDItems.flashlight(6 + random.nextInt(3));
}
}
}

View file

@ -10,8 +10,8 @@ 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".
* Central factory for DBD custom items: Medkit and Flashlight.
* Items are identified by PersistentDataContainer tag "mdbd:item_type".
* Charges are stored as "mdbd:charges".
*/
public final class DBDItems {
@ -31,7 +31,7 @@ public final class DBDItems {
charges, ItemType.MEDKIT.getDefaultCharges(),
List.of(
"<gray>Лечит одно состояние здоровья.",
"<yellow>ПКМ <gray>— начать лечение"
"<yellow>ПКМ <gray>— начать самолечение"
),
"MEDKIT"
);
@ -55,54 +55,6 @@ public final class DBDItems {
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,
@ -111,7 +63,6 @@ public final class DBDItems {
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));
@ -148,51 +99,18 @@ public final class DBDItems {
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);
return switch (type) {
case "MEDKIT" -> medkit(newCharges);
case "FLASHLIGHT" -> flashlight(newCharges);
default -> item;
};
}
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>— активировать");
};
}
}

View file

@ -1,96 +0,0 @@
package dev.loki.item;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Arrays;
public class ItemKey implements BaseItem {
private static final NamespacedKey ITEM_KEY = new NamespacedKey("mdbd", "item_type");
private static final NamespacedKey CHARGES_KEY = new NamespacedKey("mdbd", "charges");
private final int charges;
public ItemKey() {
this(ItemType.KEY.getDefaultCharges());
}
public ItemKey(int charges) {
this.charges = charges;
}
@Override
public ItemType getType() {
return ItemType.KEY;
}
@Override
public ItemStack createItemStack() {
ItemStack item = new ItemStack(Material.TRIPWIRE_HOOK);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName("§6" + getType().getDisplayName());
meta.setLore(Arrays.asList(
"§7" + getType().getDescription(),
"§eCharges: §f" + charges + "/" + getType().getDefaultCharges(),
"§7Opens the Hatch instantly"
));
PersistentDataContainer pdc = meta.getPersistentDataContainer();
pdc.set(ITEM_KEY, PersistentDataType.STRING, getItemId());
pdc.set(CHARGES_KEY, PersistentDataType.INTEGER, charges);
item.setItemMeta(meta);
}
return item;
}
@Override
public boolean canUse(Player player) {
return charges > 0;
}
@Override
public void onUse(Player player) {
if (charges > 0) {
player.sendMessage("§6You use the Key on the Hatch...");
}
}
@Override
public void onDeplete(Player player) {
player.getInventory().setItemInMainHand(null);
player.sendMessage("§cYour key breaks!");
}
@Override
public int getCharges() {
return charges;
}
@Override
public void setCharges(int charges) {
}
@Override
public boolean isDepleted() {
return charges <= 0;
}
@Override
public String getItemId() {
return "KEY";
}
public static boolean isKey(ItemStack item) {
if (item == null || !item.hasItemMeta()) return false;
PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer();
return "KEY".equals(pdc.get(ITEM_KEY, PersistentDataType.STRING));
}
}

View file

@ -1,96 +0,0 @@
package dev.loki.item;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Arrays;
public class ItemMap implements BaseItem {
private static final NamespacedKey ITEM_KEY = new NamespacedKey("mdbd", "item_type");
private static final NamespacedKey CHARGES_KEY = new NamespacedKey("mdbd", "charges");
private final int charges;
public ItemMap() {
this(ItemType.MAP.getDefaultCharges());
}
public ItemMap(int charges) {
this.charges = charges;
}
@Override
public ItemType getType() {
return ItemType.MAP;
}
@Override
public ItemStack createItemStack() {
ItemStack item = new ItemStack(Material.MAP);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName("§d" + getType().getDisplayName());
meta.setLore(Arrays.asList(
"§7" + getType().getDescription(),
"§eCharges: §f" + charges + "/" + getType().getDefaultCharges(),
"§7Right-click to reveal generators"
));
PersistentDataContainer pdc = meta.getPersistentDataContainer();
pdc.set(ITEM_KEY, PersistentDataType.STRING, getItemId());
pdc.set(CHARGES_KEY, PersistentDataType.INTEGER, charges);
item.setItemMeta(meta);
}
return item;
}
@Override
public boolean canUse(Player player) {
return charges > 0;
}
@Override
public void onUse(Player player) {
if (charges > 0) {
player.sendMessage("§dThe Map reveals nearby generators!");
}
}
@Override
public void onDeplete(Player player) {
player.getInventory().setItemInMainHand(null);
player.sendMessage("§cYour map has faded!");
}
@Override
public int getCharges() {
return charges;
}
@Override
public void setCharges(int charges) {
}
@Override
public boolean isDepleted() {
return charges <= 0;
}
@Override
public String getItemId() {
return "MAP";
}
public static boolean isMap(ItemStack item) {
if (item == null || !item.hasItemMeta()) return false;
PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer();
return "MAP".equals(pdc.get(ITEM_KEY, PersistentDataType.STRING));
}
}

View file

@ -1,98 +0,0 @@
package dev.loki.item;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import java.util.Arrays;
public class ItemToolbox implements BaseItem {
private static final NamespacedKey ITEM_KEY = new NamespacedKey("mdbd", "item_type");
private static final NamespacedKey CHARGES_KEY = new NamespacedKey("mdbd", "charges");
private final int charges;
public ItemToolbox() {
this(ItemType.TOOLBOX.getDefaultCharges());
}
public ItemToolbox(int charges) {
this.charges = charges;
}
@Override
public ItemType getType() {
return ItemType.TOOLBOX;
}
@Override
public ItemStack createItemStack() {
ItemStack item = new ItemStack(Material.CHEST);
ItemMeta meta = item.getItemMeta();
if (meta != null) {
meta.setDisplayName("§b" + getType().getDisplayName());
meta.setLore(Arrays.asList(
"§7" + getType().getDescription(),
"§eCharges: §f" + charges + "/" + getType().getDefaultCharges(),
"§7Hold while repairing for bonus speed"
));
PersistentDataContainer pdc = meta.getPersistentDataContainer();
pdc.set(ITEM_KEY, PersistentDataType.STRING, getItemId());
pdc.set(CHARGES_KEY, PersistentDataType.INTEGER, charges);
item.setItemMeta(meta);
}
return item;
}
@Override
public boolean canUse(Player player) {
return charges > 0;
}
@Override
public void onUse(Player player) {
player.sendMessage("§bYour toolbox speeds up the repair!");
}
@Override
public void onDeplete(Player player) {
player.getInventory().setItemInMainHand(null);
player.sendMessage("§cYour toolbox is empty!");
}
@Override
public int getCharges() {
return charges;
}
@Override
public void setCharges(int charges) {
}
@Override
public boolean isDepleted() {
return charges <= 0;
}
@Override
public String getItemId() {
return "TOOLBOX";
}
public static boolean isToolbox(ItemStack item) {
if (item == null || !item.hasItemMeta()) return false;
PersistentDataContainer pdc = item.getItemMeta().getPersistentDataContainer();
return "TOOLBOX".equals(pdc.get(ITEM_KEY, PersistentDataType.STRING));
}
public static double getRepairBonus() {
return 0.15;
}
}

View file

@ -2,10 +2,7 @@ package dev.loki.item;
public enum ItemType {
MEDKIT("Medkit", "Heals one health state", 16),
FLASHLIGHT("Flashlight", "Blinds the Killer", 8),
TOOLBOX("Toolbox", "Speeds up repairs", 16),
KEY("Key", "Opens the Hatch", 32),
MAP("Map", "Reveals objectives", 16);
FLASHLIGHT("Flashlight", "Blinds the Killer", 8);
private final String displayName;
private final String description;
@ -17,15 +14,7 @@ public enum ItemType {
this.defaultCharges = defaultCharges;
}
public String getDisplayName() {
return displayName;
}
public String getDescription() {
return description;
}
public int getDefaultCharges() {
return defaultCharges;
}
public String getDisplayName() { return displayName; }
public String getDescription() { return description; }
public int getDefaultCharges() { return defaultCharges; }
}

View file

@ -27,14 +27,13 @@ import java.util.Map;
import java.util.UUID;
/**
* Handles right-click usage of DBD custom items during gameplay.
* Handles right-click usage of DBD custom items (Medkit, Flashlight) 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
@ -68,33 +67,28 @@ public class ItemUseListener implements Listener {
ItemStack hand = player.getInventory().getItemInMainHand();
if (!DBDItems.isDBDItem(hand)) return;
event.setCancelled(true);
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);
if ("MEDKIT".equals(type)) {
useMedkit(player, (Survivor) bp, hand);
} else if ("FLASHLIGHT".equals(type)) {
useFlashlight(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) {
if (survivor.getState() != PlayerState.INJURED) {
player.sendMessage(Component.text("Вы не ранены!", NamedTextColor.RED));
return;
}
@ -106,11 +100,10 @@ public class ItemUseListener implements Listener {
}
player.sendMessage(Component.text("Начинаете самолечение...", NamedTextColor.GREEN));
player.sendActionBar(Component.text("⚕ Самолечение...", NamedTextColor.GREEN));
BukkitTask task = new BukkitRunnable() {
int ticks = 0;
int chargesLeft = charges;
final int startCharges = charges;
@Override
public void run() {
@ -121,29 +114,40 @@ public class ItemUseListener implements Listener {
}
ticks++;
chargesLeft = Math.max(0, charges - ticks);
// Update action bar progress
int filled = (int)(8.0 * ticks / MEDKIT_TICKS);
// Прогресс в action bar
int filled = (int) (8.0 * ticks / MEDKIT_TICKS);
int empty = 8 - filled;
int pct = (int) (100.0 * ticks / MEDKIT_TICKS);
player.sendActionBar(Component.text(
"⚕ " + "█".repeat(filled) + "░".repeat(empty) +
" " + (int)(100.0 * ticks / MEDKIT_TICKS) + "%",
"⚕ " + "█".repeat(filled) + "░".repeat(empty) + " " + pct + "%",
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);
// Звук каждые 20 тиков
if (ticks % 20 == 0) {
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_HURT, 0.15f, 1.8f);
// Обновляем заряды на предмете
int usedCharges = (int)(1.0 * ticks / MEDKIT_TICKS * startCharges);
int remaining = Math.max(1, startCharges - usedCharges);
player.getInventory().setItemInMainHand(DBDItems.setCharges(
player.getInventory().getItemInMainHand(), remaining));
}
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));
player.playSound(player.getLocation(), Sound.ENTITY_PLAYER_LEVELUP, 0.6f, 1.5f);
// Тратим заряды
int remaining = startCharges - 8;
if (remaining <= 0) {
player.getInventory().setItemInMainHand(null);
player.sendMessage(Component.text("Медкит израсходован.", NamedTextColor.RED));
} else {
player.getInventory().setItemInMainHand(DBDItems.medkit(remaining));
}
activeHeals.remove(uuid);
cancel();
}
@ -162,7 +166,7 @@ public class ItemUseListener implements Listener {
}
}
// ── Flashlight ────────────────────────────────────────────────────────────
// ── Фонарик ───────────────────────────────────────────────────────────────
private void useFlashlight(Player player, ItemStack item) {
int charges = DBDItems.getCharges(item);
@ -171,7 +175,7 @@ public class ItemUseListener implements Listener {
return;
}
// Ray-trace to find killer within 15 blocks
// Луч 15 блоков
var result = player.getWorld().rayTraceEntities(
player.getEyeLocation(),
player.getEyeLocation().getDirection(),
@ -184,10 +188,10 @@ public class ItemUseListener implements Listener {
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.addPotionEffect(new PotionEffect(PotionEffectType.SLOWNESS, 50, 2, 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));
player.sendMessage(Component.text("✔ Ослепили киллера!", NamedTextColor.YELLOW));
hitKiller = true;
}
}
@ -196,69 +200,15 @@ public class ItemUseListener implements Listener {
player.sendMessage(Component.text("Мимо...", NamedTextColor.GRAY));
}
// Spend 1 charge per use
updateItemCharges(player, charges - 1);
// 1 заряд за использование
int remaining = charges - 1;
if (remaining <= 0) {
player.getInventory().setItemInMainHand(null);
player.sendMessage(Component.text("Фонарик разрядился.", NamedTextColor.RED));
} else {
player.getInventory().setItemInMainHand(DBDItems.flashlight(remaining));
}
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));
}
}
}