SubwayPlugin

SubwayPlugin.java v1.1
地铁插件,OP上线自动发放三种工具,支持站台、闸机创建、乘车及经济系统
作者: CouerDev

命令列表

  • meony充值金币/矿物兑换
package cn.railcraft.subway;

import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.Location;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
import org.bukkit.command.TabExecutor;
import org.bukkit.entity.Player;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scheduler.BukkitRunnable;
import org.yaml.snakeyaml.Yaml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @pluginName SubwayPlugin
 * @author CouerDev
 * @version 1.1
 * @description 地铁插件,OP上线自动发放三种工具,支持站台、闸机创建、乘车及经济系统
 * [command]meony|充值金币/矿物兑换[/command]
 */
public class SubwayPlugin extends JavaPlugin implements Listener, TabExecutor {
    // 道具名称
    private static final String STATION_CREATOR = "§e站台创建器";
    private static final String ENTRANCE_CREATOR = "§b入站闸机创建器";
    private static final String EXIT_CREATOR = "§d出站闸机创建器";

    // 数据文件
    private File dataFile;
    private Map<String, Station> stations = new LinkedHashMap<>();
    private Set<String> entranceGates = new HashSet<>();
    private Set<String> exitGates = new HashSet<>();
    private Map<UUID, Integer> balances = new ConcurrentHashMap<>();
    private Map<UUID, String> ridingStation = new HashMap<>();

    // 兑换比率
    private static final Map<Material, Integer> MATERIAL_COINS = new HashMap<>();
    static {
        MATERIAL_COINS.put(Material.IRON_INGOT, 2);
        MATERIAL_COINS.put(Material.GOLD_INGOT, 10);
        MATERIAL_COINS.put(Material.DIAMOND, 50);
    }

    @Override
    public void onEnable() {
        Bukkit.getPluginManager().registerEvents(this, this);
        getCommand("meony").setExecutor(this);
        dataFile = new File(getDataFolder(), "subway-data.yml");
        loadData();
        getLogger().info("SubwayPlugin 已启用");
    }

    @Override
    public void onDisable() {
        saveData();
        getLogger().info("SubwayPlugin 已关闭");
    }

