PropManager fix. Added abstract BlockAndSeekItem. Created LazyLocation. Added many items for seeker/hider

This commit is contained in:
2025-12-03 18:07:11 +03:00
parent fd94112d87
commit 3cbcfb385f
125 changed files with 2173 additions and 575 deletions

View File

@@ -1,40 +1,43 @@
package hdvtdev.blockandseek;
import hdvtdev.blockandseek.eventListeners.RequiredEventListener;
import hdvtdev.blockandseek.eventListeners.EventListener;
import hdvtdev.blockandseek.eventListeners.ForceControlEventListener;
import hdvtdev.blockandseek.eventListeners.RequiredEventListener;
import hdvtdev.blockandseek.items.Pistol;
import hdvtdev.blockandseek.managers.*;
import hdvtdev.blockandseek.objects.*;
import hdvtdev.blockandseek.roulette.RouletteCreator;
import java.io.File;
import java.time.Duration;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import me.libraryaddict.disguise.LibsDisguises;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.*;
import org.bukkit.attribute.Attribute;
import org.bukkit.attribute.AttributeInstance;
import org.bukkit.attribute.AttributeModifier;
import org.bukkit.block.Block;
import org.bukkit.command.CommandSender;
import org.bukkit.command.PluginCommand;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.RayTraceResult;
import org.incendo.cloud.bukkit.CloudBukkitCapabilities;
import org.incendo.cloud.bukkit.parser.location.LocationParser;
import org.incendo.cloud.execution.ExecutionCoordinator;
import org.incendo.cloud.paper.LegacyPaperCommandManager;
import org.incendo.cloud.parser.standard.StringParser;
import java.io.File;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
public class BlockAndSeek extends JavaPlugin {
private static JavaPlugin javaPlugin;
@@ -61,32 +64,41 @@ public class BlockAndSeek extends JavaPlugin {
return javaPlugin.getLogger();
}
@Override
public void onEnable() {
javaPlugin = this;
LibsDisguises libsDisguises = (LibsDisguises) Bukkit.getPluginManager().getPlugin("LibsDisguises");
LibsDisguises libsDisguises =
(LibsDisguises) Bukkit.getPluginManager().getPlugin(
"LibsDisguises"
);
if (libsDisguises == null) {
getLogger().severe("LibsDisguises not found! It's required for the plugin to work!");
getLogger().severe(
"LibsDisguises not found! It's required for the plugin to work!"
);
super.onDisable();
}
this.init();
PluginCommand command = Objects.requireNonNull(getCommand("blockandseek"));
PluginCommand command = Objects.requireNonNull(
getCommand("blockandseek")
);
PluginManager manager = getServer().getPluginManager();
boolean forceControl = Config.forceControl();
if (forceControl) getLogger().info("Using force control");
manager.registerEvents(forceControl ? new ForceControlEventListener() : new EventListener(), this);
manager.registerEvents(
forceControl
? new ForceControlEventListener()
: new EventListener(),
this
);
manager.registerEvents(new RequiredEventListener(), this);
}
private void init() {
try {
File dataFolder = getDataFolder();
if (!dataFolder.exists()) {
@@ -109,105 +121,224 @@ public class BlockAndSeek extends JavaPlugin {
saveResource("languages/en_US.yml", false);
}
MapsManager.loadMaps();
TranslationManager.loadLanguages();
MapsManager.loadMaps();
commandManager = LegacyPaperCommandManager.createNative(
this,
ExecutionCoordinator.simpleCoordinator()
this,
ExecutionCoordinator.simpleCoordinator()
);
if (commandManager.hasCapability(CloudBukkitCapabilities.NATIVE_BRIGADIER)) {
if (
commandManager.hasCapability(
CloudBukkitCapabilities.NATIVE_BRIGADIER
)
) {
try {
commandManager.registerBrigadier();
} catch (IllegalStateException ignored) {
}
} catch (IllegalStateException ignored) {}
}
this.registerCommands();
} catch (Exception e) {
getLogger().severe("Cloud err: " + e.getMessage());
e.printStackTrace();
}
}
private final PropManager propManager = new PropManager();
private static final UUID ZOOM_UUID = UUID.randomUUID();
private void registerCommands() {
var root = commandManager.commandBuilder("blockandseek");
var editBase = root
.literal("editMap")
.required("map_name", StringParser.stringParser(), MapsManager.mapSuggestions);
.literal("editMap")
.required(
"map_name",
StringParser.stringParser(),
MapsManager.mapSuggestions
);
//edit commands
commandManager.command(
editBase.literal("setLobby") // Это наш <parameter> как литерал
.required("location", LocationParser.locationParser())
.handler(ctx -> {
})
editBase
.literal("setLobby")
.required("location", LocationParser.locationParser())
.handler(ctx -> {})
);
commandManager.command(
editBase.literal("setLocation")
.required("location", LocationParser.locationParser()) // Это <value>
.handler(ctx -> {
String configName = ctx.get("config_name");
Location location = ctx.get("location");
ctx.sender().sendMessage("В конфиге " + configName + " точка установлена: " + location.toString());
})
);
commandManager.command(root
.literal("maps")
editBase
.literal("setLocation")
.required("location", LocationParser.locationParser())
.handler(ctx -> {
ctx.sender().sendMessage(MapsManager.getMaps().stream().map(BlockAndSeekMap::getWorld).collect(Collectors.joining(", ")));
String configName = ctx.get("config_name");
Location location = ctx.get("location");
ctx
.sender()
.sendMessage(
"В конфиге " +
configName +
" точка установлена: " +
location.toString()
);
})
);
commandManager.command(root
commandManager.command(
root
.literal("testMessage")
.handler(ctx -> {
ctx.sender().sendMessage(MiniMessage.miniMessage().deserialize(" <gradient:#FFAA00:#FFD700><bold>System</bold></gradient> <dark_gray>»</dark_gray> <yellow>Ваш игровой режим изменен.</yellow>\n"));
ctx
.sender()
.sendMessage(
MiniMessage.miniMessage().deserialize(
" <gradient:#FFAA00:#FFD700><bold>System</bold></gradient> <dark_gray>»</dark_gray> <yellow>Ваш игровой режим изменен.</yellow>\n"
)
);
})
);
commandManager.command(root
commandManager.command(
root
.literal("testGlow")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
RayTraceResult result = player.rayTraceBlocks(128);
if (result != null) {
Block block = result.getHitBlock();
if (block != null) {
GlowUtil.highlightBlock(player, block.getLocation().toBlockLocation());
}
}
}
})
);
commandManager.command(
root
.literal("menuTest")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
player.getInventory().addItem(ItemManager.getMenuItem());
player
.getInventory()
.addItem(ItemManager.getItem(Items.MENU).getTranslated(player));
}
})
);
commandManager.command(
root
.literal("morphItem")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
player
.getInventory()
.addItem(ItemManager.getItem(Items.MORPH).getTranslated(player));
}
})
);
commandManager.command(root
commandManager.command(
root
.literal("pistolGet")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
player
.getInventory()
.addItem(ItemManager.getItem(Items.PISTOL).getTranslated(player));
}
})
);
commandManager.command(
root
.literal("clear")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
AttributeInstance attribute = player.getAttribute(
Attribute.GENERIC_MOVEMENT_SPEED
);
if (
attribute != null &&
attribute.getModifier(ZOOM_UUID) != null
) {
attribute.removeModifier(ZOOM_UUID);
}
}
})
);
commandManager.command(
root
.literal("pistolTest")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
player.getInventory().addItem(ItemManager.getItem(Items.MORPH).getTranslated(player));
}
})
);
commandManager.command(
root
.literal("grenadeTest")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
player.getInventory().addItem(ItemManager.getItem(Items.GRENADE).getTranslated(player));
}
})
);
commandManager.command(
root
.literal("reload")
.handler(ctx -> {
var sender = ctx.sender();
if (Config.loadConfig()) {
MapsManager.loadMaps();
TranslationManager.loadLanguages();
sender.sendMessage("Configs were reloaded!");
} else sender.sendMessage("Failed to reload configs! Check server logs.");
})
);
commandManager.command(
root
.literal("configTest")
.handler(ctx -> ctx.sender().sendMessage(Config.toStaticString()))
);
commandManager.command(
root
.literal("localeTest")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
player.sendMessage("Locale is: " + player.locale());
player.sendMessage(TranslationManager.get(player, TranslationKey.FREEZE_ITEM));
player.sendMessage(
TranslationManager.get(
player,
TranslationKey.FREEZE_ITEM
)
);
}
})
);
commandManager.command(root
commandManager.command(
root
.literal("createGame")
.permission(perm)
.required("map_name", StringParser.stringParser(), MapsManager.mapSuggestions)
.required(
"map_name",
StringParser.stringParser(),
MapsManager.mapSuggestions
)
.handler(context -> {
String name = context.get("map_name");
GamesManager.createGame(name);
@@ -217,18 +348,11 @@ public class BlockAndSeek extends JavaPlugin {
})
);
commandManager.command(root
.literal("createMap")
.permission(perm)
.required("map_name", StringParser.stringParser(), MapsManager.worldSuggestions)
.handler(context -> {
String name = context.get("map_name");
MapsManager.createMap(name);
context.sender().sendMessage("Map probably created");
})
);
commandManager.command(root
commandManager.command(
root
.literal("joinGame")
.permission(perm)
.required("name", StringParser.stringParser())
@@ -241,93 +365,10 @@ public class BlockAndSeek extends JavaPlugin {
})
);
commandManager.command(
editBase.literal("generateBlocks")
.handler(ctx -> {
if (ctx.sender() instanceof Player player) {
String mapName = ctx.get("map_name");
BlockAndSeekMap map = MapsManager.getMap(mapName);
var cached = BlocksGenerator.get(map.getSpawn().getWorld().getName());
if (cached != null) {
new RouletteCreator(player, cached);
} else {
BlocksGenerator.getSortedBlockStats(player.getLocation(), 32, (var list) -> {
List<Map.Entry<Material, Long>> selectedBlocks = new ArrayList<>(list);
// 2. Перемешиваем и берем 30 случайных (или меньше, если всего блоков меньше 30)
Collections.shuffle(selectedBlocks);
if (selectedBlocks.size() > 30) {
selectedBlocks = selectedBlocks.subList(0, 30);
}
selectedBlocks.sort((a, b) -> Long.compare(b.getValue(), a.getValue()));
List<Map.Entry<Material, Rarity>> result = new ArrayList<>();
// 4. Считаем общий вес редкостей (100 + 50 + 25 + ...) = 189
int totalWeight = Arrays.stream(Rarity.values()).mapToInt(Rarity::getChance).sum();
// Шаг веса на один блок.
// Представь длинную полоску длиной 189 единиц. Мы режем её на 30 равных частей.
// Куда попадает разрез — такая и редкость.
double weightStep = (double) totalWeight / selectedBlocks.size();
double currentWeightPosition = 0; // Текущая позиция на шкале весов
// Идем по отсортированному списку (от самых частых к самым редким)
for (Map.Entry<Material, Long> entry : selectedBlocks) {
// Определяем, в какой диапазон редкости попадает текущий блок
Rarity assignedRarity = Rarity.COMMON; // Дефолт
int weightCursor = 0;
for (Rarity r : Rarity.values()) {
weightCursor += r.getChance();
// Если текущая позиция веса меньше границы этой редкости — мы нашли её
// (Добавляем половину шага, чтобы брать "центр" диапазона блока для точности)
if ((currentWeightPosition + weightStep / 2) <= weightCursor) {
assignedRarity = r;
break;
}
}
result.add(new AbstractMap.SimpleEntry<>(entry.getKey(), assignedRarity));
// Сдвигаемся дальше по шкале
currentWeightPosition += weightStep;
}
var r = result.stream().map(e -> new PropBlock(new ItemStack(e.getKey()), e.getValue())).toList();
map.setBlocks(r);
map.save();
BlocksGenerator.addCache(player.getLocation().getWorld().getName(), r);
});
}
}
})
);
}
private void spawnSmokeCloud(Location center) {
new BukkitRunnable() {
int ticksPassed = 0;
@@ -343,20 +384,35 @@ public class BlockAndSeek extends JavaPlugin {
World world = center.getWorld();
if (world == null) return;
world.spawnParticle(
Particle.EXPLOSION,
center,
15,
1.2,
1.2,
1.2,
0.05
);
world.spawnParticle(Particle.EXPLOSION_HUGE, center, 15, 1.2, 1.2, 1.2, 0.05);
for (var entity : world.getNearbyEntities(center, 2.5, 2.5, 2.5)) {
if (entity instanceof Player player && player.getLocation().distanceSquared(center) <= 9) {
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, 40, 1));
for (var entity : world.getNearbyEntities(
center,
2.5,
2.5,
2.5
)) {
if (
entity instanceof Player player &&
player.getLocation().distanceSquared(center) <= 9
) {
player.addPotionEffect(
new PotionEffect(PotionEffectType.BLINDNESS, 40, 1)
);
}
}
ticksPassed++;
}
}.runTaskTimer(this, 0L, 5L);
}
.runTaskTimer(this, 0L, 5L);
}
}

View File

@@ -4,26 +4,39 @@ import eu.okaeri.configs.ConfigManager;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import eu.okaeri.configs.annotation.Exclude;
import eu.okaeri.configs.exception.OkaeriException;
import eu.okaeri.configs.serdes.commons.SerdesCommons;
import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer;
import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit;
import hdvtdev.blockandseek.objects.LazyLocation;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.WorldCreator;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
public class Config extends OkaeriConfig {
static {
loadConfig();
}
@Exclude
private static Config config;
static {
@Comment("Server options.")
private ServerSettings serverSettings = new ServerSettings();
@Comment("Spawn location. Useless if the Server.forceControl is false.")
private LazyLocation spawn = new LazyLocation("world");
@Comment("Show hidden BlockAndSeek commands.")
private boolean enableDebugCommands = false;
public static boolean loadConfig() {
try {
File configFile = new File(BlockAndSeek.getPluginDataFolder(), "config.yml");
if (!configFile.exists()) configFile.createNewFile();
config = ConfigManager.create(Config.class, (it) -> {
it.withConfigurer(
new YamlBukkitConfigurer(),
@@ -33,23 +46,16 @@ public class Config extends OkaeriConfig {
it.withBindFile(configFile);
it.withLogger(BlockAndSeek.getPluginLogger());
it.saveDefaults();
it.load(true);
});
config.load();
config.save();
} catch (Exception e) {
return true;
} catch (OkaeriException e) {
BlockAndSeek.getPluginLogger().severe("Failed to load config.yml: " + e.getMessage());
e.printStackTrace();
return false;
}
}
@Comment("Server options.")
private ServerSettings serverSettings = new ServerSettings();
@Comment("Spawn location. Useless if the Server.forceControl is false.")
private Location spawn = Objects.requireNonNull(Bukkit.createWorld(new WorldCreator("world"))).getSpawnLocation();
@Comment("Show hidden BlockAndSeek commands.")
private boolean enableDebugCommands = false;
public static boolean debugEnabled() {
return config.enableDebugCommands;
@@ -60,7 +66,7 @@ public class Config extends OkaeriConfig {
}
public static Location spawn() {
return config.spawn;
return config.spawn.getLocation();
}
public static String toStaticString() {

View File

@@ -0,0 +1,83 @@
package hdvtdev.blockandseek;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
import com.github.retrooper.packetevents.protocol.entity.data.EntityData;
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataTypes;
import com.github.retrooper.packetevents.util.Vector3d;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDestroyEntities;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
public class GlowUtil {
public static int highlightBlock(Player viewer, Location blockLoc) {
int entityId = -ThreadLocalRandom.current().nextInt(10000, Integer.MAX_VALUE);
UUID uuid = UUID.randomUUID();
Vector3d position = new Vector3d(
blockLoc.getX(),
blockLoc.getY(),
blockLoc.getZ()
);
WrapperPlayServerSpawnEntity spawnPacket = new WrapperPlayServerSpawnEntity(
entityId,
Optional.of(uuid),
EntityTypes.INTERACTION,
position,
0f, 0f, 0f,
0,
Optional.empty()
);
List<EntityData<?>> metaDataList = new ArrayList<>();
byte flags = (byte) 0x40;
metaDataList.add(new EntityData<>(0, EntityDataTypes.BYTE, flags));
metaDataList.add(new EntityData<>(8, EntityDataTypes.FLOAT, 1.0f));
metaDataList.add(new EntityData<>(9, EntityDataTypes.FLOAT, 1.0f));
WrapperPlayServerEntityMetadata metadataPacket = new WrapperPlayServerEntityMetadata(entityId, metaDataList);
PacketEvents.getAPI().getPlayerManager().sendPacket(viewer, spawnPacket);
PacketEvents.getAPI().getPlayerManager().sendPacket(viewer, metadataPacket);
return entityId;
}
public static void removeGlow(Player viewer, int entityId) {
WrapperPlayServerDestroyEntities destroyPacket = new WrapperPlayServerDestroyEntities(new int[]{entityId});
PacketEvents.getAPI().getPlayerManager().sendPacket(viewer, destroyPacket);
}
public static void setPlayerGlowing(Player viewer, Player target, boolean glowing) {
List<EntityData<?>> metaDataList = new ArrayList<>();
byte flags = 0;
if (target.getFireTicks() > 0) flags |= 0x01;
if (target.isSneaking()) flags |= 0x02;
if (target.isSprinting()) flags |= 0x08;
if (target.isSwimming()) flags |= 0x10;
if (target.isInvisible()) flags |= 0x20;
if (glowing) {
flags |= 0x40;
} else {
if (target.isGlowing()) flags |= 0x40;
}
if (target.isGliding()) flags |= (byte) 0x80;
metaDataList.add(new EntityData<>(0, EntityDataTypes.BYTE, flags));
WrapperPlayServerEntityMetadata metadataPacket = new WrapperPlayServerEntityMetadata(target.getEntityId(), metaDataList);
PacketEvents.getAPI().getPlayerManager().sendPacket(viewer, metadataPacket);
}
}

View File

@@ -13,6 +13,7 @@ public class Keys {
public static final NamespacedKey FACE_CHANGING_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekFaceChangingItem");
public static final NamespacedKey MENU_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekMenuItem");
public static final NamespacedKey LEAVE_ITEM = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekLeaveItem");
public static final NamespacedKey PISTOL = new NamespacedKey(BlockAndSeek.getInstance(), "BlockAndSeekPistol");
public static final Team NO_COLLIDE_TEAM;

View File

@@ -1,12 +1,12 @@
package hdvtdev.blockandseek;
import hdvtdev.blockandseek.managers.PropManager;
import me.libraryaddict.disguise.DisguiseAPI;
import org.bukkit.GameMode;
import org.bukkit.attribute.Attribute;
import org.bukkit.entity.Player;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.potion.PotionEffect;
public final class Utils {
@@ -18,6 +18,20 @@ public final class Utils {
p.setExp(0.9998f * ((float) level / 100));
}
public static void damageSeeker(Player seeker, int damage) {
seeker.damage(damage);
setLevelWithBar(seeker, (int) (seeker.getHealth() * 5));
}
public static void healSeeker(Player player, double heal) {
double currentHealth = player.getHealth();
if (currentHealth < 20) {
double newHealth = Math.min(currentHealth + heal, 20);
player.setHealth(newHealth);
setLevelWithBar(player, (int) Math.round(player.getHealth() * 5));
}
}
public static void clearPlayer(Player player) {
DisguiseAPI.undisguiseToAll(player);
@@ -25,7 +39,7 @@ public final class Utils {
player.getInventory().clear();
player.getInventory().setArmorContents(null);
player.getInventory().setExtraContents(null);
PropManager.removePlayer(player);
player.setGameMode(GameMode.SURVIVAL);
@@ -38,6 +52,8 @@ public final class Utils {
player.setSaturation(5.0f);
player.setExhaustion(0f);
player.setInvulnerable(false);
player.setInvisible(false);
player.setVisibleByDefault(true);
player.setLevel(0);
player.setExp(0f);
player.setFireTicks(0);

View File

@@ -7,12 +7,15 @@ import hdvtdev.blockandseek.GuiHolder;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.menus.GamesMenu;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.block.BlockDamageEvent;
import org.bukkit.event.entity.EntityDamageByEntityEvent;
import org.bukkit.event.entity.EntityDismountEvent;
@@ -29,6 +32,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.RayTraceResult;
import java.util.HashSet;
import java.util.Set;
@@ -40,7 +44,11 @@ public class RequiredEventListener implements Listener {
@EventHandler
public void onPlayerJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
if (!Keys.NO_COLLIDE_TEAM.hasPlayer(player)) {
Keys.NO_COLLIDE_TEAM.addPlayer(player);
}
//player.setResourcePack("https://gitea.hdvtdev.tech/hdvt/idk/raw/branch/main/pack.zip", "f32bf084b01b57601d7ae3711e5c88e31ce637a3", true);
}
@EventHandler
@@ -48,28 +56,12 @@ public class RequiredEventListener implements Listener {
}
//todo remove
private final PropManager propManager = new PropManager();
private final Set<UUID> coolDown = new HashSet<>();
@EventHandler
public void onRightClick(PlayerInteractEvent event) {
if (event.getHand() != EquipmentSlot.HAND) return;
if (event.getAction().isLeftClick()) return;
public void onPlayerClick(PlayerInteractEvent event) {
Player player = event.getPlayer();
ItemStack item = event.getItem();
BlockAndSeekItem item = BlockAndSeekItem.tryCast(event.getItem());
if (item != null) {
ItemMeta meta = item.getItemMeta();
if (meta != null) {
PersistentDataContainer dataContainer = meta.getPersistentDataContainer();
if (dataContainer.has(Keys.MENU_ITEM)) {
new GamesMenu(player);
event.setCancelled(true);
}
}
item.onRawInteract(event);
}
}

View File

@@ -0,0 +1,57 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.Keys;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
public class DashItem extends BlockAndSeekItem {
public DashItem() {
super(Items.DASH, Duration.ofSeconds(75), ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
Player player = event.getPlayer();
Location location = player.getLocation();
player.playSound(location, Sound.ENTITY_BAT_TAKEOFF, 1f, 1f);
Vector direction = location.getDirection();
//direction.setY(0).normalize();
direction.multiply(2.0);
direction.setY(0.4);
player.setVelocity(direction);
}
@Override
protected @NotNull ItemStack buildRawItem() {
ItemStack itemStack = new ItemStack(Material.LAPIS_LAZULI);
itemStack.editMeta(itemMeta -> {
itemMeta.addEnchant(Enchantment.INFINITY, 1, true);
itemMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
});
return itemStack;
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.DASH_ITEM);
}
}

View File

@@ -0,0 +1,38 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.Keys;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
public class FaceChangingItem extends BlockAndSeekItem {
public FaceChangingItem() {
super(Items.FACE_CHANGING, Duration.ZERO, ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
PropManager.changePropDirection(event.getPlayer());
}
@Override
protected @NotNull ItemStack buildRawItem() {
return new ItemStack(Material.NETHER_STAR);
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.FACE_CHANGING_ITEM);
}
}

View File

@@ -0,0 +1,40 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import net.kyori.adventure.text.Component;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
public class FreezeItem extends BlockAndSeekItem {
public FreezeItem() {
super(Items.FREEZE, Duration.ofMillis(100), ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
Player player = event.getPlayer();
player.sendActionBar(Component.text(PropManager.freezeOrUnfreeze(player).toString()));
}
@Override
protected @NotNull ItemStack buildRawItem() {
ItemStack itemStack = new ItemStack(Material.HEART_OF_THE_SEA);
itemStack.editMeta(itemMeta -> itemMeta.addEnchant(Enchantment.INFINITY, 1, true));
return itemStack;
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.FREEZE_ITEM);
}
}

View File

@@ -0,0 +1,148 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.Utils;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
public class Grenade extends BlockAndSeekItem {
public Grenade() {
super(Items.GRENADE, Duration.ofSeconds(10), ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
Player player = event.getPlayer();
final Location location = player.getEyeLocation();
final Vector velocity = location.getDirection().multiply(1.5);
location.getWorld().playSound(location, Sound.BLOCK_CHAIN_STEP, 1f, 1f);
final ArmorStand tntVisual = location.getWorld().spawn(location, ArmorStand.class, entity -> {
entity.setVisible(false);
entity.setGravity(false);
entity.setMarker(true);
entity.setSmall(true);
entity.getEquipment().setHelmet(new ItemStack(Material.TNT));
});
new BukkitRunnable() {
final double gravity = 0.05;
final double drag = 0.99;
@Override
public void run() {
location.getWorld().spawnParticle(Particle.CAMPFIRE_COSY_SMOKE, location, 1, 0, 0, 0, 0);
Vector move = velocity.clone();
RayTraceResult hit = location.getWorld().rayTraceBlocks(location, move, move.length());
if (hit != null && hit.getHitBlock() != null) {
tntVisual.remove();
Location hitLoc = hit.getHitPosition().toLocation(location.getWorld());
int radius = 4;
List<UUID> props = hitLoc.getNearbyEntities(radius, radius, radius).stream().filter(entity -> entity instanceof Player p && !p.equals(player))
.map(Entity::getUniqueId).collect(Collectors.toCollection(ArrayList::new));
location.getWorld().playSound(hitLoc, Sound.ENTITY_GENERIC_EXPLODE, 1f, 1f);
location.getWorld().spawnParticle(Particle.EXPLOSION, hitLoc, 1);
List<Block> affectedBlocks = new ArrayList<>();
for (int x = -radius; x <= radius; x++) {
for (int y = -radius; y <= radius; y++) {
for (int z = -radius; z <= radius; z++) {
Block b = hitLoc.getBlock().getRelative(x, y, z);
if (hitLoc.distance(b.getLocation().add(0.5, 0.5, 0.5)) <= radius) {
if (!b.getType().isAir()) {
affectedBlocks.add(b);
}
}
}
}
}
for (Block b : affectedBlocks) {
Location blockLocation = b.getLocation();
UUID maybeProp = PropManager.getProp(blockLocation);
if (maybeProp != null) {
PropManager.forceUnfreeze(maybeProp);
props.add(maybeProp);
}
}
if (!props.isEmpty()) {
player.playSound(player, Sound.ENTITY_ARROW_HIT_PLAYER, 1f, 1f);
for (UUID uuid : props) {
Player hider = Bukkit.getPlayer(uuid);
if (hider != null) {
hider.damage(13.5);
Utils.healSeeker(player, 13.5);
}
}
} else Utils.damageSeeker(player, 25);
this.cancel();
return;
}
location.add(move);
tntVisual.teleport(location.clone().subtract(0, 0.7, 0));
velocity.multiply(drag);
velocity.setY(velocity.getY() - gravity);
if (location.getY() < location.getWorld().getMinHeight() || !location.getWorld().isChunkLoaded(location.getBlockX() >> 4, location.getBlockZ() >> 4)) {
tntVisual.remove();
this.cancel();
}
}
}.runTaskTimer(BlockAndSeek.getInstance(), 0L, 1L);
}
@Override
protected @NotNull ItemStack buildRawItem() {
ItemStack itemStack = new ItemStack(Material.FIREWORK_STAR);
itemStack.editMeta(itemMeta -> itemMeta.addEnchant(Enchantment.INFINITY, 1, true));
return itemStack;
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.GRENADE);
}
}

View File

@@ -0,0 +1,4 @@
package hdvtdev.blockandseek.items;
public class LeaveItem {
}

View File

@@ -0,0 +1,38 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.menus.GamesMenu;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.Material;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
public class MenuItem extends BlockAndSeekItem {
public MenuItem() {
super(Items.MENU, ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
new GamesMenu(event.getPlayer());
}
@Override
protected @NotNull ItemStack buildRawItem() {
ItemStack itemStack = new ItemStack(Material.COMPASS);
itemStack.editMeta(itemMeta -> itemMeta.addEnchant(Enchantment.INFINITY, 1, true));
return itemStack;
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.MENU);
}
}

View File

@@ -0,0 +1,51 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.MiscDisguise;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.RayTraceResult;
import org.jetbrains.annotations.NotNull;
public class MorphItem extends BlockAndSeekItem {
public MorphItem() {
super(Items.MORPH, ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
Block block = event.getClickedBlock();
Player player = event.getPlayer();
if (block == null) {
RayTraceResult result = player.rayTraceBlocks(128);
if (result != null) {
block = result.getHitBlock();
} else return;
}
if (block == null) return;
DisguiseAPI.disguiseToAll(player, new MiscDisguise(DisguiseType.FALLING_BLOCK, new ItemStack(block.getType())));
}
@Override
protected @NotNull ItemStack buildRawItem() {
ItemStack itemStack = new ItemStack(Material.PHANTOM_MEMBRANE);
itemStack.editMeta(itemMeta -> itemMeta.addEnchant(Enchantment.INFINITY, 1, true));
return itemStack;
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.MORPH_ITEM);
}
}

View File

@@ -0,0 +1,112 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.Utils;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.util.RayTraceResult;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
import java.util.UUID;
public class Pistol extends BlockAndSeekItem {
public Pistol() {
super(Items.PISTOL, Duration.ofMillis(750), ClickAction.RIGHT_CLICK);
}
@Override
public void onInteract(@NotNull PlayerInteractEvent event) {
Player player = event.getPlayer();
player.playSound(player, Sound.ENTITY_FIREWORK_ROCKET_BLAST_FAR, 1.0f, 1.0f);
Location startLoc = player.getEyeLocation();
Vector direction = startLoc.getDirection();
RayTraceResult result = player.getWorld().rayTrace(
startLoc,
direction,
50,
FluidCollisionMode.NEVER,
true,
0.5,
entity -> {
if (entity.equals(player)) return false;
if (entity instanceof ArmorStand) return false;
if (entity instanceof Player maybeProp) {
return !PropManager.isProped(maybeProp.getUniqueId());
}
return true;
}
);
Vector endPosition = result == null ? startLoc.toVector().add(direction.clone().multiply(64)) : result.getHitPosition();
double distance = startLoc.toVector().distance(endPosition);
double step = 0.5;
for (double d = 0; d < distance; d += step) {
Location particleLoc = startLoc.clone().add(direction.clone().multiply(d));
player.getWorld().spawnParticle(Particle.SMOKE, particleLoc, 1, 0, 0, 0, 0);
}
if (result != null) {
Entity hitEntity = result.getHitEntity();
if (hitEntity instanceof Player target) {
target.damage(5);
double strength = 0.1;
double x = -target.getLocation().getDirection().getX();
double z = -target.getLocation().getDirection().getZ();
target.knockback(strength, x, z);
player.playSound(player, Sound.ENTITY_ARROW_HIT_PLAYER, 1f, 1f);
Utils.healSeeker(player, 5);
} else {
Block hitBlock = result.getHitBlock();
if (hitBlock != null) {
UUID uuid = PropManager.getProp(hitBlock.getLocation());
if (uuid != null) {
Player hider = Bukkit.getPlayer(uuid);
if (hider != null) {
PropManager.forceUnfreeze(hider);
player.playSound(player, Sound.ENTITY_ARROW_HIT_PLAYER, 1f, 1f);
hider.damage(5);
Utils.healSeeker(player, 5);
}
} else {
Utils.damageSeeker(player, 5);
}
}
}
}
}
@Override
protected @NotNull ItemStack buildRawItem() {
return new ItemStack(Material.BLACK_DYE);
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.PISTOL);
}
}

View File

@@ -0,0 +1,73 @@
package hdvtdev.blockandseek.items;
import hdvtdev.blockandseek.Keys;
import hdvtdev.blockandseek.managers.TranslationManager;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.Material;
import org.bukkit.Sound;
import org.bukkit.enchantments.Enchantment;
import org.bukkit.entity.Player;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemFlag;
import org.bukkit.inventory.ItemStack;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
import java.time.Duration;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
public class SoundMaker extends BlockAndSeekItem {
private static final List<Sound> SHORT_SOUNDS = List.of(
Sound.ENTITY_CHICKEN_AMBIENT,
Sound.BLOCK_GLASS_BREAK,
Sound.ENTITY_PLAYER_SPLASH,
Sound.ENTITY_SHULKER_AMBIENT,
Sound.ENTITY_WITCH_AMBIENT,
Sound.BLOCK_BELL_USE,
Sound.ENTITY_VILLAGER_AMBIENT,
Sound.ENTITY_SNIFFER_HAPPY,
Sound.ENTITY_PIG_DEATH,
Sound.ENTITY_PIG_AMBIENT,
Sound.ENTITY_GOAT_SCREAMING_PREPARE_RAM,
Sound.ENTITY_GOAT_SCREAMING_DEATH,
Sound.ENTITY_LLAMA_SPIT,
Sound.ENTITY_CAT_PURREOW,
Sound.ENTITY_SHEEP_AMBIENT
);
public SoundMaker() {
super(Items.SOUND_MAKER, Duration.ofMillis(1750), ClickAction.RIGHT_CLICK);
}
@Override
protected void onInteract(@NotNull PlayerInteractEvent event) {
Sound sound = SHORT_SOUNDS.get(ThreadLocalRandom.current().nextInt(0, SHORT_SOUNDS.size()));
Player player = event.getPlayer();
player.getWorld().playSound(player.getLocation(), sound, 1.75f, 1.0f);
}
@Override
protected @NotNull ItemStack buildRawItem() {
ItemStack itemStack = new ItemStack(Material.BROWN_DYE);
itemStack.editMeta(soundMeta -> {
soundMeta.getPersistentDataContainer().set(Keys.SOUND_ITEM, PersistentDataType.BOOLEAN, true);
soundMeta.addEnchant(Enchantment.INFINITY, 1, true);
soundMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
});
return itemStack;
}
@Override
public @NotNull ItemStack getTranslated(Player player) {
return TranslationManager.translateItem(player, getCloned(), TranslationKey.SOUND_ITEM);
}
}

View File

@@ -0,0 +1,39 @@
package hdvtdev.blockandseek.managers;
import hdvtdev.blockandseek.roulette.RouletteList;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;
public class FrecamManager {
private final RouletteList<UUID> hiders;
private static final HashMap<UUID, Location> playerLocations = new HashMap<>();
public FrecamManager(List<UUID> hiders) {
this.hiders = new RouletteList<>(hiders);
}
public void teleportToNext(@NotNull Player player) {
Player target = Bukkit.getPlayer(hiders.next());
if (target != null) {
UUID uuid = player.getUniqueId();
Location location = target.getLocation();
if (!playerLocations.containsKey(uuid)) {
playerLocations.put(uuid, player.getLocation());
}
player.setGameMode(GameMode.SPECTATOR);
}
}
}

View File

@@ -27,16 +27,12 @@ public class GamesManager {
return games.keySet();
}
public static @Nullable BlockAndSeekGame createGame(String name) {
public static BlockAndSeekGame createGame(String name) {
BlockAndSeekMap map = MapsManager.getMap(name);
if (map != null) {
BlockAndSeekGame game = new BlockAndSeekGame(name, map);
games.put(name, game);
return game;
}
return null;
BlockAndSeekGame game = new BlockAndSeekGame(name, map);
games.put(name, game);
return game;
}
public static void remove(String name) {

View File

@@ -1,6 +1,9 @@
package hdvtdev.blockandseek.managers;
import hdvtdev.blockandseek.Keys;
import hdvtdev.blockandseek.items.*;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Items;
import hdvtdev.blockandseek.objects.TranslationKey;
import lombok.Getter;
import net.kyori.adventure.text.Component;
@@ -15,19 +18,32 @@ import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.inventory.meta.LeatherArmorMeta;
import org.bukkit.persistence.PersistentDataType;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Map;
import static hdvtdev.blockandseek.objects.TranslationKey.*;
public class ItemManager {
@Getter
private static final ItemStack freezeItem = new ItemStack(Material.HEART_OF_THE_SEA);
@Getter
private static final ItemStack faceChangingItem = new ItemStack(Material.NETHER_STAR);
@Getter
private static final ItemStack dashItem = new ItemStack(Material.LAPIS_LAZULI);
@Getter
private static final ItemStack soundItem = new ItemStack(Material.BROWN_DYE);
private static final Map<Items, BlockAndSeekItem> items = new EnumMap<>(Items.class);
static {
items.put(Items.MORPH, new MorphItem());
items.put(Items.MENU, new MenuItem());
items.put(Items.DASH, new DashItem());
items.put(Items.SOUND_MAKER, new SoundMaker());
items.put(Items.FACE_CHANGING, new FaceChangingItem());
items.put(Items.PISTOL, new Pistol());
items.put(Items.GRENADE, new Grenade());
items.put(Items.FREEZE, new FreezeItem());
}
public static BlockAndSeekItem getItem(Items item) {
return items.get(item);
}
private static final ItemStack seekerSword = new ItemStack(Material.WOODEN_SWORD);
private static final ItemStack seekerHelmet = new ItemStack(Material.LEATHER_HELMET);
@@ -35,57 +51,26 @@ public class ItemManager {
private static final ItemStack seekerLeggings = new ItemStack(Material.LEATHER_LEGGINGS);
private static final ItemStack seekerBoots = new ItemStack(Material.LEATHER_BOOTS);
@Getter
private static final ItemStack menuItem = new ItemStack(Material.COMPASS);
@Getter
private static final ItemStack games = new ItemStack(Material.BOOKSHELF);
@Getter
private static final ItemStack createGameButton = new ItemStack(Material.SLIME_BALL);
@Getter
private static final ItemStack leaveItem = new ItemStack(Material.RED_DYE);
static {
ItemMeta freezeMeta = freezeItem.getItemMeta();
freezeMeta.getPersistentDataContainer().set(Keys.FREEZE_ITEM, PersistentDataType.BOOLEAN, true);
freezeItem.setItemMeta(freezeMeta);
ItemMeta faceChangingMeta = faceChangingItem.getItemMeta();
faceChangingMeta.getPersistentDataContainer().set(Keys.FACE_CHANGING_ITEM, PersistentDataType.BOOLEAN, true);
faceChangingItem.setItemMeta(faceChangingMeta);
ItemMeta swordMeta = seekerSword.getItemMeta();
swordMeta.addEnchant(Enchantment.DAMAGE_ALL, 2, false);
swordMeta.addEnchant(Enchantment.SHARPNESS, 2, false);
swordMeta.setUnbreakable(true);
swordMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_ATTRIBUTES);
seekerSword.setItemMeta(swordMeta);
ItemMeta menuMeta = menuItem.getItemMeta();
menuMeta.getPersistentDataContainer().set(Keys.MENU_ITEM, PersistentDataType.BOOLEAN, true);
menuMeta.addEnchant(Enchantment.ARROW_INFINITE, 1, true);
menuMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
menuItem.setItemMeta(menuMeta);
ItemMeta dashMeta = dashItem.getItemMeta();
dashMeta.getPersistentDataContainer().set(Keys.DASH_ITEM, PersistentDataType.BOOLEAN, true);
dashMeta.addEnchant(Enchantment.ARROW_INFINITE, 1, true);
dashMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
dashItem.setItemMeta(dashMeta);
ItemMeta soundMeta = soundItem.getItemMeta();
soundMeta.getPersistentDataContainer().set(Keys.SOUND_ITEM, PersistentDataType.BOOLEAN, true);
soundMeta.addEnchant(Enchantment.ARROW_INFINITE, 1, true);
soundMeta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
soundItem.setItemMeta(soundMeta);
ItemMeta gamesMeta = games.getItemMeta();
games.setItemMeta(gamesMeta);
ItemMeta leaveMeta = leaveItem.getItemMeta();
leaveMeta.getPersistentDataContainer().set(Keys.LEAVE_ITEM, PersistentDataType.BOOLEAN, true);
leaveItem.setItemMeta(leaveMeta);
ItemMeta createGameButtonMeta = createGameButton.getItemMeta();
createGameButton.setItemMeta(createGameButtonMeta);
@@ -100,7 +85,11 @@ public class ItemManager {
public static void setSeekerSet(Player seeker) {
PlayerInventory inventory = seeker.getInventory();
inventory.clear();
inventory.addItem(TranslationManager.translateItem(seeker, seekerSword, TranslationKey.SEEKER_TEMPLATE));
inventory.addItem(TranslationManager.translateItem(seeker, seekerSword, TranslationKey.SEEKER_TEMPLATE),
getItem(Items.DASH).getTranslated(seeker),
getItem(Items.PISTOL).getTranslated(seeker),
getItem(Items.GRENADE).getTranslated(seeker)
);
ItemStack[] armor = new ItemStack[]{
TranslationManager.translateItem(seeker, seekerBoots, TranslationKey.SEEKER_TEMPLATE),
TranslationManager.translateItem(seeker, seekerLeggings, TranslationKey.SEEKER_TEMPLATE),
@@ -117,7 +106,7 @@ public class ItemManager {
LeatherArmorMeta meta = (LeatherArmorMeta) item.getItemMeta();
meta.setColor(Color.RED);
meta.setUnbreakable(true);
meta.addEnchant(Enchantment.PROTECTION_ENVIRONMENTAL, 10, true);
meta.addEnchant(Enchantment.PROTECTION, 10, true);
meta.addEnchant(Enchantment.THORNS, 3, true);
return meta;
@@ -126,25 +115,18 @@ public class ItemManager {
public static void defaultInventory(Player player) {
PlayerInventory inventory = player.getInventory();
inventory.clear();
inventory.addItem(TranslationManager.translateItem(player, menuItem, TranslationKey.MENU));
inventory.addItem(getItem(Items.MENU).getTranslated(player));
}
public static void hiderInventory(Player player) {
PlayerInventory playerInventory = player.getInventory();
playerInventory.clear();
playerInventory.addItem(
TranslationManager.translateItem(player, freezeItem, FREEZE_ITEM),
TranslationManager.translateItem(player, faceChangingItem, FACE_CHANGING_ITEM),
TranslationManager.translateItem(player, soundItem, SOUND_ITEM),
TranslationManager.translateItem(player, dashItem, DASH_ITEM)
getItem(Items.FREEZE).getTranslated(player),
getItem(Items.FACE_CHANGING).getTranslated(player),
getItem(Items.SOUND_MAKER).getTranslated(player),
getItem(Items.DASH).getTranslated(player)
);
}
public static void giveFaceChangingItem(Player player) {
player.getInventory().addItem(TranslationManager.translateItem(player, faceChangingItem, TranslationKey.SEEKER_TEMPLATE));
}
}

View File

@@ -4,15 +4,16 @@ import eu.okaeri.configs.ConfigManager;
import eu.okaeri.configs.serdes.commons.SerdesCommons;
import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer;
import eu.okaeri.configs.yaml.bukkit.serdes.SerdesBukkit;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.objects.BlockAndSeekMap;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.command.CommandSender;
import org.incendo.cloud.suggestion.Suggestion;
import org.incendo.cloud.suggestion.SuggestionProvider;
import org.intellij.lang.annotations.Flow;
import java.io.File;
import java.util.*;
@@ -26,94 +27,49 @@ public final class MapsManager {
private MapsManager() {}
public static void loadMaps() {
if (!MAPS_DIR.exists()) {
MAPS_DIR.mkdirs();
}
public static List<String> getAllMaps() {
return new ArrayList<>(maps.keySet());
}
File[] files = MAPS_DIR.listFiles((dir, name) -> name.endsWith(".yml"));
if (files == null) return;
for (File file : files) {
String name = file.getName().replace(".yml", "");
try {
BlockAndSeekMap map = loadConfig(file);
maps.put(name, map);
} catch (Exception e) {
BlockAndSeek.getPluginLogger().severe("Error loading map " + name + ": " + e.getMessage());
e.printStackTrace();
}
}
BlockAndSeek.getPluginLogger().info("Loaded " + maps.size() + " maps.");
public static boolean hasMap(String name) {
return maps.containsKey(name);
}
public static BlockAndSeekMap getMap(String name) {
return maps.get(name);
}
public static Collection<BlockAndSeekMap> getMaps() {
return maps.values();
}
public static void loadMaps() {
public static void createMap(String name) {
File mapFile = new File(MAPS_DIR, name + ".yml");
if (mapFile.exists()) {
throw new IllegalArgumentException("Map already exists!");
}
File[] mapsFile = MAPS_DIR.listFiles();
BlockAndSeekMap map = loadConfig(mapFile);
map.setWorld(name);
if (Bukkit.getWorld(name) != null) {
Location spawn = Bukkit.getWorld(name).getSpawnLocation();
map.setLobby(spawn);
map.setSpawn(spawn);
}
map.save();
maps.put(name, map);
}
private static BlockAndSeekMap loadConfig(File file) {
var map = ConfigManager.create(BlockAndSeekMap.class, (it) -> {
it.withConfigurer(
new YamlBukkitConfigurer(),
new SerdesBukkit(),
new SerdesCommons()
);
it.withBindFile(file);
it.withLogger(BlockAndSeek.getPluginLogger());
it.load(true);
});
String worldName = map.getWorld();
if (worldName == null) {
BlockAndSeek.getPluginLogger().warning("Map " + file.getName() + " has no world defined!");
return map;
}
World world = Bukkit.getWorld(worldName);
if (world == null) {
File worldFolder = new File(Bukkit.getWorldContainer(), worldName);
if (worldFolder.exists() && worldFolder.isDirectory()) {
BlockAndSeek.getPluginLogger().info("World '" + worldName + "' is not loaded. Loading now...");
world = Bukkit.createWorld(new WorldCreator(worldName));
if (mapsFile != null) {
for (File mapFile : mapsFile) {
BlockAndSeekMap blockAndSeekMap = ConfigManager.create(BlockAndSeekMap.class, (it) -> {
it.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons(), new SerdesBukkit());
it.withBindFile(mapFile);
it.saveDefaults();
it.load(true);
});
maps.put(mapFile.getName().replace(".yml", ""), blockAndSeekMap);
}
}
map.getLobby().setWorld(world);
map.getSpawn().setWorld(world);
}
map.save();
return map;
public static void addMap(String name) {
BlockAndSeekMap blockAndSeekMap = ConfigManager.create(BlockAndSeekMap.class, (it) -> {
it.withConfigurer(new YamlBukkitConfigurer(), new SerdesCommons(), new SerdesBukkit());
it.withBindFile(new File(MAPS_DIR, name + ".yml"));
it.saveDefaults();
it.load(true);
});
maps.put(name, blockAndSeekMap);
}
// --- Suggestions ---
public static SuggestionProvider<CommandSender> worldSuggestions = (context, input) ->
public static final SuggestionProvider<CommandSender> worldSuggestions = (context, input) ->
CompletableFuture.supplyAsync(() -> {
// Лучше брать загруженные миры Bukkit, если карта должна быть в активном мире
// Если нужны папки миров:
@@ -128,7 +84,7 @@ public final class MapsManager {
.collect(Collectors.toList());
});
public static SuggestionProvider<CommandSender> mapSuggestions = (context, input) ->
public static final SuggestionProvider<CommandSender> mapSuggestions = (context, input) ->
CompletableFuture.supplyAsync(() -> maps.keySet().stream()
.map(Suggestion::suggestion)
.collect(Collectors.toList()));

View File

@@ -1,110 +1,112 @@
package hdvtdev.blockandseek.managers;
import hdvtdev.blockandseek.BlockAndSeek;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.disguisetypes.Disguise;
import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.MiscDisguise;
import me.libraryaddict.disguise.disguisetypes.watchers.FallingBlockWatcher;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.*;
import org.bukkit.block.Block;
import org.bukkit.block.BlockFace;
import org.bukkit.block.data.BlockData;
import org.bukkit.block.data.Openable;
import org.bukkit.block.data.Directional;
import org.bukkit.block.data.Orientable;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import org.bukkit.event.entity.CreatureSpawnEvent;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public final class PropManager {
public class PropManager {
private final Map<UUID, PropData> players = new HashMap<>();
private final Map<Location, UUID> props = new HashMap<>();
private static final Map<UUID, PropData> players = new ConcurrentHashMap<>();
private static final Map<Location, UUID> props = new ConcurrentHashMap<>();
public void removePlayer(Player player) {
private PropManager() {}
public static void removePlayer(Player player) {
PropData propData = players.remove(player.getUniqueId());
if (propData != null) {
props.remove(propData.propLocation);
propData.armorStand.remove();
}
}
public boolean isProped(UUID uuid) {
return props.containsValue(uuid);
public static boolean isProped(UUID uuid) {
return players.containsKey(uuid);
}
public void forceUnfreeze(Player player) {
public static void forceUnfreeze(Player player) {
if (players.containsKey(player.getUniqueId())) {
freezeOrUnfreeze(player);
}
}
public void forceUnfreeze(UUID uuid) {
public static void forceUnfreeze(UUID uuid) {
if (players.containsKey(uuid)) {
freezeOrUnfreeze(Bukkit.getPlayer(uuid));
}
}
@Nullable
public UUID getProp(Location location) {
return props.remove(location);
public static UUID getProp(Location location) {
return props.get(location);
}
//fixme: replace body with freezeOrUnfreeze(Player player) body
/*
public PropState freezeOrUnfreeze(UUID uuid) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
return freezeOrUnfreeze(player);
}
return PropState.FAILED;
}
*/
//i dont know how dis works xd
public PropState freezeOrUnfreeze(Player player) {
public static PropState freezeOrUnfreeze(Player player) {
UUID uuid = player.getUniqueId();
PropData propData = players.remove(uuid);
Location location = player.getLocation().toCenterLocation();
if (propData != null) {
ArmorStand armorStand = propData.armorStand;
location.getWorld().setBlockData(location, propData.blockData);
location.setY(Math.floor(location.getY()));
player.setInvulnerable(false);
armorStand.removePassenger(player);
armorStand.remove();
Location propLocation = propData.propLocation;
props.remove(propLocation);
propLocation.getBlock().setBlockData(propData.blockData);
propLocation.setYaw(player.getYaw());
propLocation.setPitch(player.getPitch());
player.teleport(propLocation.toCenterLocation());
player.setInvulnerable(false);
player.setVisibleByDefault(true);
player.setGameMode(GameMode.SURVIVAL);
propData.armorStand.removePassenger(player);
propData.armorStand.remove();
return PropState.UNFROZEN;
} else {
Location location = player.getLocation();
Block block = location.getBlock();
BlockData blockData = block.getBlockData();
Location blockLocation = block.getLocation();
if (props.containsKey(blockLocation)) {
return PropState.FAILED;
}
Location centerLocation = blockLocation.toCenterLocation();
Location upperBlockLocation = centerLocation.clone();
upperBlockLocation.setY(upperBlockLocation.getY() + 1);
Location upperBlockLocation = centerLocation.clone().add(0, 1, 0);
if (!upperBlockLocation.getBlock().isSolid() && !blockLocation.getBlock().isSolid()) {
props.put(blockLocation, uuid);
if (!upperBlockLocation.getBlock().isSolid() && !block.getType().isSolid()) {
BlockData originalBlockData = block.getBlockData();
BlockData disguiseBlockData = getPlayerDisguiseData(player);
location.getWorld().setBlockData(blockLocation, disguiseBlockData == null ? Material.TARGET.createBlockData() : disguiseBlockData);
block.setBlockData(disguiseBlockData == null ? Material.TARGET.createBlockData() : disguiseBlockData);
centerLocation.setY(centerLocation.getY() - 0.85);
player.setInvulnerable(true);
player.setVisibleByDefault(false);
player.setFreezeTicks(40);
ArmorStand armorStand = location.getWorld().spawn(centerLocation, ArmorStand.class, stand -> {
stand.setVisible(false);
stand.setVisible(false);
stand.setCollidable(false);
stand.setGravity(true);
@@ -114,21 +116,60 @@ public final class PropManager {
stand.setInvulnerable(true);
});
players.put(uuid, new PropData(armorStand, blockData));
players.put(uuid, new PropData(armorStand, originalBlockData, blockLocation));
props.put(blockLocation, uuid);
return PropState.FROZEN;
}
}
return PropState.FAILED;
}
private static final BlockFace[] rotationOrder = {
BlockFace.NORTH, BlockFace.EAST, BlockFace.SOUTH, BlockFace.WEST,
BlockFace.UP, BlockFace.DOWN
};
public static void changePropDirection(Player player) {
if (players.containsKey(player.getUniqueId())) {
Block block = player.getLocation().getBlock();
BlockData blockData = block.getBlockData();
if (blockData instanceof Orientable orientable) {
Axis nextAxis = switch (orientable.getAxis()) {
case X -> Axis.Y;
case Y -> Axis.Z;
case Z -> Axis.X;
};
orientable.setAxis(nextAxis);
block.setBlockData(orientable);
}
else if (blockData instanceof Directional directional) {
BlockFace currentFace = directional.getFacing();
BlockFace nextFace = null;
for (int i = 0; i < rotationOrder.length; i++) {
if (rotationOrder[i] == currentFace) {
for (int j = 1; j <= rotationOrder.length; j++) {
BlockFace potentialFace = rotationOrder[(i + j) % rotationOrder.length];
if (directional.getFaces().contains(potentialFace)) {
nextFace = potentialFace;
break;
}
}
break;
}
}
if (nextFace != null) {
directional.setFacing(nextFace);
block.setBlockData(directional);
}
}
}
}
@Nullable
private static BlockData getPlayerDisguiseData(Player player) {
Disguise disguise = DisguiseAPI.getDisguise(player);
@@ -141,14 +182,9 @@ public final class PropManager {
public enum PropState {
FROZEN,
UNFROZEN,
FAILED
FROZEN, UNFROZEN, FAILED
}
private record PropData(ArmorStand armorStand, BlockData blockData) {
private record PropData(ArmorStand armorStand, BlockData blockData, Location propLocation) {
}
}
}

View File

@@ -8,6 +8,7 @@ import eu.okaeri.configs.annotation.Exclude;
import eu.okaeri.configs.serdes.commons.SerdesCommons;
import eu.okaeri.configs.yaml.bukkit.YamlBukkitConfigurer;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.objects.BlockAndSeekItem;
import hdvtdev.blockandseek.objects.Translation;
import hdvtdev.blockandseek.objects.TranslationKey;
import lombok.Getter;
@@ -37,6 +38,7 @@ public final class TranslationManager {
public static final String bracedPrefix = "<gradient:#FFAA00:#FFD700><bold>[BlockAndSeek]</bold></gradient>";
private static final Map<String, EnumMap<TranslationKey, String>> translations = new HashMap<>();
private static final Map<String, BlockAndSeekItem> itemTranslations = new HashMap<>();
public static Component get(Player player, TranslationKey key, String... placeholders) {
return get(player.locale().toString(), key, placeholders);

View File

@@ -27,65 +27,35 @@ public final class WorldManager {
private static final AsyncScheduler asyncScheduler = Bukkit.getAsyncScheduler();
private static final ConcurrentHashMap<String, WorldManager> worldManagers = new ConcurrentHashMap<>();
static {
//TODO
}
private final File originalWorld;
private final ConcurrentLinkedQueue<World> freeCopies = new ConcurrentLinkedQueue<>();
private final AtomicInteger copyIndex = new AtomicInteger(1);
private final int reservedCopies;
private final int reservedCopies = 1;
private WorldManager(String worldName, int reservedCopies) {
public WorldManager(String worldName) {
this.originalWorld = new File(serverFolder, worldName + "_copy");
this.reservedCopies = reservedCopies;
new BukkitRunnable() {
boolean unloading = false;
@Override
public void run() {
if (!Bukkit.isTickingWorlds() && !unloading) {
unloading = true;
Bukkit.unloadWorld(worldName, true);
asyncScheduler.runNow(BlockAndSeek.getInstance(), task -> {
try {
FileUtils.copyDirectoryStructure(new File(serverFolder, worldName), originalWorld);
createCopy();
} catch (IOException e) {
BlockAndSeek.getPluginLogger().severe("Failed to copy world \"" + worldName + "\": " + e.getMessage());
this.cancel();
}
});
this.cancel();
}
}
}.runTaskTimer(BlockAndSeek.getInstance(), 0, 1);
}
public static void createWorldManager(String existingWorld, int reservedCopies) {
worldManagers.put(existingWorld, new WorldManager(existingWorld, reservedCopies));
}
public static @Nullable WorldManager get(String worldName) {
return worldManagers.get(worldName);
private void createCopies() {
for (int i = 0; i < reservedCopies; i++) {
createCopy();
}
}
/**
* @return {@link World} if a free copy is available or a new one has been created.
* Returns {@code null} if there are currently no free copies and a new copy is still being created.
*/
public @Nullable World acquire() {
if (freeCopies.size() <= 1) {
createCopy(); //TODO
createCopies(); //TODO
}
return freeCopies.poll();
}

View File

@@ -17,6 +17,7 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.stream.Collectors;
import static hdvtdev.blockandseek.objects.TranslationKey.UNKNOWN_MAP;
@@ -26,37 +27,39 @@ public class MapsMenu implements GuiHolder {
private final Inventory inventory;
public MapsMenu(Player player) {
Component title = TranslationManager.get(player, TranslationKey.GAMES_MENU);
Component title = TranslationManager.get(player, TranslationKey.MAPS_MENU);
this.inventory = Bukkit.createInventory(this, 54, title);
init();
this.init();
showInventory(player);
}
private void init() {
for (BlockAndSeekMap map : MapsManager.getMaps()) {
for (String name : MapsManager.getAllMaps()) {
ItemStack gameItem = new ItemStack(Material.MAP);
ItemMeta itemMeta = gameItem.getItemMeta();
itemMeta.displayName(TranslationManager.get(TranslationManager.defaultLanguage, TranslationKey.MAP, "%name%", map.getWorld()));
gameItem.setItemMeta(itemMeta);
gameItem.editMeta(itemMeta ->
itemMeta.displayName(TranslationManager.get(TranslationManager.defaultLanguage, TranslationKey.MAP, "%name%", name)));
inventory.addItem(gameItem);
}
}
@Override
public void onClick(InventoryClickEvent event) {
ItemStack item = event.getCurrentItem();
event.setCancelled(true);
if (item != null) {
Player player = (Player) event.getWhoClicked();
String mapName = TranslationManager.plaintText.serialize(item.displayName()).replaceAll("[\\[\\]]", "");
var map = MapsManager.getMap(mapName);
if (map != null) {
if (MapsManager.hasMap(mapName)) {
GamesManager.createGame(mapName).addPlayer(player);
} else {
player.sendMessage(TranslationManager.get(player, UNKNOWN_MAP, "%map%", mapName, "%maps%", MapsManager.getMaps().stream().map(BlockAndSeekMap::getWorld).collect(Collectors.joining(", "))));
player.sendMessage(UNKNOWN_MAP.translate(player, "%map%", mapName, "%maps%", String.join(", ", MapsManager.getAllMaps())));
}
player.closeInventory();
}
}
@Override

View File

@@ -0,0 +1,48 @@
package hdvtdev.blockandseek.objects;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.managers.ItemManager;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.scheduler.BukkitRunnable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
public abstract class BlockAndSeekButton {
private static final NamespacedKey namespacedKey = new NamespacedKey(BlockAndSeek.getInstance(), "item_id");
private final ItemStack button;
public BlockAndSeekButton(@NotNull Items id) {
this.button = asItemStack(id);
}
@NotNull
private ItemStack asItemStack(@NotNull Items val) {
ItemStack itemStack = buildRawItem();
itemStack.editMeta(meta -> meta.getPersistentDataContainer().set(namespacedKey, Items.TYPE, val));
return itemStack;
}
@NotNull
protected abstract ItemStack buildRawItem();
@NotNull
public abstract ItemStack getTranslated(Player player);
@NotNull
protected final ItemStack getCloned() {
return button.clone();
}
}

View File

@@ -9,17 +9,18 @@ import hdvtdev.blockandseek.managers.GamesManager;
import hdvtdev.blockandseek.managers.ItemManager;
import hdvtdev.blockandseek.managers.PropManager;
import hdvtdev.blockandseek.managers.StateManager;
import hdvtdev.blockandseek.menus.GamesMenu;
import hdvtdev.blockandseek.roulette.RouletteCreator;
import me.libraryaddict.disguise.DisguiseAPI;
import me.libraryaddict.disguise.disguisetypes.DisguiseType;
import me.libraryaddict.disguise.disguisetypes.MiscDisguise;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.title.Title;
import org.bukkit.Bukkit;
import org.bukkit.GameMode;
import org.bukkit.Sound;
import org.bukkit.entity.ArmorStand;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
@@ -40,6 +41,8 @@ import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.plugin.Plugin;
import org.bukkit.potion.PotionEffect;
import org.bukkit.potion.PotionEffectType;
import org.bukkit.scheduler.BukkitRunnable;
import java.util.*;
@@ -48,7 +51,6 @@ public class BlockAndSeekGame {
private final BlockAndSeekMap map;
private final StateManager stateManager = new StateManager();
private final PropManager propManager = new PropManager();
private final String name;
private GamePhase phase;
@@ -62,7 +64,7 @@ public class BlockAndSeekGame {
public boolean addPlayer(Player player) {
if (phase instanceof LobbyPhase && stateManager.playerCount() < map.getMaxPlayers()) {
stateManager.setPlayerState(player.getUniqueId(), PlayerType.NONE);
player.teleport(map.getLobby());
map.getLobby().teleport(player);
player.setInvulnerable(true);
return true;
}
@@ -128,7 +130,7 @@ public class BlockAndSeekGame {
//fixme: player leave event: count == 0 -> phase = null -> delete game
private class LobbyPhase extends GamePhase {
private static final int WAIT_TIME = 30;
private static final int WAIT_TIME = 31;
@Override
public void onTick() {
@@ -166,6 +168,7 @@ public class BlockAndSeekGame {
private class StartedGamePhase extends GamePhase implements Listener {
private long gameDuration = map.getGameDuration().toSeconds();
private final long midGame = gameDuration / 2;
@Override
public void onTick() {
@@ -182,6 +185,14 @@ public class BlockAndSeekGame {
broadcastToAll(TranslationKey.SEEKERS_WIN);
this.onDisable();
} else {
if (gameDuration == midGame) {
for (UUID uuid : stateManager.getPlayersInState(PlayerType.PROP)) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
new RouletteCreator(player, map.getBlocks());
}
}
}
actionBarToAll(TranslationKey.TIME_LEFT, "%time%", String.valueOf(gameDuration));
}
gameDuration--;
@@ -197,7 +208,7 @@ public class BlockAndSeekGame {
if (player != null) {
stateManager.setPlayerState(uuid, PlayerType.PROP);
ItemManager.hiderInventory(player);
player.teleport(map.getSpawn());
map.getSpawn().teleport(player);
new RouletteCreator(player, map.getBlocks());
player.setInvulnerable(false);
} else BlockAndSeek.getPluginLogger().warning("Player is null. ");
@@ -213,12 +224,15 @@ public class BlockAndSeekGame {
Collections.shuffle(rawSeekers);
var seekers = rawSeekers.subList(0, Math.min(count, playerSet.size()));
long spawnDelay = map.getSeekerSpawnDelay().toSeconds() * 20;
for (UUID seeker : seekers) {
Player player = Bukkit.getPlayer(seeker);
if (player != null) {
stateManager.setPlayerState(seeker, PlayerType.SEEKER);
player.setWalkSpeed(0.22f);
player.setWalkSpeed(0.27f);
player.addPotionEffect(new PotionEffect(PotionEffectType.BLINDNESS, (int) spawnDelay, 2));
ItemManager.setSeekerSet(player);
Utils.setLevelWithBar(player, 100);
GamesManager.addSeekerImmune(player);
@@ -231,12 +245,12 @@ public class BlockAndSeekGame {
for (UUID seeker : seekers) {
Player player = Bukkit.getPlayer(seeker);
if (player != null) {
player.teleport(map.getSpawn());
map.getSpawn().teleport(player);
player.setInvulnerable(false);
}
}
}
}.runTaskLater(BlockAndSeek.getInstance(), map.getSeekerSpawnDelay().toSeconds() * 20);
}.runTaskLater(BlockAndSeek.getInstance(), spawnDelay);
}
@Override
@@ -281,6 +295,7 @@ public class BlockAndSeekGame {
if (stateManager.hasPlayer(uuid)) {
stateManager.setPlayerState(uuid, PlayerType.SPECTATOR);
player.setGameMode(GameMode.SPECTATOR);
PropManager.removePlayer(player);
event.setCancelled(true);
}
}
@@ -302,7 +317,7 @@ public class BlockAndSeekGame {
if (playerType == PlayerType.SEEKER) {
event.setCancelled(true);
} else if (playerType == PlayerType.PROP) {
if (!propManager.isProped(uuid)) {
if (!PropManager.isProped(uuid)) {
event.setCancelled(true);
}
}
@@ -313,7 +328,7 @@ public class BlockAndSeekGame {
public void onEntityDismount(EntityDismountEvent event) {
if (event.getEntity() instanceof Player player && event.getDismounted() instanceof ArmorStand) {
if (stateManager.hasPlayer(player.getUniqueId())) {
propManager.forceUnfreeze(player);
PropManager.forceUnfreeze(player);
}
}
}
@@ -363,11 +378,15 @@ public class BlockAndSeekGame {
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
if (PlayerType.SEEKER.equals(stateManager.getState(uuid))) {
UUID prop = propManager.getProp(event.getBlock().getLocation());
UUID prop = PropManager.getProp(event.getBlock().getLocation());
if (prop != null) {
propManager.forceUnfreeze(prop);
PropManager.forceUnfreeze(prop);
Utils.healSeeker(player, 5);
player.playSound(player, Sound.ENTITY_ARROW_HIT_PLAYER, 1f, 1f);
Player hider = Bukkit.getPlayer(prop);
if (hider != null) hider.damage(4); //todo calculate damage fix
} else {
player.damage(2);
player.damage(1);
Utils.setLevelWithBar(player, (int) Math.round(player.getHealth() * 5));
}
}
@@ -389,39 +408,6 @@ public class BlockAndSeekGame {
}
}
private final Set<UUID> coolDown = new HashSet<>();
@EventHandler
public void onRightClick(PlayerInteractEvent event) {
if (event.getHand() != EquipmentSlot.HAND) return;
if (event.getAction().isLeftClick()) return;
Player player = event.getPlayer();
UUID uuid = player.getUniqueId();
ItemStack item = event.getItem();
if (item != null) {
ItemMeta meta = item.getItemMeta();
if (meta != null) {
PersistentDataContainer dataContainer = meta.getPersistentDataContainer();
if (dataContainer.has(Keys.FREEZE_ITEM)) {
if (!coolDown.contains(uuid)) {
player.sendActionBar(Component.text(propManager.freezeOrUnfreeze(event.getPlayer()).toString()));
coolDown.add(uuid);
new BukkitRunnable() {
@Override
public void run() {
coolDown.remove(uuid);
}
}.runTaskLater(BlockAndSeek.getInstance(), 2);
}
}
}
}
}
}
private class EndedGamePhase extends GamePhase {
@@ -440,7 +426,7 @@ public class BlockAndSeekGame {
for (UUID uuid : stateManager.getPlayers()) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
propManager.forceUnfreeze(player);
PropManager.forceUnfreeze(player);
player.setInvulnerable(true);
player.setGlowing(true);
}

View File

@@ -0,0 +1,112 @@
package hdvtdev.blockandseek.objects;
import hdvtdev.blockandseek.BlockAndSeek;
import hdvtdev.blockandseek.managers.ItemManager;
import org.bukkit.Material;
import org.bukkit.NamespacedKey;
import org.bukkit.entity.Player;
import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.time.Duration;
import java.util.EnumSet;
import java.util.Set;
public abstract class BlockAndSeekItem {
private static final NamespacedKey namespacedKey = new NamespacedKey(BlockAndSeek.getInstance(), "item_id");
private final long cooldown;
private final ItemStack rawItem;
private final Set<Action> actions;
private static final Set<Action> rightClickOnly = EnumSet.of(Action.RIGHT_CLICK_BLOCK, Action.RIGHT_CLICK_AIR);
private static final Set<Action> leftClickOnly = EnumSet.of(Action.LEFT_CLICK_BLOCK, Action.LEFT_CLICK_AIR);
private static final Set<Action> anyClick = EnumSet.of(
Action.RIGHT_CLICK_BLOCK, Action.RIGHT_CLICK_AIR,
Action.LEFT_CLICK_BLOCK, Action.LEFT_CLICK_AIR
);
public BlockAndSeekItem(@NotNull Items id, @NotNull Duration cooldown, @NotNull ClickAction clickAction) {
this.actions = switch (clickAction) {
case RIGHT_CLICK -> rightClickOnly;
case LEFT_CLICK -> leftClickOnly;
case ANY -> anyClick;
};
this.cooldown = cooldown.toMillis() / 50;
this.rawItem = asItemStack(id);
}
public BlockAndSeekItem(@NotNull Items id) {
this(id, Duration.ZERO, ClickAction.ANY);
}
public BlockAndSeekItem(@NotNull Items id, @NotNull ClickAction action) {
this(id, Duration.ZERO, action);
}
public BlockAndSeekItem(@NotNull Items id, @NotNull Duration cooldown) {
this(id, cooldown, ClickAction.ANY);
}
@NotNull
private ItemStack asItemStack(@NotNull Items val) {
ItemStack itemStack = buildRawItem();
itemStack.editMeta(meta -> meta.getPersistentDataContainer().set(namespacedKey, Items.TYPE, val));
return itemStack;
}
public final boolean onRawInteract(@NotNull PlayerInteractEvent event) {
if (this.actions.contains(event.getAction())) {
if (cooldown != 0) {
Player player = event.getPlayer();
Material material = event.getMaterial();
if (!player.hasCooldown(material)) {
player.setCooldown(material, (int) cooldown);
onInteract(event);
}
return false;
} else {
onInteract(event);
}
}
return true;
}
protected abstract void onInteract(@NotNull PlayerInteractEvent event);
@NotNull
protected abstract ItemStack buildRawItem();
@NotNull
public abstract ItemStack getTranslated(Player player);
@NotNull
protected final ItemStack getCloned() {
return rawItem.clone();
}
@Nullable
public static BlockAndSeekItem tryCast(@Nullable ItemStack itemStack) {
if (itemStack != null) {
Items items = itemStack.getItemMeta().getPersistentDataContainer().get(namespacedKey, Items.TYPE);
if (items != null) {
return ItemManager.getItem(items);
}
}
return null;
}
public enum ClickAction {
RIGHT_CLICK,
LEFT_CLICK,
ANY
}
}

View File

@@ -8,7 +8,6 @@ import eu.okaeri.validator.annotation.Min;
import lombok.Getter;
import lombok.Setter;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.inventory.ItemStack;
@@ -20,14 +19,11 @@ import java.util.List;
@Setter
public class BlockAndSeekMap extends OkaeriConfig {
@Comment("The name of the world where the game takes place")
private String world;
@Comment("The main spawn point inside the arena")
private Location spawn;
private LazyLocation spawn;
@Comment("The waiting lobby location where players join before the match")
private Location lobby;
private LazyLocation lobby;
@Comment("Minimum number of players required to start the game")
@Min(2)
@@ -42,7 +38,7 @@ public class BlockAndSeekMap extends OkaeriConfig {
@CustomKey("game-duration")
private Duration gameDuration = Duration.ofMinutes(10);
@Comment("How long seekers must wait before they are released")
@Comment("How long seekers must wait before they are released. Format examples: 10m, 300s, 1h")
@CustomKey("seeker-spawn-delay")
private Duration seekerSpawnDelay = Duration.ofSeconds(30);
@@ -51,9 +47,20 @@ public class BlockAndSeekMap extends OkaeriConfig {
@CustomKey("delay-before-game-end")
private Duration delayBeforeGameEnd = Duration.ofSeconds(10);
@Comment("Available blocks for hiders")
@Comment("Create blocks roulette for all hiders to select a new prop")
private boolean midGameBlockSwap = true;
@Comment("Grants seeker infinite health, speed, and jump boost for the specified time before the game ends.")
@Comment("Format examples: 10m, 300s, 1h. If duration is zero, sudden death will be disabled")
@CustomKey("sudden-death")
private Duration suddenDeath = Duration.ofSeconds(30);
@Comment("Available prop blocks for hiders")
private List<PropBlock> blocks = new ArrayList<>(List.of(new PropBlock(new ItemStack(Material.TARGET), Rarity.LEGENDARY)));
}

View File

@@ -0,0 +1,46 @@
package hdvtdev.blockandseek.objects;
import hdvtdev.blockandseek.managers.ItemManager;
import org.bukkit.persistence.PersistentDataAdapterContext;
import org.bukkit.persistence.PersistentDataType;
import org.jetbrains.annotations.NotNull;
public enum Items implements PersistentDataType<String, Items> {
PISTOL,
FREEZE,
FACE_CHANGING,
LEAVE,
MENU,
SOUND_MAKER,
DASH,
MORPH, GRENADE;
public static final PersistentDataType<String, Items> TYPE = PISTOL;
@Override
public @NotNull Class<String> getPrimitiveType() {
return String.class;
}
@Override
public @NotNull Class<Items> getComplexType() {
return Items.class;
}
@Override
public @NotNull String toPrimitive(@NotNull Items complex, @NotNull PersistentDataAdapterContext context) {
return complex.name();
}
@Override
public @NotNull Items fromPrimitive(@NotNull String primitive, @NotNull PersistentDataAdapterContext context) {
return Items.valueOf(primitive);
}
public @NotNull BlockAndSeekItem getItem() {
return ItemManager.getItem(this);
}
}

View File

@@ -0,0 +1,69 @@
package hdvtdev.blockandseek.objects;
import eu.okaeri.configs.OkaeriConfig;
import eu.okaeri.configs.annotation.Comment;
import eu.okaeri.configs.annotation.CustomKey;
import eu.okaeri.configs.annotation.Exclude;
import hdvtdev.blockandseek.BlockAndSeek;
import lombok.NoArgsConstructor;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.WorldCreator;
import org.bukkit.entity.Player;
@NoArgsConstructor
public class LazyLocation extends OkaeriConfig {
public LazyLocation(String worldName) {
this.worldName = worldName;
}
@Comment("Name of the world. World will be automatically loaded.")
@CustomKey("world-name")
private String worldName;
private double x;
private double y;
private double z;
private float yaw;
private float pitch;
@Exclude
private Location location;
private void loadLocation() {
BlockAndSeek.getPluginLogger().info("Loading world \"" + worldName + "\"");
World world = Bukkit.createWorld(new WorldCreator(worldName));
this.location = new Location(world, x, y, z, yaw, pitch);
}
public Location getLocation() {
if (location == null) loadLocation();
return location;
}
public void teleport(Player player) {
player.teleport(this.getLocation());
}
public static LazyLocation from(Location loc) {
LazyLocation ll = new LazyLocation();
if (loc.getWorld() != null) {
ll.worldName = loc.getWorld().getName();
} else {
BlockAndSeek.getPluginLogger().severe("World in location is null! Using default placeholder BlockAndSeekWorld!");
ll.worldName = "BlockAndSeekWorld";
}
ll.x = loc.getX();
ll.y = loc.getY();
ll.z = loc.getZ();
ll.yaw = loc.getYaw();
ll.pitch = loc.getPitch();
return ll;
}
}

View File

@@ -1,5 +1,9 @@
package hdvtdev.blockandseek.objects;
import hdvtdev.blockandseek.managers.TranslationManager;
import net.kyori.adventure.text.Component;
import org.bukkit.entity.Player;
public enum TranslationKey {
// Misc
UNKNOWN_COMMAND,
@@ -36,5 +40,11 @@ public enum TranslationKey {
FACE_CHANGING_ITEM,
SOUND_ITEM,
LEAVE_ITEM,
DASH_ITEM;
DASH_ITEM, MORPH_ITEM, PISTOL, GRENADE;
public Component translate(Player player, String... placeholders) {
return TranslationManager.get(player, this, placeholders);
}
}

View File

@@ -10,6 +10,7 @@ import hdvtdev.blockandseek.objects.TranslationKey;
import org.bukkit.Bukkit;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.InventoryHolder;
import org.bukkit.inventory.ItemStack;
@@ -135,6 +136,17 @@ public final class RouletteCreator implements InventoryHolder {
}
}.runTaskTimer(BlockAndSeek.getInstance(), 0L, 1L);
new BukkitRunnable() {
@Override
public void run() {
if (player.getOpenInventory().getTopInventory().equals(gui)) {
player.closeInventory(InventoryCloseEvent.Reason.PLAYER);
}
}
}.runTaskLater(BlockAndSeek.getInstance(), 15 * 20);
}
}

View File

@@ -5,16 +5,20 @@ import org.jetbrains.annotations.NotNull;
import java.util.List;
import java.util.Objects;
import java.util.List;
import java.util.Objects;
import org.jetbrains.annotations.NotNull; // Или другой пакет аннотаций
public class RouletteList<T> {
private final List<T> items;
private int currentIndex;
public RouletteList(@NotNull List<T> items) {
this.items = Objects.requireNonNull(items);
this.currentIndex = 0;
if (items.isEmpty()) {
throw new IllegalArgumentException("List must not be empty");
}
this.items = List.copyOf(items);
this.currentIndex = 0;
}
public T next() {
@@ -22,4 +26,14 @@ public class RouletteList<T> {
currentIndex = (currentIndex + 1) % items.size();
return item;
}
}
public T previous() {
T item = items.get(currentIndex);
currentIndex = (currentIndex - 1 + items.size()) % items.size();
return item;
}
public T current() {
return items.get(currentIndex);
}
}