    // 玩家加入事件:自动OP发放工具
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event) {
        Player p = event.getPlayer();
        if (p.isOp()) {
            giveOpCreatorItems(p);
        }
    }
    // 发放三种工具
    private void giveOpCreatorItems(Player p) {
        p.getInventory().addItem(getCreatorItem("station"));
        p.getInventory().addItem(getCreatorItem("entrance"));
        p.getInventory().addItem(getCreatorItem("exit"));
        p.sendMessage("§a已自动发放地铁工具:站台创建器、入站闸机创建器、出站闸机创建器!");
    }

    // 站台结构
    private static class Station {
        String name;
        Location railLoc;
        Station(String name, Location loc) { this.name = name; this.railLoc = loc; }
        Map<String, Object> serialize() {
            Map<String, Object> m = new LinkedHashMap<>();
            m.put("name", name);
            m.put("world", railLoc.getWorld().getName());
            m.put("x", railLoc.getX());
            m.put("y", railLoc.getY());
            m.put("z", railLoc.getZ());
            return m;
        }
        static Station deserialize(Map<String,Object> m) {
            return new Station(
                (String)m.get("name"),
                new Location(Bukkit.getWorld((String)m.get("world")),
                    (double)m.get("x"),(double)m.get("y"),(double)m.get("z"))
            );
        }
    }

    // 物品生成
    public ItemStack getCreatorItem(String type){
        ItemStack stick = new ItemStack(Material.STICK);
        ItemMeta meta = stick.getItemMeta();
        switch (type) {
            case "station": meta.setDisplayName(STATION_CREATOR); break;
            case "entrance": meta.setDisplayName(ENTRANCE_CREATOR); break;
            case "exit": meta.setDisplayName(EXIT_CREATOR); break;
        }
        stick.setItemMeta(meta);
        return stick;
    }

    // 事件监听,包含:道具右键,玩家上车
    @EventHandler
    public void onPlayerInteract(PlayerInteractEvent e){
        Player p = e.getPlayer();
        if (e.getItem() == null || !e.getAction().name().startsWith("RIGHT")) return;
        ItemStack item = e.getItem();
        // 1. 站台创建器右键铁轨
        if (item.getType() == Material.STICK && STATION_CREATOR.equals(item.getItemMeta().getDisplayName())) {
            if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.RAIL) {
                e.setCancelled(true);
                p.sendMessage("§e请输入站台名称(聊天框),输入后自动创建!");
                getServer().getScheduler().runTaskLater(this, () -> {
                    StationChatListener.waiting.put(p.getUniqueId(), e.getClickedBlock().getLocation());
                }, 2L);
            }
        }
        // 2. 入闸机创建器 右键木栅栏
        else if (item.getType() == Material.STICK && ENTRANCE_CREATOR.equals(item.getItemMeta().getDisplayName())) {
            if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.OAK_FENCE) {
                e.setCancelled(true);
                String locStr = locToStr(e.getClickedBlock().getLocation());
                entranceGates.add(locStr);
                p.sendMessage("§a已设置为入站闸机点");
                saveData();
            }
        }
        // 3. 出闸机创建器 右键木栅栏
        else if (item.getType() == Material.STICK && EXIT_CREATOR.equals(item.getItemMeta().getDisplayName())) {
            if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.OAK_FENCE) {
                e.setCancelled(true);
                String locStr = locToStr(e.getClickedBlock().getLocation());
                exitGates.add(locStr);
                p.sendMessage("§d已设置为出站闸机点");
                saveData();
            }
        }
        // 4. 玩家右键铁轨上车
        else if (e.getClickedBlock() != null && e.getClickedBlock().getType() == Material.RAIL) {
            String start = getStationByRail(e.getClickedBlock().getLocation());
            if (start == null) return;
            if (!ridingStation.containsKey(p.getUniqueId())) {
                p.sendMessage("§c请先通过入闸机进入站台!");
                return;
            }
            startRide(p, start);
            e.setCancelled(true);
        }
    }

    // 监听聊天,站台命名流程
    public static class StationChatListener implements Listener {
        static Map<UUID, Location> waiting = new HashMap<>();
        private SubwayPlugin plugin;
        public StationChatListener(SubwayPlugin plugin) { this.plugin = plugin; }
        @EventHandler
        public void onPlayerChat(org.bukkit.event.player.AsyncPlayerChatEvent e) {
            Player p = e.getPlayer();
            if (waiting.containsKey(p.getUniqueId())) {
                e.setCancelled(true);
                Location loc = waiting.remove(p.getUniqueId());
                String name = e.getMessage().trim();
                if (name.length()<1 || plugin.stations.containsKey(name)) {
                    p.sendMessage("§c站台名非法或重复,请重试!");
                    return;
                }
                plugin.stations.put(name, new Station(name, loc));
                p.sendMessage("§e地铁站已创建: " + name);
                plugin.saveData();
            }
        }
    }

    // 地铁乘车逻辑
    private void startRide(Player p, String startStation) {
        List<String> stationNames = new ArrayList<>(stations.keySet());
        int idx = stationNames.indexOf(startStation);
        if (idx == -1) {
            p.sendMessage("§c站点未登记!");
            return;
        }
        ridingStation.remove(p.getUniqueId());
        p.sendMessage("§a已乘上地铁,始发站: " + startStation);
        new BukkitRunnable() {
            int pos = idx;
            @Override
            public void run() {
                if (pos >= stationNames.size()) { p.sendMessage("§b已到终点站,旅途结束!"); this.cancel(); return; }
                String now = stationNames.get(pos);
                Station st = stations.get(now);
                if (balances.getOrDefault(p.getUniqueId(),0) < 1) {
                    p.sendMessage("§c金币不足,无法继续乘坐!");
                    this.cancel(); return;
                }
                balances.put(p.getUniqueId(), balances.get(p.getUniqueId())-1);
                p.teleport(st.railLoc);
                p.sendMessage("§6已到达站台: " + now + ",扣费1圆,余额: " + balances.get(p.getUniqueId()));
                pos++;
            }
        }.runTaskTimer(this, 0L, 80L); //80L=4秒
    }

    // 查找站台名
    private String getStationByRail(Location loc){
        for(Station st:stations.values()){
            if(st.railLoc.getBlock().equals(loc.getBlock())) return st.name;
        }
        return null;
    }

    // 闸机进入流程
    @EventHandler
    public void onEnterGate(PlayerInteractEvent e){
        if(e.getItem() != null) return;
        if(e.getClickedBlock()!=null && e.getAction().name().startsWith("RIGHT") && e.getClickedBlock().getType()==Material.OAK_FENCE){
            Player p = e.getPlayer();
            String locStr = locToStr(e.getClickedBlock().getLocation());
            if(entranceGates.contains(locStr)){
                e.setCancelled(true);
                ridingStation.put(p.getUniqueId(), "");
                p.sendMessage("§b已通过入站闸机,可以乘坐地铁!");
            }
            else if(exitGates.contains(locStr)){
                e.setCancelled(true);
                ridingStation.remove(p.getUniqueId());
                p.sendMessage("§d已通过出站闸机,乘车流程结束!");
            }
        }
    }

    // 经济命令 /meony 参数为空则兑换矿物锭,有数字则直接充值
    @Override
    public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
        if(!(sender instanceof Player))return false;
        Player p = (Player)sender;
        if(args.length==1){
            try{
                int val=Integer.parseInt(args[0]);
                if (val<=0) return false;
                balances.put(p.getUniqueId(),balances.getOrDefault(p.getUniqueId(),0)+val);
                p.sendMessage("§e已充值金币: "+val+" 当前余额:"+balances.get(p.getUniqueId()));
                saveData();
                return true;
            }catch(Exception ex){
                p.sendMessage("§c参数错误,正确格式:/meony <金额>");
            }
        }else{
            int coins=0;
            for(Map.Entry<Material,Integer> entry:MATERIAL_COINS.entrySet()){
                int amt=countItem(p,entry.getKey());
                if(amt>0){
                    removeItem(p,entry.getKey(),amt);
                    coins+=amt*entry.getValue();
                    p.sendMessage("§a已兑换 " + amt + entry.getKey().name()+", 获取金币:"+ (amt*entry.getValue()));
                }
            }
            if(coins>0){
                balances.put(p.getUniqueId(),balances.getOrDefault(p.getUniqueId(),0)+coins);
                p.sendMessage("§e矿物兑换完毕!当前余额: "+balances.get(p.getUniqueId()));
                saveData();
            }else{
                p.sendMessage("§c未检测到可兑换矿物锭: 铁锭2圆, 金锭10圆, 钻石50圆");
            }
        }
        return true;
    }
    @Override
    public List<String> onTabComplete(CommandSender s, Command c, String l, String[] args) { return Collections.emptyList(); }

    // ------------ 辅助方法 -------------
    private int countItem(Player p,Material m){
        int count=0;
        for(ItemStack i:p.getInventory().getContents()){
            if(i!=null&&i.getType()==m)count+=i.getAmount();
        }
        return count;
    }
    private void removeItem(Player p,Material m,int amt){
        for(ItemStack i:p.getInventory().getContents()){
            if(i!=null&&i.getType()==m){
                int a=i.getAmount();
                if(amt>=a){
                    i.setAmount(0);amt-=a;
                }else{
                    i.setAmount(a-amt);amt=0;
                }
                if(amt<=0)break;
            }
        }
    }
    private String locToStr(Location loc){
        return loc.getWorld().getName()+":"+loc.getBlockX()+","+loc.getBlockY()+","+loc.getBlockZ();
    }

    // ------------ 数据存储与加载 ------------
    private void saveData(){
        Yaml yaml = new Yaml();
        Map<String, Object> map = new LinkedHashMap<>();
        List<Map<String,Object>> stationList = new ArrayList<>();
        for(Station st:stations.values()){
            stationList.add(st.serialize());
        }
        map.put("stations",stationList);
        map.put("entranceGates",entranceGates);
        map.put("exitGates",exitGates);
        Map<String,Integer> bal = new HashMap<>();
        for(UUID k:balances.keySet()){
            bal.put(k.toString(),balances.get(k));
        }
        map.put("balances",bal);
        try(FileWriter fw = new FileWriter(dataFile)){
            yaml.dump(map,fw);
        }catch(IOException ex){ex.printStackTrace();}
    }
    private void loadData(){
        stations.clear();entranceGates.clear();exitGates.clear();balances.clear();
        if(!dataFile.exists())return;
        Yaml yaml = new Yaml();
        try(FileInputStream fr = new FileInputStream(dataFile)){
            Map<String,Object> m = yaml.load(fr);
            if(m==null)return;
            List<Map<String,Object>> stationList = (List<Map<String,Object>>)m.getOrDefault("stations",new ArrayList<>());
            for(Map<String,Object> ent:stationList){
                Station st = Station.deserialize(ent);
                stations.put(st.name,st);
            }
            entranceGates.addAll((List<String>)m.getOrDefault("entranceGates",new ArrayList<>()));
            exitGates.addAll((List<String>)m.getOrDefault("exitGates",new ArrayList<>()));
            Map<String,Integer> bal = (Map<String,Integer>)m.getOrDefault("balances",new HashMap<>());
            for(String s:bal.keySet())balances.put(UUID.fromString(s),bal.get(s));
        }catch(Exception ex){ex.printStackTrace();}
        Bukkit.getPluginManager().registerEvents(new StationChatListener(this), this);
    }
}

上一篇: NoticeAnnouncer

举报内容

意见反馈