//LiteLoaderScript Dev Helper
/// <reference path="d:/Game/MCBE/Server/LiteloaderSE/JS/HelperLib-master/src/index.d.ts"/> 
/* jshint esversion: 11 */
/*jshint -W069 */
let INFO = {
    name: "WnUnion",
    intro: "联盟",
    version: [1, 3, 2],
    other: {
        auth: "Wn1027",
        group_QQ: "311860068",
    }
};
// 1.2.0 导出大量函数接口
// 1.2.0 修复表单异步导致的bug
// 1.2.0 放置重力方块与能被水冲走的方块不积累个人建设度
// 1.2.0 英语适配

ll.registerPlugin(INFO.name, INFO.intro, INFO.version, INFO.other);

let PATH = {
    root: `./plugins/WnUnion`,
    log: `./logs/WnUnion`
};

// 日志设置
function setLogFile(){
    logger.setConsole(true, 4);
    let date = new Date();
    logger.setFile(`${PATH.log}/WnUnion-${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}.txt`, 4);
    return true;
}
setInterval(() => {
    setLogFile(); 
},60000);

// 默认配置文件
let default_config = {
    Language: 'zh_CN',
    CreateCost: 0,               // 创建联盟费用
    CreateAuthority: true,       // 创建联盟是否需腐竹给予权限(/union lord "error block" true)
    UnionNameLimit: 15,          // 联盟名称字符数上限
    UnionDiscriptionLimit: 100,  // 联盟描述字符数上限

    TpaEnabled: false,           // 是否允许传送到联盟成员
    TpaPrice: 0,                 // Tpa价格
    TpPosEnabled: false,         // 是否开启传送锚点功能
    TpPosPrice: 0,               // 设置传送锚点价格（删除不返还）
    TpPosNum: 2,                 // 传送锚点限制数量

    BillSavedDays: 60,           // 账单、日志保存天数（表单）./log/WnUnion/下的日志永远不会删

    BankEnabled: false,          // 是否开启联盟财富功能
    UnionChat: true,             // 是否启用联盟内聊天
    ChatMedal: false,            // 聊天勋章（此功能会代理聊天栏样式，与其他聊天栏样式修改的插件冲突）
    PAPI: false,                 // 是否启用: PAPI公共变量导出  联盟勋章: %player_UnionMedal% , 联盟等级(不带符号):%player_UnionLevel%
    BlockPlaceEnabled: true,     // 是否启用: 放置方块获得个人建设度分数（如果想自定义获取个人分数的方式, 就需要关闭此项）         
    EconomyType: 'llmoney',      // 货币类型: llmoney, scoreboard, func(外部函数)
    EconomyName: '§r§e金币§r',   // 货币名称
    EconomyScoreBoardName: '',   // 货币计分板名称
    EconomyFunc: {       
        namespace: '',           // 经济函数命名空间
        getMoney: 'getMoney',    // 导入函数名称
        setMoney: 'setMoney',
        addMoney: 'addMoney',
        reduceMoney: 'reduceMoney',
    }
};
let default_config_en = {
    Language: 'en_US',
    CreateCost: 0,               // Create Union Fee
    CreateAuthority: true,       // Do you need permission from the server owner to create an union(/union lord "error block" true)
    UnionNameLimit: 15,          // Maximum number of characters in union name
    UnionDiscriptionLimit: 100,  // Maximum number of characters in union description

    TpaEnabled: false,           // Allow delivery to union members
    TpaPrice: 0,                 // Tpa price
    TpPosEnabled: false,         // Whether to enable the transfer anchor(TP pos) function
    TpPosPrice: 0,               // Set TP pos price（Delete non return coins）
    TpPosNum: 2,                 // TP pos limit quantity

    BillSavedDays: 60,           // Days to save bills and logs (form) except the logs under ./log/WnUnion/ 

    BankEnabled: false,          // Whether to activate the alliance bank function
    UnionChat: true,             // Enable intra union chat
    ChatMedal: false,            // Chat Medal（Conflicts with other chat plugins）
    PAPI: false,                 // enabled: PAPI BEPlaceholderAPI | UnionMedal: %player_UnionMedal% | UnionLevel: %player_UnionLevel%
    BlockPlaceEnabled: true,     // enabled: Place blocks to obtain points     
    EconomyType: 'llmoney',      // llmoney, scoreboard, func(external function)
    EconomyName: '§r§eCoin§r',   // 
    EconomyScoreBoardName: '',   // 
    EconomyFunc: {       
        namespace: '',           // 
        getMoney: 'getMoney',    // 
        setMoney: 'setMoney',
        addMoney: 'addMoney',
        reduceMoney: 'reduceMoney',
    }
};

function loadConfig(cfg){
    return {
        Language: cfg.Language ?? 'zh_CN',               
        CreateCost: cfg.CreateCost ?? 0,               // 创建联盟费用
        CreateAuthority: cfg.CreateAuthority ?? true,       // 创建联盟是否需腐竹给予权限(/union lord "error block" true)
        UnionNameLimit: cfg.UnionNameLimit ?? 15,          // 联盟名称字符数上限
        UnionDiscriptionLimit: cfg.UnionDiscriptionLimit ?? 100,  // 联盟描述字符数上限
    
        TpaEnabled: cfg.TpaEnabled ?? false,           // 是否允许传送到联盟成员
        TpaPrice: cfg.TpaPrice ?? 0,                 // Tpa价格
        TpPosEnabled: cfg.TpPosEnabled ?? false,         // 是否开启传送锚点功能
        TpPosPrice: cfg.TpPosPrice ?? 0,               // 设置传送锚点价格（删除不返还）
        TpPosNum: cfg.TpPosNum ?? 2,                 // 传送锚点限制数量
    
        BillSavedDays: cfg.BillSavedDays ?? 60,           // 账单、日志保存天数（表单）./log/WnUnion/下的日志永远不会删
    
        BankEnabled: cfg.BankEnabled ?? false,          // 是否开启联盟财富功能
        UnionChat: cfg.UnionChat ?? true,                             // 是否启用联盟内聊天
        ChatMedal: cfg.ChatMedal ?? false,            // 聊天勋章（此功能会代理聊天栏样式，与其他聊天栏样式修改的插件冲突）
        PAPI: cfg.PAPI ?? false,                 // 是否启用PAPI公共变量导出联盟 %player_UnionMedal%
        
        BlockPlaceEnabled: cfg.BlockPlaceEnabled ?? true,

        EconomyType: cfg.EconomyType ?? 'llmoney',      // 货币类型: llmoney, scoreboard, func(外部函数)
        EconomyName: cfg.EconomyName ?? '§r§e金币§r',   // 货币名称
        EconomyScoreBoardName: cfg.EconomyScoreBoardName ?? '',   // 货币计分板名称
        EconomyFunc: {       
            namespace: cfg?.EconomyFunc?.namespace ?? '',           // 经济函数命名空间
            getMoney: cfg?.EconomyFunc?.getMoney ?? 'getMoney',    // 导入函数名称
            setMoney: cfg?.EconomyFunc?.setMoney ?? 'setMoney',
            addMoney: cfg?.EconomyFunc?.addMoney ?? 'addMoney',
            reduceMoney: cfg?.EconomyFunc?.reduceMoney ?? 'reduceMoney',
        }
    };
}

let CONF = {
    CONFIG:        new JsonConfigFile(`${PATH.root}/config.json`,JSON.stringify(default_config)),
    UNIONSDATA:    new JsonConfigFile(`${PATH.root}/unionData.json`),
    PLAYERDATA:    new JsonConfigFile(`${PATH.root}/playerData.json`),
    LOG:           new JsonConfigFile(`${PATH.root}/log.json`),
    BANKBILL:      new JsonConfigFile(`${PATH.root}/bankBill.json`),
    CONSTUCTBILL:  new JsonConfigFile(`${PATH.root}/constructBill.json`),
};
// config.json 更新载入
let cfg = loadConfig(JSON.parse(CONF.CONFIG.read()));
for (let key in cfg){
    CONF.CONFIG.set(key, cfg[key]);
}

// 多语言载入
Language();
function tr() {
    let args = Array.from(arguments);
    let pattern = args.shift();
    return i18n.trl(CONF.CONFIG.get('Language')??'zh_CN', pattern, ...args);
}

// 联盟数据更新
let UnionsData = JSON.parse(CONF.UNIONSDATA.read());
for (let guid in UnionsData){ // 更新数据及初始化
    UnionsData[guid] = loadUnion(UnionsData[guid]);
    saveUnion(guid, UnionsData[guid]);
}

let LEVEL = [
    {value: 0, color: '', text: tr('.member')},
    {value: 1, color: '§3', text: tr('.manager')},
    {value: 2, color: '§d', text: tr('.lord')},
];
let DIM_TEXT = [tr('.overworld'), tr('.theNether'), tr('.theEnd')];


let UnionsDataSort = {
    default: [],
    blockPlace: [],
    money: [],
    memberNum: []
};

function unionSort(UnionsData){
    let list = Object.values(UnionsData);
    UnionsDataSort.default = [...list].map((union)=>union.guid);
    UnionsDataSort.blockPlace = [...list].sort((a, b)=>b.score.blockPlace - a.score.blockPlace).map((union)=>union.guid);
    UnionsDataSort.money = [...list].sort((a, b)=>b.money - a.money).map((union)=>union.guid);
    UnionsDataSort.time = [...list].sort((a, b)=>parseDateStr(b.regTime) - parseDateStr(a.regTime)).map((union)=>union.guid);
    UnionsDataSort.memberNum = [...list].sort((a, b)=>Object.keys(b.playerList).length - Object.keys(a.playerList).length).map((union)=>union.guid);
}
unionSort(UnionsData);
setInterval(()=>{
    unionSort(UnionsData);
}, 300000);


// 所有联盟
function unionList(pl, lastForm=()=>{}){
    let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
    let fm_unionList = mc.newSimpleForm()
    .setTitle(tr('gui.allUnion'))
    .setContent(``);
    if (playerData.unionGuid == null){
        fm_unionList.addButton(tr('gui.create.button'));
    }
    let unionGuidList;
    switch (playerData.unionOrder){
        case 0: //'default'
            unionGuidList = UnionsDataSort.default;
            fm_unionList.addButton(tr('gui.allUnion.order.default'));
            for (let unionGuid of unionGuidList){
                fm_unionList.addButton(`§小${UnionsData[unionGuid].name}`, UnionsData[unionGuid].icon);
            }
            break;
        case 1: //'blockPlace':
            unionGuidList = UnionsDataSort.blockPlace;
            fm_unionList.addButton(tr('gui.allUnion.order.level'));
            for (let unionGuid of unionGuidList){
                fm_unionList.addButton(`§小${UnionsData[unionGuid].name}\n§r${unionLevelText(UnionsData[unionGuid].score.blockPlace)}`, UnionsData[unionGuid].icon);
            }
            break;
        case 2: //'money':
            unionGuidList = UnionsDataSort.money;
            fm_unionList.addButton(tr('gui.allUnion.order.money'));
            for (let unionGuid of unionGuidList){
                fm_unionList.addButton(`§小${UnionsData[unionGuid].name}\n§r§3[${UnionsData[unionGuid].money}]`, UnionsData[unionGuid].icon);
            }
            break;
        case 3: //'time':
            unionGuidList = UnionsDataSort.time;
            fm_unionList.addButton(tr('gui.allUnion.order.reg'));
            for (let unionGuid of unionGuidList){
                fm_unionList.addButton(`§小${UnionsData[unionGuid].name}\n§r[${UnionsData[unionGuid].regTime}]`, UnionsData[unionGuid].icon);
            }
            break;
        case 4: //'memberNum':
            unionGuidList = UnionsDataSort.memberNum;
            fm_unionList.addButton(tr('gui.allUnion.order.member'));
            for (let unionGuid of unionGuidList){
                fm_unionList.addButton(`§小${UnionsData[unionGuid].name}\n§r${Object.keys(UnionsData[unionGuid].playerList).length}人`, UnionsData[unionGuid].icon);
            }
            break;
    }

    pl.sendForm(fm_unionList, unionList_run);

    function unionList_run(pl, id){
        if (id == null){return lastForm();}
        if (playerData.unionGuid == null){
            // 创建联盟
            if (id == 0){
                return createUnion(pl, ()=>{unionList(pl);});    
            }
            id = id - 1;
        }
        // 联盟排序
        if (id == 0){
            playerData.unionOrder += 1;
            if (playerData.unionOrder == 5){
                playerData.unionOrder = 0;
            }
            CONF.PLAYERDATA.set(pl.xuid, playerData);
            return unionList(pl, lastForm);
        }
        // 联盟详情
        else{
            let unionGuid = unionGuidList[id-1];
            return unionInfo(pl, unionGuid, ()=>{unionList(pl);});
        }     
    }
}

// 创建联盟
function createUnion(pl, lastForm=()=>{}){
    if ((getMoney(pl.xuid) ?? 0) < CONF.CONFIG.get("CreateCost")){
        return menu_end(pl, tr('gui.create.title'), tr('gui.create.insufficientmoney', CONF.CONFIG.get("EconomyName"), CONF.CONFIG.get("CreateCost")), lastForm);
    }
    if (CONF.CONFIG.get("CreateAuthority")){
        if (loadPlData(CONF.PLAYERDATA.get(pl.xuid)).unionLord == false){
            return menu_end(pl, tr('gui.create.title'), tr('gui.create.noAccess'), lastForm);
        }
    }
    if (loadPlData(CONF.PLAYERDATA.get(pl.xuid))?.unionGuid != null){
        return menu_end(pl, tr('gui.create.title'), tr('gui.create.haveJoined'), lastForm);
    }
    let fm_createUnion = mc.newCustomForm()
    .setTitle(tr('gui.create.title'))
    .addLabel(tr('gui.create.cost.label', CONF.CONFIG.get("EconomyName"), CONF.CONFIG.get("CreateCost")))
    .addInput(tr('gui.create.newName'));

    pl.sendForm(fm_createUnion, createUnion_run);

    function createUnion_run(pl, args){
        if(args == null){return lastForm();}
        let newUnionName = args[1];
        if (newUnionName.trim() == ''){
            return menu_end(pl, tr('gui.create.title'), tr('gui.bulletin.new.notempty'), fm_createUnion, createUnion_run);
        }
        if (newUnionName.length > (CONF.CONFIG.get('UnionNameLimit')??Infinity)){
            return menu_end(pl, tr('gui.create.title'), tr('gui.create.unionNameLimit', newUnionName.length, CONF.CONFIG.get('UnionNameLimit')), fm_createUnion, createUnion_run);
        }
        let unionGuid = system.randomGuid();
        if (unionGuid in UnionsData){return;}
        let creatTime = system.getTimeStr();
        saveUnion(unionGuid, loadUnion({
            guid: unionGuid,
            name: newUnionName,
            founder: {xuid: pl.xuid, realName: pl.realName},
            lord: {xuid: pl.xuid, realName: pl.realName},
            regTime: creatTime,
            playerList: {
                [pl.xuid]: {
                    xuid: pl.xuid,
                    realName: pl.realName,
                    level: LEVEL.length-1,
                    lastLogTime: creatTime,
                    joinTime: creatTime
                }
            }
        }));
        let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
        playerData.unionGuid = unionGuid;
        CONF.PLAYERDATA.set(pl.xuid, playerData);
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (CONF.CONFIG.get("CreateCost") > 0){
            reduceMoney(pl.xuid, CONF.CONFIG.get("CreateCost"), tr('gui.create.success.tip', newUnionName));
        }
        addUnionLog(union.guid, tr('gui.create.success.tip.1', pl.realName, newUnionName));
        mc.broadcast(tr('gui.create.success.tip.1', pl.realName, newUnionName));
        unionSort(UnionsData);
        return unionInfo(pl, unionGuid);
    }
}

// 联盟信息
function unionInfo(pl, unionGuid, lastForm=()=>{}){
    let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
    // if (playerData == undefined){return menu_end(pl, '§l我的联盟', '§c你没有加入联盟');}
    // let unionGuid = playerData.unionGuid;
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (union == null){return;}
    let fm_unionInfo = mc.newSimpleForm()
    .setTitle(`§l${unionGuid == playerData?.unionGuid ? union.name : tr('gui.unionInfo.title', union.name)}`)
    .setContent(`\n${display_unionInfo(unionGuid)}\n `)
    .addButton(tr('gui.bulletin'), 'textures/ui/comment')
    .addButton(tr('gui.memberList', Object.keys(union.playerList).length), 'textures/ui/dressing_room_skins');
    if (pl.isOP() || unionGuid == playerData?.unionGuid){
        fm_unionInfo.addButton(tr('gui.contribute'), 'textures/ui/icon_minecoin_9x9');
    }else{
        fm_unionInfo.addButton(tr('gui.application'), 'textures/ui/book_addtextpage_default');
    }
    fm_unionInfo.addButton(tr('gui.tpPos'), 'textures/ui/speed_effect');
    fm_unionInfo.addButton(tr('gui.unionLog'), 'textures/ui/icon_book_writable');
    fm_unionInfo.addButton(tr('gui.allUnion'), 'textures/ui/World');
    if (pl.isOP() || (pl.xuid == union.lord.xuid || union.playerList[pl.xuid]?.level >= 1)){
        fm_unionInfo.addButton(tr('gui.unionManager'), 'textures/ui/icon_setting');
    }
    
    pl.sendForm(fm_unionInfo, unionInfo_run);

    function unionInfo_run(pl, id){
        if (id == null){return lastForm();}
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        if (id == 0){ // 联盟公告
            return unionBulletin(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
        }
        if (id == 1){ // 成员列表
            return unionPlayerList(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
        }
        if (id == 2){ // 存取款 | 申请加入
            if (pl.isOP() || playerData.unionGuid == unionGuid){
                return unionContribute(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
            }else{
                return unionJoin(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
            }
        }
        if (id == 3){ // 传送锚点
            return unionTpPos(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
        }
        if (id == 4){ // 联盟日志
            return unionNotice(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
        }
        if (id == 5){ // 所有联盟
            return unionList(pl); 
        }
        if (id == 6){ // 联盟管理
            return unionManager(pl, unionGuid, ()=>{unionInfo(pl, unionGuid, ()=>{lastForm();});});
        }
    }
}

// 联盟公告
function unionBulletin(pl, unionGuid, lastForm=()=>{}){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (union == null){return;}
    let fm_bulletin = mc.newSimpleForm()
    .setTitle(tr('gui.bulletin'))
    .setContent(``);
    if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
        fm_bulletin.addButton(tr('gui.bulletin.new'));
    }
    let i = 0;
    for (let bulletin of union.bulletin){
        fm_bulletin.addButton(`§小${i == 0 ? tr('gui.bulletin.top') : ''}${bulletin.title}\n§r${bulletin.time}`);
        i++;
    }
    
    pl.sendForm(fm_bulletin, bulletin_run);

    function bulletin_run(pl, id){
        if (id == null){return lastForm();}
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        let fm_newBulletin; 
        if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
            if (id == 0){
                fm_newBulletin = mc.newCustomForm()
                .setTitle(tr('gui.bulletin.new'))
                .addLabel(tr('gui.bulletin.new.tip'))
                .addInput(tr('gui.bulletin.new.topic'))
                .addInput(tr('gui.bulletin.new.content'));
                return pl.sendForm(fm_newBulletin, newBulletin_run);
            }
            id = id -1;
        }
        let bulletinIndex = id;
        let fm_bulletinInfo = mc.newSimpleForm()
        .setTitle(tr('gui.bulletin.title'));
        let content = `\n`;
        content += tr('gui.bulletin.topic', union.bulletin[id].title);
        content += tr('gui.bulletin.sender', union.bulletin[id].realName);
        content += tr('gui.bulletin.sendTime', union.bulletin[id].time);
        content += tr('gui.bulletin.content', union.bulletin[id].content);
        fm_bulletinInfo.setContent(content);
        fm_bulletinInfo.addButton(tr('.confirm'));
        if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
            fm_bulletinInfo.addButton(tr('gui.bulletin.setTop'));
            fm_bulletinInfo.addButton(tr('gui.bulletin.delete'));
        }

        pl.sendForm(fm_bulletinInfo, bulletinInfo_run);

        function bulletinInfo_run(pl, id){
            if (id == null || id == 0){return unionBulletin(pl, unionGuid, lastForm);}
            let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
            if (union == null){return;}
            if (id == 1){
                union.bulletin.unshift(union.bulletin.splice(bulletinIndex,1)[0]);
                saveUnion(union.guid, union);
                sendPlayerNotice(pl.xuid, {title: tr('gui.bulletin'), msg: tr('gui.bulletin.setTop.success', union.bulletin[union.bulletin.length-1].title)});
                return unionBulletin(pl, unionGuid, lastForm);
            }
            if (id == 2){
                pl.sendModalForm(tr('gui.bulletin'), tr('gui.bulletin.delete.confirm', union.bulletin[bulletinIndex].title), tr('.delete'), tr('.cancel'), (pl, res)=>{
                    if (res == null || res == false){return pl.sendForm(fm_bulletinInfo, bulletinInfo_run);}
                    if (res == true){
                        sendUnionNotice(union.guid, [1,2], {title: tr('gui.bulletin'), msg: tr('gui.bulletin.delete.success', pl.realName, union.bulletin[bulletinIndex].title)});
                        addUnionLog(union.guid, tr('gui.bulletin.delete.success', pl.realName, union.bulletin[bulletinIndex].title));
                        union.bulletin.splice(bulletinIndex, 1);
                        saveUnion(union.guid, union);
                        return unionBulletin(pl, unionGuid, lastForm);
                    }
                });
            }
        }

        
        function newBulletin_run(pl, args){
            if (args == null){return pl.sendForm(fm_bulletin, bulletin_run);}
            let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
            if (union == null){return;}
            if (!(args[1] != '' && args[2] != '')){
                return menu_end(pl, tr('gui.bulletin.new'), tr('gui.bulletin.new.notempty'), fm_newBulletin, newBulletin_run);
            }
            args[2] = args[2].replaceAll('\\n', '\n ');
            if (union.bulletin.length == 0){
                union.bulletin.unshift({xuid: pl.xuid, realName: pl.realName, title: args[1], content: args[2], time: system.getTimeStr()});
            }else{
                union.bulletin.splice(1, 0, {xuid: pl.xuid, realName: pl.realName, title: args[1], content: args[2], time: system.getTimeStr()});
            }
            saveUnion(union.guid, union);
            sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.bulletin'), msg: tr('gui.bulletin.new.success', pl.realName, args[1])});
            addUnionLog(union.guid, tr('gui.bulletin.new.success', pl.realName, args[1]));
            return unionBulletin(pl, unionGuid, lastForm);
        }
    }
}

// 联盟通知
function unionNotice(pl, unionGuid, lastForm=()=>{}){
    return billDisplay(pl, 'UNIONNOTICE', unionGuid, lastForm);
}

// 成员列表
function unionPlayerList(pl, unionGuid, lastForm=()=>{}){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (union == null){return;}
    let fm_playerList = mc.newSimpleForm()
    .setTitle(tr('gui.memberList.title', union.name))
    .setContent(``);
    for (let playerXuid in union.playerList){
        let playerInfo = union.playerList[playerXuid];
        switch (playerInfo.level){
            case 0:
                fm_playerList.addButton(`§小§l${mc.getPlayer(playerInfo.xuid)!=null?'§2':''}${playerInfo.realName}`);
                break;
            case 1:
                fm_playerList.addButton(`§小§l${LEVEL[1].color}[${LEVEL[1].text}]§r§l ${mc.getPlayer(playerInfo.xuid)!=null?'§2':''}${playerInfo.realName}`);
                break;
            case 2: 
                fm_playerList.addButton(`§小§l${LEVEL[2].color}[${LEVEL[2].text}]§r§l ${mc.getPlayer(playerInfo.xuid)!=null?'§2':''}${playerInfo.realName}`);
                break;
            default:
                fm_playerList.addButton(`§小§l§c[ERROR]§r§l ${playerInfo.realName}`);
                break;
        }
    }

    pl.sendForm(fm_playerList, playerList_run);

    function playerList_run(pl, id){
        if (id == null){return lastForm();}
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        let playerInfo = loadMemberData(union.playerList[Object.keys(union.playerList)[id]]);
        let fm_playerInfo = mc.newSimpleForm()
        .setTitle(tr('gui.memberInfo'));
        let content = ``;
        content += tr('gui.member.name', playerInfo.realName);
        content += tr('gui.member.level', LEVEL[playerInfo.level].text);
        content += tr('gui.member.contribute', (playerInfo.contribute/1000).toFixed(1));
        content += tr('gui.member.lastLogTime', playerInfo.lastLogTime);
        content += tr('gui.member.joinTime', playerInfo.joinTime);
        fm_playerInfo.setContent(`\n${content}\n `);

        if (mc.getPlayer(playerInfo.xuid) != null){
            if (CONF.CONFIG.get('TpaEnabled') && pl.xuid in union.playerList){
                fm_playerInfo.addButton(`${tr('gui.member.tp.button')}§r\n${(CONF.CONFIG.get('TpaPrice') != null && CONF.CONFIG.get('TpaPrice') > 0) ? `${CONF.CONFIG.get('TpaPrice')} ${CONF.CONFIG.get('EconomyName')}`:''}`);
            }else{
                fm_playerInfo.addButton(tr('gui.member.online'));
            }
        }else{
            fm_playerInfo.addButton(tr('gui.member.offline'));
        }
        
        if (pl.xuid == playerInfo.xuid){
            if (union.playerList[pl.xuid]?.level == 2){
                fm_playerInfo.addButton(tr('.confirm'));
            }else{
                fm_playerInfo.addButton(tr('gui.member.exit.button'));
            }
            
        }else{
            if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
                fm_playerInfo.addButton(tr('gui.member.kick.button'));
            }else{
                fm_playerInfo.addButton(tr('.confirm'));
            }
        }
        
        if (pl.isOP() || union.playerList[pl.xuid]?.level >= 2){
            if (union.playerList[playerInfo.xuid]?.level == 0){
                fm_playerInfo.addButton(tr('gui.member.setOp.button'));
            }else if (union.playerList[playerInfo.xuid]?.level == 1){
                fm_playerInfo.addButton(tr('gui.member.deop.button'));
            }
        }

        pl.sendForm(fm_playerInfo, playerInfo_run);

        function playerInfo_run(pl, id){
            if (id == null){return unionPlayerList(pl, unionGuid, lastForm);}
            let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
            if (union == null){return;}
            // 传送至成员
            if (id == 0){
                if (mc.getPlayer(playerInfo.xuid) != null){
                    if (CONF.CONFIG.get('TpaEnabled') && pl.xuid in union.playerList){
                        // fm_playerInfo.addButton(`§3传送至该成员`);
                        let pl_1 = mc.getPlayer(playerInfo.xuid);
                        if (playerInfo.xuid.xuid == pl.xuid){
                            return menu_end(pl, tr('gui.member.tpa.title'), tr('gui.member.tpa.notself'), fm_playerInfo, playerInfo_run);
                        }
                        if ((CONF.CONFIG.get('TpaPrice')??0)>0){
                            if ((getMoney(pl.xuid)??0) < (CONF.CONFIG.get('TpaPrice')??0)){
                                return menu_end(pl, tr('gui.member.tpa.title'), `${tr('gui.member.tpa.insufficientmoney')} (${(getMoney(pl.xuid)??0) } < ${(CONF.CONFIG.get('TpaPrice')??0)})`, fm_playerInfo, playerInfo_run);
                            }
                        }
                        pl_1.sendModalForm(tr('gui.member.tpa.title'), tr('gui.member.tpa.postContent', pl.realName), tr('gui.member.tpa.agree'), tr('.refuse'), (pl_1, res)=>{
                            if (pl == null){return;}
                            if (res == false || res == null){
                                pl.tell(tr('gui.member.tpa.refuse.tip', pl_1.realName));
                            }else{
                                if ((CONF.CONFIG.get('TpaPrice')??0)>0){
                                    if ((getMoney(pl.xuid)??0) < (CONF.CONFIG.get('TpaPrice')??0)){
                                        return pl.tell(tr('gui.member.tpa.insufficientmoney'));
                                    }
                                    reduceMoney(pl.xuid, CONF.CONFIG.get('TpaPrice'), tr('gui.member.tpa.note', union.name, pl_1.realName));
                                }
                                pl.teleport(mc.getPlayer(playerInfo.xuid).pos);
                                pl.tell(tr('gui.member.tpa.agree.tip', pl_1.realName));
                            }
                        });
                        pl.tell(tr('gui.member.tpa.post.tip', pl_1.realName));
                        return;
                    }else{
                        return unionPlayerList(pl, unionGuid, lastForm);
                        //fm_playerInfo.addButton(`§2玩家在线`);
                    }
                }else{
                    return unionPlayerList(pl, unionGuid, lastForm);
                    //fm_playerInfo.addButton(`§7玩家离线`);
                }
            }
            
            // 退出联盟 踢出联盟
            if (id == 1){
                if (pl.xuid != playerInfo.xuid){
                    if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
                        if (union.playerList[playerInfo.xuid]?.level == 2){
                            return menu_end(pl, tr('gui.member.kick.title'), tr('gui.member.exit.error.noLord'), fm_playerInfo, playerInfo_run);
                        }
                        if (!pl.isOP() && union.playerList[playerInfo.xuid]?.level == 1 && union.playerList[playerInfo.xuid]?.level == 1){
                            return menu_end(pl, tr('gui.member.kick.title'), tr('gui.member.exit.error.onlyLord'), fm_playerInfo, playerInfo_run);
                        }
                        pl.sendModalForm(tr('gui.member.kick.title'), tr('gui.member.exit.kick.tip', playerInfo.realName, union.name), '§c'+tr('.confirm'), tr('.cancel'), (pl, res)=>{
                            if (res == null || res == false){return pl.sendForm(fm_playerInfo, playerInfo_run);}
                            if (res == true){
                                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                                if (union == null){return;}
                                delete union.playerList[playerInfo.xuid];
                                saveUnion(union.guid, union);
                                let playerData = loadPlData(CONF.PLAYERDATA.get(playerInfo.xuid));
                                playerData.unionGuid = null;
                                CONF.PLAYERDATA.set(playerInfo.xuid, playerData);
                                sendPlayerNotice(playerInfo.xuid, {title: tr('gui.member.exit.kick.title'), msg: tr('gui.member.exit.kick.msg', pl.realName, union.name)});
                                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.member.exit.kick.title'), msg: tr('gui.member.exit.kick.log', playerInfo.realName, pl.realName, union.name)});
                                addUnionLog(union.guid, tr('gui.member.exit.kick.log', playerInfo.realName, pl.realName, union.name));
                                mc.broadcast(tr('gui.member.exit.broadcast', playerInfo.realName, union.name));
                                return;
                            }
                        });
                        //fm_playerInfo.addButton(`§c§l【管理】移出联盟`);
                    }else{
                        return unionPlayerList(pl, unionGuid, lastForm);
                        //fm_playerInfo.addButton(`§l确定`);
                    }
                }else{
                    if (union.playerList[pl.xuid]?.level == 2){
                        return unionPlayerList(pl, unionGuid, lastForm);
                        //fm_playerInfo.addButton(`§l确定`);
                    }else{
                        pl.sendModalForm(tr('gui.member.exit.title'), tr('gui.member.exit.confirm', union.name), tr('.confirm'), tr('.cancel'), (pl, res)=>{
                            if (res == null || res == false){return pl.sendForm(fm_playerInfo, playerInfo_run);}
                            if (res == true){
                                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                                if (union == null){return;}
                                delete union.playerList[pl.xuid];
                                saveUnion(union.guid, union);
                                let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
                                playerData.unionGuid = null;
                                CONF.PLAYERDATA.set(pl.xuid, playerData);

                                sendPlayerNotice(pl.xuid, {title: tr('gui.member.exit.kick.title'), msg: tr('gui.member.exit.success', union.name)});
                                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.member.exit.kick.title'), msg: tr('gui.member.exit.broadcast', pl.realName, union.name)});
                                addUnionLog(union.guid, tr('gui.member.exit.broadcast', pl.realName, union.name));
                                mc.broadcast(tr('gui.member.exit.broadcast', pl.realName, union.name));
                                return;
                            }
                        });
                        return;
                        //fm_playerInfo.addButton(`§c§l退出联盟`);
                    }
                }
            }

            // 设为管理员
            if (id == 2){
                if (pl.isOP() || union.playerList[pl.xuid]?.level >= 2){
                    if (union.playerList[playerInfo.xuid]?.level == 0){
                        pl.sendModalForm(tr('gui.member.manager.title'), tr('gui.member.manager.confirm', playerInfo.realName), tr('.confirm'), tr('.cancel'), (pl, res)=>{
                            if (res == null || res == false){return pl.sendForm(fm_playerInfo, playerInfo_run);}
                            if (res == true){
                                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                                if (union == null){return;}
                                union.playerList[playerInfo.xuid].level = 1;
                                saveUnion(union.guid, union);
                                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.member.manager.title'), msg: tr('gui.member.manager.success', playerInfo.realName, pl.realName)});
                                //sendPlayerNotice(playerInfo.xuid, {title: '§d联盟管理员', msg: `§a你已成为§d联盟管理员`});
                                addUnionLog(union.guid, tr('gui.member.manager.success', playerInfo.realName, pl.realName));
                                return;
                            }
                        });
                        //fm_playerInfo.addButton(`§c§l【管理】设为管理员`);
                    }else if (union.playerList[playerInfo.xuid]?.level == 1){
                        pl.sendModalForm(tr('gui.member.manager.title'), tr('gui.member.manager.dismissal.confirm', playerInfo.realName), tr('.confirm'), tr('.cancel'), (pl, res)=>{
                            if (res == null || res == false){return pl.sendForm(fm_playerInfo, playerInfo_run);}
                            if (res == true){
                                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                                if (union == null){return;}
                                union.playerList[playerInfo.xuid].level = 0;
                                saveUnion(union.guid, union);
                                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.member.manager.title'), msg: tr('gui.member.manager.dismissal.success', playerInfo.realName, pl.realName)});
                                //sendPlayerNotice(playerInfo.xuid, {title: '§d联盟管理员', msg: `§7你被撤除§d联盟管理员`});
                                addUnionLog(union.guid, tr('gui.member.manager.dismissal.success', playerInfo.realName, pl.realName));
                                return;
                            }
                        });
                        //fm_playerInfo.addButton(`§c§l【管理】撤除管理员`);
                    }
                }
            }
            
        }
    }
}

// 联盟传送锚点
function unionTpPos(pl, unionGuid, lastForm=()=>{}){
    if (!pl.isOP() && !CONF.CONFIG.get('TpPosEnabled')){
        return menu_end(pl, tr('gui.tpPos.title'), tr('gui.tpPos.unenabled'), lastForm);
    }
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (union == null){return;}
    if (!pl.isOP() && union.playerList[pl.xuid] == null){
        return menu_end(pl, tr('gui.tpPos.title'), tr('gui.tpPos.noAccess'), lastForm);
    }
    
    let fm_tpPos = mc.newSimpleForm()
    .setTitle(tr('gui.tpPos.title'))
    .setContent(``);

    for (var i = 0; i < union.tpPos.length; i++){
		fm_tpPos.addButton(`${tr('gui.tpPos.tpTo')} §r<${union.tpPos[i][0]}§r>\n§r${union.tpPos[i][1]}, ${union.tpPos[i][2]}, ${union.tpPos[i][3]}, ${DIM_TEXT[union.tpPos[i][4]]}`, 'textures/items/ender_pearl');
    }
    if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
        fm_tpPos.addButton(tr('gui.tpPos.set'), 'textures/ui/icon_setting');
    }

    pl.sendForm(fm_tpPos, tpPos_run);

    function tpPos_run(pl, id){
        if (id == null){return lastForm();}
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        let fm_setPos = mc.newCustomForm()
        .setTitle(tr('gui.tpPos.set'))
        .addLabel(`${tr('gui.tpPos.set.tip.1')}\n§r${(CONF.CONFIG.get('TpPosPrice')==null || CONF.CONFIG.get('TpPosPrice')==0)?'\n':`${tr('gui.tpPos.set.tip.2', CONF.CONFIG.get('TpPosPrice'))}`}${tr('gui.tpPos.set.tip.3', CONF.CONFIG.get('TpPosNum'))}`)
        .addInput(tr('gui.tpPos.create.name'))
        .addInput(tr('gui.tpPos.delete.input'));
        if (id < union.tpPos.length){
            pl.teleport(union.tpPos[id][1], union.tpPos[id][2], union.tpPos[id][3], union.tpPos[id][4]);
            mc.runcmdEx(`execute as "${pl.realName}" at "${pl.realName}" run title @s title ${union.tpPos[id][0]}`);
            return;
        }
        if (id == union.tpPos.length){
            return pl.sendForm(fm_setPos, setPos_run);
        }
        
        function setPos_run(pl, args){
            if (args == null){return pl.sendForm(fm_tpPos, tpPos_run);}
            let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
            if (union == null){return;}
            let newName = args[1];
            let delAnchor = args[2];
            if (newName != ''){
                if (union.tpPos.length >= CONF.CONFIG.get('TpPosNum')){
                    return menu_end(pl, tr('gui.tpPos.create.title'), tr('gui.tpPos.create.error.maxPos', CONF.CONFIG.get('TpPosNum')), fm_tpPos, tpPos_run);
                }
                union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (CONF.CONFIG.get('TpPosPrice')!=null && CONF.CONFIG.get('TpPosPrice') >0){
                    if (union.money < CONF.CONFIG.get('TpPosPrice')){
                        return menu_end(pl, tr('gui.tpPos.create.title'), `${tr('gui.tpPos.create.error.notEnoughMoney')} (${union.money} < ${CONF.CONFIG.get('TpPosPrice')})`, fm_tpPos, tpPos_run);
                    }else{
                        union.money -= CONF.CONFIG.get('TpPosPrice');
                        addBankBill(union.guid, -CONF.CONFIG.get('TpPosPrice'), pl.xuid, pl.realName, tr('gui.tpPos.create.title'));
                    }
                }
                union.tpPos.push([newName, pl.blockPos.x, pl.blockPos.y, pl.blockPos.z, pl.blockPos.dimid, pl.realName]);
                saveUnion(union.guid, union);
                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.tpPos.notice.title'), msg: tr('gui.tpPos.create.success.log', pl.realName, newName)});
                addUnionLog(union.guid, tr('gui.tpPos.create.success.log', pl.realName, newName));
                pl.tell(tr('gui.tpPos.create.success.tip',newName));
                return;
            }

            if (delAnchor != ''){
                if (!(/(^[1-9]\d*$)/.test(delAnchor))){
                    return menu_end(pl, tr('gui.tpPos.delete.title'), tr('gui.error.positiveInteger'), fm_tpPos, tpPos_run);
                }
                if (Number(delAnchor) <= union.tpPos.length){
                    let index = Number(delAnchor)-1;
                    sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.tpPos.notice.title'), msg: tr('gui.tpPos.delete.success.log', pl.realName, union.tpPos[index][0])});
                    addUnionLog(union.guid, tr('gui.tpPos.delete.success.log', pl.realName, union.tpPos[index][0]));
                    pl.tell(tr('gui.tpPos.delete.success.tip', union.tpPos[index][0]));
                    union.tpPos.splice(index, 1);
                    saveUnion(union.guid, union);
                    return;
                }else{
                    return menu_end(pl, tr('gui.tpPos.notice.title'), tr('gui.error.rightIndex'), fm_tpPos, tpPos_run);
                }
                
            }

            return pl.sendForm(fm_tpPos, tpPos_run);
        }
    }
}

// 联盟贡献
function unionContribute(pl, unionGuid, lastForm=()=>{}){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (!pl.isOP() && union.playerList[pl.xuid] == null){
        return menu_end(pl, tr('gui.contribute.title'), tr('gui.tpPos.noAccess'), lastForm);
    }

    let fm_contribute = mc.newSimpleForm()
    .setTitle(tr('gui.contribute.title'))
    .setContent(`\n${unionLevelText(union.score.blockPlace)}§r ${union.name} \n\n ${tr('gui.contribute.construct')}:§r ${(union.score.blockPlace/1000).toFixed(1)}\n\n ${tr('gui.contribute.bank.1')}:§r ${union.money}\n `)
    .addButton(tr('gui.contribute.contribute.construct'))
    .addButton(tr('gui.contribute.bank'));
    

    pl.sendForm(fm_contribute, contribute_run);

    function contribute_run(pl, id){
        if (id == null){return lastForm();}
        if (id == 0){
            return unionConstruct(pl, unionGuid, ()=>{unionContribute(pl, unionGuid, lastForm);});
        }
        if (id == 1){
            return unionBank(pl, unionGuid, ()=>{unionContribute(pl, unionGuid, lastForm);});
        }
    }

    function unionBank(pl, unionGuid, lastForm=()=>{}){
        if (!CONF.CONFIG.get('BankEnabled')){
            return menu_end(pl, tr('gui.contribute.bank'), tr('gui.bank.unenabled'), lastForm);
        }
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        let fm_bank = mc.newSimpleForm()
        .setTitle(tr('gui.contribute.bank'))
        .setContent(tr('gui.bank.tip', union.money, CONF.CONFIG.get('EconomyName')))
        .addButton(tr('gui.bank.deposit.button'))
        .addButton(tr('gui.bank.withdrawal.button'))
        .addButton(tr('gui.bank.bill.button'));
    
        pl.sendForm(fm_bank, bank_run);
    
        function bank_run(pl, id){
            if (id == null){return lastForm();}
            let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
            if (union == null){return;}
            let fm_unionDeposit = mc.newCustomForm()
            .setTitle(tr('gui.bank.deposith.title'))
            .addLabel(tr('gui.bank.deposith.wallet', getMoney(pl.xuid)??0))
            .addInput(tr('gui.bank.deposith.input'), tr('gui.input.positiveInteger'))
            .addInput(tr('gui.bank.deposith.note'));

            let fm_unionWithdrawal = mc.newCustomForm()
            .setTitle(tr('gui.bank.withdrawal.title'))
            .addLabel(tr('gui.bank.wealth.wallet', union.money))
            .addInput(tr('gui.bank.withdrawal.input'), tr('gui.input.positiveInteger'))
            .addInput(tr('gui.bank.withdrawal.reason'));

            function unionDeposit_run(pl, args){
                if (args == null){return lastForm();}
                if (args[1].trim() == ''){return lastForm();}
                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (union == null){return;}
                if (!(/(^[1-9]\d*$)/.test( args[1] ))){
                    return menu_end(pl, tr('gui.bank.deposith.title'), tr('gui.error.positiveInteger'), fm_unionDeposit, unionDeposit_run);
                }
                let value = Number(args[1]);
                let note = args[2];
                let balance = getMoney(pl.xuid);
                if (balance == null){
                    return menu_end(pl, tr('gui.bank.deposith.title'), tr('gui.bank.deposith.error.notEnoughMoney', '0'), fm_unionDeposit, unionDeposit_run);
                }
                if (balance < value){
                    return menu_end(pl, tr('gui.bank.deposith.title'), tr('gui.bank.deposith.error.notEnoughMoney', balance), fm_unionDeposit, unionDeposit_run);
                }

                let ecoRes = reduceMoney(pl.xuid, value, tr('gui.bank.deposith.billnote', union.name));
                if (ecoRes == false){
                    return menu_end(pl, tr('gui.bank.deposith.title'), tr('gui.bank.deposith.error'), fm_unionDeposit, unionDeposit_run);
                }

                addUnionMoney(union.guid, value, pl.xuid, pl.realName, note);
                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.contribute.bank'), msg: tr('gui.bank.deposith.success', pl.realName, value, CONF.CONFIG.get('EconomyName'), note)});
                logger.info(tr('gui.bank.deposith.success.log', union.name, pl.realName, value, CONF.CONFIG.get('EconomyName'), note));
                unionSort(UnionsData);
                return unionBank(pl, unionGuid, lastForm);
            }

            function unionWithdrawal_run(pl, args){
                if (args == null){return lastForm();}
                if (args[1].trim() == ''){return lastForm();}
                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (union == null){return;}
                if (!(/(^[1-9]\d*$)/.test( args[1] ))){
                    return menu_end(pl, tr('gui.bank.withdrawal.title'), tr('gui.error.positiveInteger'), fm_unionWithdrawal, unionWithdrawal_run);
                }
                let value = Number(args[1]);
                let note = args[2];
                if (union.money < value){
                    return menu_end(pl, tr('gui.bank.withdrawal.title'), tr('gui.bank.withdrawal.error.notEnoughMoney'), fm_unionWithdrawal, unionWithdrawal_run);
                }
                if (note.trim() == ''){
                    return menu_end(pl, tr('gui.bank.withdrawal.title'), tr('gui.bank.withdrawal.error.notNote'), fm_unionWithdrawal, unionWithdrawal_run);
                }

                let ecoRes = addMoney(pl.xuid, value, tr('gui.bank.withdrawal.billnote', union.name));
                if (ecoRes == false){
                    return menu_end(pl, tr('gui.bank.withdrawal.title'), tr('gui.bank.withdrawal.error.addmoney'), fm_unionDeposit, unionDeposit_run);
                }

                addUnionMoney(union.guid, -value, pl.xuid, pl.realName, note);
                sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.contribute.bank'), msg: tr('gui.bank.withdrawal.success', pl.realName, value, CONF.CONFIG.get('EconomyName'), note)});
                logger.info(tr('gui.bank.withdrawal.success.log', union.name, pl.realName, value, CONF.CONFIG.get('EconomyName'), note));
                unionSort(UnionsData);
                return unionBank(pl, unionGuid, lastForm);
            }

            if (id == 0){
                return pl.sendForm(fm_unionDeposit, unionDeposit_run);
            }
            if (id == 1){
                return pl.sendForm(fm_unionWithdrawal, unionWithdrawal_run);
            }
            if (id == 2){
                return billDisplay(pl, 'BANK', union.guid, ()=>{return unionBank(pl, unionGuid, lastForm);});
            }
        }
    }

    function unionConstruct(pl, unionGuid, lastForm=()=>{}){
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
        let fm_construct = mc.newCustomForm()
        .setTitle(tr('gui.construct.title'))
        .addLabel(tr('gui.construct.personal', toThousands(playerData.score.blockPlace)))
        .addInput(tr('gui.construct.input', union.name), tr('gui.input.positiveInteger'))
        .addInput(tr('gui.construct.note'))
        .addSwitch(tr('gui.construct.bill'));

        pl.sendForm(fm_construct, construct_run);

        function construct_run(pl, args){
            if (args == null){return lastForm();}
            let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
            if (union == null){return;}
            if (args[1].trim() == '' && args[3] == false){return lastForm();}
            if (args[1].trim() == '' && args[3] == true){return billDisplay(pl, 'CONSTRUCT', union.guid, ()=>{return unionConstruct(pl, unionGuid, lastForm);});}
            if (!(/(^[1-9]\d*$)/.test( args[1] ))){
                return menu_end(pl, tr('gui.construct.title'), tr('gui.error.positiveInteger'), fm_construct, construct_run);
            }
            let value = Number(args[1]);
            if (value < 1000){
                return menu_end(pl, tr('gui.construct.title'), tr('gui.construct.error.tooLittle'), fm_construct, construct_run);
            }
            if (value > playerData.score.blockPlace){
                return menu_end(pl, tr('gui.construct.title'), tr('gui.construct.error.notEnough', playerData.score.blockPlace, value), fm_construct, construct_run);
            }
            let note = args[2];

            playerData.score.blockPlace -= value;
            CONF.PLAYERDATA.set(pl.xuid, playerData);
            let memberData = loadMemberData(union.playerList[pl.xuid]);
            memberData.contribute += value;
            union.playerList[pl.xuid] = memberData;
            saveUnion(union.guid, union);
            addUnionConstruct(union.guid, value, pl.xuid, pl.realName, note); // 增加加联盟建设度
            sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.construct.title'), msg: tr('gui.construct.success', value>0?`§e[+${value}]`:`§b[${value}]`, pl.realName, value, note)});
            logger.info(tr('gui.construct.success.log', union.name, value>0?`§e[+${value}]`:`§b[${value}]`, pl.realName, value, note));
            return unionContribute(pl, unionGuid, lastForm);
        }
    }
}

// 申请加入
function unionJoin(pl, unionGuid, lastForm=()=>{}){
    if (loadPlData(CONF.PLAYERDATA.get(pl.xuid)).unionGuid != null){
        return menu_end(pl, tr('gui.application.title'), tr('gui.application.error.onlyOne'), lastForm);
    }
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (!union.recruitment.enabled){
        return menu_end(pl, tr('gui.application.title'), tr('gui.application.error.refuse', union.name), lastForm);
    }
    
    let fm_unionJoin = mc.newCustomForm()
    .setTitle(tr('gui.application.title'))
    .addLabel(tr('gui.application.tip', union.name, union.recruitment.bulletin))
    .addInput(tr('gui.application.reason'));

    pl.sendForm(fm_unionJoin, unionJoin_run);

    function unionJoin_run(pl, args){
        if (args == null || args[1].trim() == ''){return lastForm();}
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        if (union.recruitment.enabled == false){
            return menu_end(pl, tr('gui.application.title'), tr('gui.application.error.refuse', union.name), lastForm);
        }
        let repeat = false;
        for (let i = 0; i < union.recruitment.application.length; i++){
            if (union.recruitment.application[i].xuid == pl.xuid){
                union.recruitment.application[i] = {
                    xuid: pl.xuid, 
                    realName: pl.realName, 
                    content: args[1], 
                    time: system.getTimeStr()
                };
                repeat = true;
            }
        }
        if (!repeat){
            union.recruitment.application.push({
                xuid: pl.xuid, 
                realName: pl.realName, 
                content: args[1], 
                time: system.getTimeStr()
            });
        }
        saveUnion(union.guid, union);
        sendPlayerNotice(pl.xuid, {title: tr('gui.application.title'), msg: tr('gui.application.send.success', pl.realName, union.name)});
        sendUnionNotice(union.guid, [1,2], {title: tr('gui.application.title'), msg: tr('gui.application.send.success', pl.realName, union.name)});
        addUnionLog(union.guid, tr('gui.application.send.success', pl.realName, union.name));
        return;
    }
}

// 联盟管理
function unionManager(pl, unionGuid, lastForm=()=>{}){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    if (!(pl.isOP() || union.playerList[pl.xuid]?.level >= 1)){
        return menu_end(pl, tr('gui.manager.title'), tr('gui.manager.error.noAccess'), lastForm);
    }
    let fm_manager = mc.newSimpleForm()
    .setTitle(tr('gui.manager.title'))
    .setContent(``);
    
    fm_manager.addButton(tr('gui.manager.setInfo.button'), 'textures/ui/icon_setting');
    fm_manager.addButton(tr('gui.manager.application.button'), 'textures/ui/book_addtextpage_default');
    if (pl.isOP() || union.playerList[pl.xuid]?.level >= 1){
        fm_manager.addButton(tr('gui.manager.other.button'), 'textures/ui/icon_setting');
    }
    if (pl.isOP() || union.playerList[pl.xuid]?.level == 2){
        fm_manager.addButton(tr('gui.manager.transference.button'), 'textures/ui/FriendsDiversity');
        fm_manager.addButton(tr('gui.manager.dissolution.button'), 'textures/ui/cancel');
    }
    
    pl.sendForm(fm_manager, manager_run);

    function manager_run(pl, id){
        if (id == null){return lastForm();}
        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
        if (union == null){return;}
        if (id == 0){
            let fm_setUnionName = mc.newCustomForm()
            .setTitle(tr('gui.manager.setInfo.button'))
            .addLabel(``)
            .addInput(tr('gui.manager.setInfo.name.input'), '', union.name)
            .addInput(tr('gui.manager.setInfo.discription.input'), '', union.discription)
            .addInput(tr('gui.manager.setInfo.medal.input'), '', union.medal)
            .addInput(tr('gui.manager.setInfo.icon.input'), '', union.icon);
            pl.sendForm(fm_setUnionName, (pl, args)=>{
                if (args == null){return pl.sendForm(fm_manager, manager_run);}
                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (union == null){return;}
                args[2] = args[2].replaceAll('\\n','\n');
                if (args[1].trim() == ''){
                    return menu_end(pl, tr('gui.manager.setInfo.button'), tr('gui.manager.setInfo.name.error.notEmpty'), fm_manager, manager_run);
                }
                if (args[1] == union.name && args[2] == union.discription && args[3] == union.medal && args[4] == union.icon){
                    return pl.sendForm(fm_manager, manager_run);
                }
                if (args[4].trim() == ''){
                    args[4] = 'textures/ui/mashup_world';
                }
                if (args[4].slice(0,9) != "textures/"){
                    args[4] = "textures/" + args[4]; 
                }
                if (args[1].length > (CONF.CONFIG.get('UnionNameLimit')??Infinity)){
                    return menu_end(pl, tr('gui.manager.setInfo.button'), tr('gui.manager.setInfo.name.error.tooLong', args[1].length, CONF.CONFIG.get('UnionNameLimit')), fm_manager, manager_run);
                }
                if (args[2].length > (CONF.CONFIG.get('UnionDiscriptionLimit')??Infinity)){
                    return menu_end(pl, tr('gui.manager.setInfo.button'), tr('gui.manager.setInfo.discription.error.tooLong', args[2].length, CONF.CONFIG.get('UnionDiscriptionLimit')), fm_manager, manager_run);
                }
                union.name = args[1];
                union.discription = args[2];
                union.medal = args[3];
                union.icon = args[4];
                saveUnion(union.guid, union);
                addUnionLog(union.guid, tr('gui.manager.setInfo.success.log', pl.realName, union.name, union.discription, union.medal, union.icon));
                sendUnionNotice(union.guid, [1,2], {title: tr('gui.manager.setInfo.button'), msg: tr('gui.manager.setInfo.success', pl.realName)});
                pl.tell(tr('gui.manager.setInfo.success.tip'));
                return unionInfo(pl, unionGuid);
            });
        }

        if (id == 1){
            let fm_unionJoin = mc.newSimpleForm()
            .setTitle(tr('gui.manager.recruitment.title'))
            .setContent(`\n${union.recruitment.bulletin}\n `)
            .addButton(union.recruitment.enabled ? tr('gui.manager.recruitment.acceptAll') : tr('gui.manager.recruitment.refuseAll'),union.recruitment.enabled ? 'textures/ui/toggle_on' : 'textures/ui/toggle_off' )
            .addButton(tr('gui.manager.recruitment.setNotice.button'), 'textures/ui/comment');
            for (let application of union.recruitment.application){
                fm_unionJoin.addButton(`§小§2${application.realName}\n§r${application.time}`, 'textures/ui/book_addtextpage_default');
            }

            pl.sendForm(fm_unionJoin, (pl, id)=>{
                if (id == null){return pl.sendForm(fm_manager, manager_run);}
                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (union == null){return;}
                if (id == 0){
                    if (union.recruitment.enabled){
                        union.recruitment.enabled = false;
                        saveUnion(union.guid, union);
                        addUnionLog(union.guid, tr('gui.manager.recruitment.change.refuseAll', pl.realName));
                        sendUnionNotice(union.guid, [1,2], {title: tr('gui.manager.recruitment.title'), msg: tr('gui.manager.recruitment.change.refuseAll', pl.realName)});
                        return pl.sendForm(fm_manager, manager_run);
                    }else{
                        union.recruitment.enabled = true;
                        saveUnion(union.guid, union);
                        addUnionLog(union.guid, tr('gui.manager.recruitment.change.acceptAll', pl.realName));
                        sendUnionNotice(union.guid, [1,2], {title: tr('gui.manager.recruitment.title'), msg: tr('gui.manager.recruitment.change.acceptAll', pl.realName)});
                        return pl.sendForm(fm_manager, manager_run);
                    }
                }
                if (id == 1){
                    let fm_setRecruitmentBulletin = mc.newCustomForm()
                    .setTitle(tr('gui.manager.recruitment.setNotice.title'))
                    .addLabel(`\n${union.recruitment.bulletin}\n `)
                    .addInput(tr('gui.manager.recruitment.setNotice.tip'),'',union.recruitment.bulletin);

                    pl.sendForm(fm_setRecruitmentBulletin, (pl, args)=>{
                        if (args == null){return pl.sendForm(fm_manager, manager_run);}
                        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                        if (union == null){return;}
                        args[1] = args[1].replaceAll('\\n','\n');
                        if (union.recruitment.bulletin == args[1]){return pl.sendForm(fm_manager, manager_run);}
                        if (args[1].length > (CONF.CONFIG.get('UnionDiscriptionLimit')??Infinity)){
                            return menu_end(pl, tr('gui.manager.recruitment.setNotice.title'), tr('gui.manager.recruitment.setNotice.error.tooLong', args[1].length, CONF.CONFIG.get('UnionDiscriptionLimit')), fm_manager, manager_run);
                        }
                        union.recruitment.bulletin = args[1];
                        saveUnion(union.guid, union);
                        addUnionLog(union.guid, tr('gui.manager.recruitment.setNotice.success', pl.realName, args[1]));
                        sendUnionNotice(union.guid, [1,2], {title: tr('gui.manager.recruitment.title'), msg: tr('gui.manager.recruitment.setNotice.success', pl.realName, args[1])});
                        pl.tell(tr('gui.manager.recruitment.setNotice.success.tip'));
                        return pl.sendForm(fm_manager, manager_run);
                    });
                }
                if (id > 1){
                    let application = JSON.parse(JSON.stringify(union.recruitment.application[id-2]));
                    let application_index = id - 2;
                    let fm_application = mc.newSimpleForm()
                    .setTitle(`§l${application.realName}`)
                    .setContent(tr('gui.manager.recruitment.application.content', application.realName, application.time, application.content))
                    .addButton(tr('gui.manager.recruitment.application.accept'), 'textures/ui/confirm')
                    .addButton(tr('gui.manager.recruitment.application.refuse'), 'textures/ui/cancel');
                    pl.sendForm(fm_application, (pl, id)=>{
                        if (id == null){return pl.sendForm(fm_manager, manager_run);}
                        let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                        if (union == null){return;}
                        let playerData = loadPlData(CONF.PLAYERDATA.get(application.xuid));
                        if (playerData.unionGuid != null){
                            union.recruitment.application.splice(application_index, 1);
                            saveUnion(union.guid, union);
                            return menu_end(pl, tr('gui.manager.recruitment.application.name', application.realName), tr('gui.manager.recruitment.application.error'), fm_manager, manager_run);
                        }

                        if (id == 0){
                            union.playerList[application.xuid] = {
                                xuid: application.xuid,
                                realName: application.realName,
                                level: 0,
                                contribute: 0,
                                lastLogTime: application.time,
                                joinTime: system.getTimeStr()
                            };
                            playerData.unionGuid = union.guid;
                            CONF.PLAYERDATA.set(application.xuid, playerData);
                            union.recruitment.application.splice(application_index, 1);
                            saveUnion(union.guid, union);
                            sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.manager.recruitment.title'), msg: tr('gui.manager.recruitment.application.success', application.realName, union.name, pl.realName)});
                            addUnionLog(union.guid, tr('gui.manager.recruitment.application.success', application.realName, union.name, pl.realName));
                            mc.broadcast(tr('gui.manager.recruitment.application.broascast', application.realName, union.name));
                            pl.tell(tr('gui.manager.recruitment.application.success.tip', application.realName, union.name));
                            return pl.sendForm(fm_manager, manager_run);
                        }

                        if (id == 1){
                            union.recruitment.application.splice(application_index, 1);
                            saveUnion(union.guid, union);
                            sendPlayerNotice(application.xuid, {title: tr('gui.manager.recruitment.title'), msg: tr('gui.manager.recruitment.application.faild', union.name, pl.realName)});
                            sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.manager.recruitment.title'), msg: tr('gui.manager.recruitment.application.faild.1', application.realName, pl.realName)});
                            addUnionLog(union.guid, tr('gui.manager.recruitment.application.faild.1', application.realName, pl.realName));
                            pl.tell(tr('gui.manager.recruitment.application.faild.tip', application.realName, union.name));
                            return pl.sendForm(fm_manager, manager_run);
                        }
                    });
                }
            });
        }

        if (id == 2){
            let fm_other = mc.newCustomForm()
            .setTitle(tr('gui.manager.other.title'))
            .addLabel('')
            .addSwitch(tr('gui.manager.other.friendlyFire.switch'), union.friendlyFire);
            pl.sendForm(fm_other, (pl, args)=>{
                if (args == null){return pl.sendForm(fm_manager, manager_run);}
                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (union == null){return;}
                if (args[1] != union.friendlyFire){
                    union.friendlyFire = args[1];
                    saveUnion(union.guid, union);
                    if (union.friendlyFire){
                        sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.manager.other.friendlyFire.title'), msg: tr('gui.manager.other.friendlyFire.open', pl.realName)});
                        addUnionLog(union.guid, tr('gui.manager.other.friendlyFire.open', pl.realName));
                        pl.tell(tr('gui.manager.other.friendlyFire.open.tip'));
                    }else{
                        sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.manager.other.friendlyFire.title'), msg: tr('gui.manager.other.friendlyFire.close', pl.realName)});
                        addUnionLog(union.guid, tr('gui.manager.other.friendlyFire.close', pl.realName));
                        pl.tell(tr('gui.manager.other.friendlyFire.close.tip'));
                    }
                }
                return pl.sendForm(fm_manager, manager_run);
            });
        }

        if (id == 3){
            let fm_transference = mc.newSimpleForm()
            .setTitle(tr('gui.manager.transference.title'))
            .setContent(tr('gui.manager.transference.content'));
            for (let xuid in union.playerList){
                fm_transference.addButton(`§小${union.playerList[xuid].realName}`);
            }

            pl.sendForm(fm_transference, (pl, id)=>{
                if (id == null){return pl.sendForm(fm_manager, manager_run);}
                let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                if (union == null){return;}
                let playerInfo = union.playerList[Object.keys(union.playerList)[id]];
                if (playerInfo.xuid == union.lord.xuid){
                    return menu_end(pl, tr('gui.manager.transference.title'), tr('gui.manager.transference.error.lord', playerInfo.realName), fm_manager, manager_run);
                }
                if (CONF.CONFIG.get('CreateAuthority')){
                    let playerData = loadPlData(CONF.PLAYERDATA.get(playerInfo.xuid));
                    if (playerData.unionLord != true){
                        return menu_end(pl, tr('gui.manager.transference.title'), tr('gui.manager.transference.error.notlord', playerInfo.realName), fm_manager, manager_run);
                    }
                }
                pl.sendModalForm(tr('gui.manager.transference.title'), tr('gui.manager.transference.confirm', playerInfo.realName), '§c'+tr('.confirm'), tr('.cancel'), (pl, res)=>{
                    if (res == null || res == false){
                        return pl.sendForm(fm_manager, manager_run);
                    }
                    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                    if (union == null){return;}
                    union.lord = {xuid: playerInfo.xuid, realName: playerInfo.realName};
                    for (let xuid in union.playerList){
                        if (union.playerList[xuid]?.level == 2){
                            union.playerList[xuid].level = 1;
                        }
                    }
                    union.playerList[playerInfo.xuid].level = 2;
                    saveUnion(union.guid, union);
                    sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.manager.transference.title'), msg: tr('gui.manager.transference.success', pl.realName, playerInfo.realName)});
                    addUnionLog(union.guid, tr('gui.manager.transference.success', pl.realName, playerInfo.realName));
                    pl.tell(tr('gui.manager.transference.success.tip'));
                });
            });
        }

        if (id == 4){
            if (union.money != 0){
                return menu_end(pl, tr('gui.manager.dissolution.title'), tr('gui.manager.dissolution.error.notEmptyMoney'), fm_manager, manager_run);
            }
            if (!pl.isOP() && union.playerList[pl.xuid]?.level != 2){
                return menu_end(pl, tr('gui.manager.dissolution.title'), tr('gui.manager.dissolution.error.noAccess'), fm_manager, manager_run);
            }
            pl.sendModalForm(tr('gui.manager.dissolution.title'), tr('gui.manager.dissolution.confirm', union.name), '§c'+tr('.confirm'), tr('.cancel'), (pl,res)=>{
                if (res == false || res == null){return pl.sendForm(fm_manager, manager_run);}
                if (res == true){
                    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
                    if (union == null){return;}
                    sendUnionNotice(union.guid, [0,1,2], {title: tr('gui.manager.dissolution.title'), msg: tr('gui.manager.dissolution.success', union.name, pl.realName)});
                    addUnionLog(union.guid, tr('gui.manager.dissolution.success', union.name, pl.realName));
                    for (let xuid in union.playerList){
                        let playerData = loadPlData(CONF.PLAYERDATA.get(xuid));
                        playerData.unionGuid = null;
                        CONF.PLAYERDATA.set(xuid, playerData);
                    }
                    CONF.LOG.delete(union.guid);
                    CONF.BANKBILL.delete(union.guid);
                    CONF.CONSTUCTBILL.delete(union.guid);
                    CONF.UNIONSDATA.delete(union.guid);
                    delete UnionsData[union.guid];
                    unionSort(UnionsData);
                    pl.tell(tr('gui.manager.dissolution.success.tip', union.name));
                    mc.broadcast(tr('gui.manager.dissolution.success.broadcast', union.name, pl.realName, union.guid));
                    logger.info(tr('gui.manager.dissolution.success.broadcast', union.name, pl.realName, union.guid));
                }
            });
        }
    }

}

//============================================

// 即时通知系统, 相同title的会被合并 {title: '', msg: ''}
function sendPlayerNotice(xuid, notice){
    let pl = mc.getPlayer(xuid);
    if (pl != null){
        pl.tell(`§r[${notice.title}§r] ${notice.msg}`);
        pl.sendToast(notice.title, notice.msg);
        return;
    }
    
    let playerData = loadPlData(CONF.PLAYERDATA.get(xuid));
    if (notice.title in playerData.notice){
        playerData.notice[notice.title].num ++;
        playerData.notice[notice.title].msg = notice.msg;
        playerData.notice[notice.title].time = system.getTimeStr();
    }else{
        playerData.notice[notice.title] = {
            title: notice.title,
            msg: notice.msg,
            time: system.getTimeStr(),
            num: 1,
        };
    }
    CONF.PLAYERDATA.set(xuid, playerData);
}

// 即时通知（联盟群发）
function sendUnionNotice(unionGuid, levelList, notice){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    for (let xuid in union.playerList){
        if (levelList.includes(union.playerList[xuid]?.level)){
            sendPlayerNotice(xuid, notice);
        }
    }
}

// 联盟通知，操作日志
function addUnionLog(unionGuid, text){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    let unionLog = CONF.LOG.init(unionGuid, {});
    if (unionLog[nowDayStr()] == null){
        unionLog[nowDayStr()] = [];
    }
    unionLog[nowDayStr()].push({time: system.getTimeStr(), text: text});
    if (Object.keys(unionLog).length > CONF.CONFIG.get('BillSavedDays')){
        let minDate = Object.keys(unionLog)[0];
        for (let dateStr in unionLog){
            if (parseDayStr(dateStr) < parseDayStr(minDate)){
                minDate = dateStr;
            }
        }
        delete unionLog[minDate];
    }
    CONF.LOG.set(unionGuid, unionLog);
    logger.info(`[${union.name}] ${text}`);
}

// 发送联盟内聊天消息
function sendUnionMsg(unionGuid, levelList, msg){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    for (let xuid in union.playerList){
        if (levelList.includes(union.playerList[xuid]?.level)){
            let pl = mc.getPlayer(xuid);
            if (pl != null){
                pl.tell(msg);
            }
        }
    }
}

// 增加联盟财富
function addUnionMoney(guid, value, xuid = '0', realName = '', note = ''){
    let union = loadUnion(CONF.UNIONSDATA.get(guid));
    if (union == null){return false;}
    if (value<0){
        if (union.money < (-value)){return false;}
    }
    union.money += value;
    saveUnion(union.guid, union);
    addBankBill(union.guid, value, xuid, realName, note);
}

// 增加个人建设度
function addPlayerConstruct(xuid, value){
    let playerData = loadPlData(CONF.PLAYERDATA.get(xuid));
    if (playerData == null){return false;}
    if (value<0){
        if (playerData.score.blockPlace < (-value)){return false;}
    }
    playerData.score.blockPlace += value;
    CONF.PLAYERDATA.set(xuid, playerData);
    return true;
}

// 增加联盟建设度（联盟等级）
function addUnionConstruct(guid, value, xuid = '0', realName = '', note = ''){
    let union = loadUnion(CONF.UNIONSDATA.get(guid));
    if (union == null){return false;}
    if (value<0){
        if (union.score.blockPlace < (-value)){return false;}
    }
    union.score.blockPlace += value;
    saveUnion(union.guid, union);
    addConstructBill(union.guid, value, xuid, realName, note);
    unionSort(UnionsData);
    return true;
}

// 添加银行记录
function addBankBill(unionGuid, value, xuid, realName, note=''){
    //let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    let unionLog = CONF.BANKBILL.init(unionGuid, {});
    if (unionLog[nowDayStr()] == null){
        unionLog[nowDayStr()] = [];
    }
    unionLog[nowDayStr()].push({time: system.getTimeStr(), value: value, xuid:xuid, realName: realName, note: note});
    if (Object.keys(unionLog).length > CONF.CONFIG.get('BillSavedDays')){
        let minDate = Object.keys(unionLog)[0];
        for (let dateStr in unionLog){
            if (parseDayStr(dateStr) < parseDayStr(minDate)){
                minDate = dateStr;
            }
        }
        delete unionLog[minDate];
    }
    CONF.BANKBILL.set(unionGuid, unionLog);
}

// 添加建设度记录
function addConstructBill(unionGuid, value, xuid, realName, note=''){
    let unionLog = CONF.CONSTUCTBILL.init(unionGuid, {});
    if (unionLog[nowDayStr()] == null){
        unionLog[nowDayStr()] = [];
    }
    unionLog[nowDayStr()].push({time: system.getTimeStr(), value: value, xuid:xuid, realName: realName, note: note});
    if (Object.keys(unionLog).length > CONF.CONFIG.get('BillSavedDays')){
        let minDate = Object.keys(unionLog)[0];
        for (let dateStr in unionLog){
            if (parseDayStr(dateStr) < parseDayStr(minDate)){
                minDate = dateStr;
            }
        }
        delete unionLog[minDate];
    }
    CONF.CONSTUCTBILL.set(unionGuid, unionLog);
}

// 账单记录展示表单
function billDisplay(pl, type, unionGuid, lastForm=()=>{}){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    let Config;
    let textFunc;
    let typeTextToday;
    let typeTextHistory;
    switch (type){
        case 'BANK':
            typeTextToday = tr('gui.bill.bank.today');
            typeTextHistory = tr('gui.bill.bank.history');
            Config = CONF.BANKBILL;
            textFunc = notice=>{
                if (notice.value > 0){
                    return tr('gui.bill.bank.notice.plus', notice.value, notice.realName, notice.note, notice.time);
                }else{
                    return tr('gui.bill.bank.notice.minus', notice.value, notice.realName, notice.note, notice.time);
                }
            };
            break;
        case 'CONSTRUCT':
            typeTextToday = tr('gui.bill.construct.today');
            typeTextHistory = tr('gui.bill.construct.history');
            Config = CONF.CONSTUCTBILL;
            textFunc = notice=>{
                if (notice.value > 0){
                    return tr('gui.bill.construct.notice.plus', notice.value, notice.realName, notice.note, notice.time);
                }else{
                    return tr('gui.bill.construct.notice.minus', notice.value, notice.realName, notice.note, notice.time);
                }
            };
            break;
        case 'UNIONNOTICE':
            typeTextToday = tr('gui.bill.notice.today');
            typeTextHistory = tr('gui.bill.notice.history');
            Config = CONF.LOG;
            textFunc = notice=>`${notice.text}\n§r§7时间: ${notice.time}`;
            break; 
    }

    let unionLog = Config.get(unionGuid);
    if (unionLog == null){
        Config.set(unionGuid, {});
        unionLog = Config.get(unionGuid);
    }
    let fm_todayNotice = mc.newSimpleForm();
    fm_todayNotice.setTitle(typeTextToday);
    let todayNotice = unionLog[nowDayStr()];
    let content = '';
    if (todayNotice != undefined){
        for (let i = 0; i< todayNotice.length; i++){
            content += ` §d#${i+1} |§r ${textFunc(todayNotice[i])}\n`;
        }
    }
    fm_todayNotice.setContent(content);

    fm_todayNotice.addButton(typeTextHistory);

    pl.sendForm(fm_todayNotice, todayNotice_run);

    function todayNotice_run(pl, id){
        if (id == null){return lastForm();}
        let fm_allNotice = mc.newCustomForm();
        fm_allNotice.setTitle(typeTextHistory);
        let content = '';
        let i = 0;
        for (let time in unionLog){
            content += ` §小§a#${i+1}| §r${time}\n`;
            i ++;
        }
        fm_allNotice.addLabel(content);
        fm_allNotice.addInput(tr('gui.bill.EnterSerialNumber'));
        
        pl.sendForm(fm_allNotice, allNotice_run);
        
        function allNotice_run(pl, args){
            if (args == null || args[1]==''){return pl.sendForm(fm_todayNotice, todayNotice_run);}
            if (!(/(^[1-9]\d*$)/.test( args[1] ))){
                return menu_end(pl, tr('gui.bill.EnterSerialNumber'), tr('gui.error.positiveInteger'), fm_allNotice, allNotice_run);
            }
            args[1] = Number(args[1]);
            if (args[1] > Object.keys(unionLog).length){
                return menu_end(pl, tr('gui.bill.EnterSerialNumber'), tr('gui.error.rightIndex'), fm_allNotice, allNotice_run);
            }
            let time = Object.keys(unionLog)[args[1]-1];
            let dayNotice = unionLog[time];
            let fm_dayNotice = mc.newSimpleForm()
            .setTitle(`§l${time}`);
            let content = '';
            for (let i = 0; i< dayNotice.length; i++){
                content += ` §d#${i+1} |§r ${textFunc(dayNotice[i])}\n`;
            }
            fm_dayNotice.setContent(content);
            pl.sendForm(fm_dayNotice, (pl,id)=>{
                return pl.sendForm(fm_allNotice, allNotice_run);
            });
        }
    }
}

// 载入联盟信息
function loadUnion(union){
    if (union == undefined){return;}
    let res = {
        guid: union.guid ?? null,
        name: union.name ?? '联盟',
        discription: union.discription ?? '',
        icon: union.icon ?? 'textures/ui/mashup_world',
        founder: union.founder ?? {xuid: '0', realName:"Steve"},  // 创建者
        lord: union.lord ?? {xuid: '0', realName:"Steve"}, // 现任盟主
        friendlyFire: union.friendlyFire ?? true, // 盟友伤害
        regTime: union.regTime ?? '2023-06-18 19:41:19', // 创建时间
        score: {
            blockPlace: union?.score?.blockPlace ?? 0, // 放置方块数量 获取联盟等级: unionLevelText(blockPlace)
        },
        tpPos: union.tpPos ?? [], // 传送锚点
        bulletin: union.bulletin ?? [], // 公告
        medal: (union.medal == undefined || union.medal.trim() == '') ? (union.name ?? '联盟') : union.medal, // 联盟勋章
        money:  union.money ?? 0, // 联盟存款
        recruitment: union.recruitment ?? {enabled: true, bulletin: "§a欢迎加入!", application: []}, // 联盟招新申请
        playerList: union.playerList ?? {} // 成员数据
    };
    if (res.icon.slice(0,9) != "textures/"){
        res.icon = "textures/" + res.icon; 
    }
    return res;
}

// 载入玩家数据
function loadPlData(playerData){
    if (playerData == undefined){return;}
    return {
        xuid: playerData.xuid ?? '0',
        realName: playerData.realName ?? 'Steve',
        unionGuid: playerData.unionGuid ?? null, // 所属联盟guid
        unionLord: playerData.unionLord ?? false, // 是否拥有联盟创建权限
        unionOrder: playerData.unionOrder ?? 0, // 联盟排序标准
        score: {
            blockPlace: playerData?.score?.blockPlace ?? 0 // 个人建设度（放置方块数量）
        },
        notice: playerData.notice ?? {} // 临时通知
    };
}

// 载入成员数据
function loadMemberData(memberData){
    if (memberData == null){return;}
    return {
        xuid: memberData.xuid ?? '0',
        realName: memberData.realName ?? 'Steve',
        level: memberData.level ?? 0,
        contribute: memberData.contribute ?? 0,
        lastLogTime: memberData.lastLogTime ?? system.getTimeStr(),
        joinTime: memberData.joinTime ?? system.getTimeStr()
    };
}

// 更新联盟信息
function saveUnion(guid, union){
    CONF.UNIONSDATA.set(guid, union);
    UnionsData[guid] = union;
}

let UnionsData_example = {
    "shengcheng": {
        guid: '',
        name: '圣城',
        discription: '发士大夫士大夫',
        founder: {xuid: 123123, realName:"WN1027"},
        lord: {xuid: 123123, realName:"WN1027"},
        operator: [],
        regTime: '2023-06-18 19:41:19',
        tpPos: [{name: '', pos: {x: 1, y: 65, z: 323, dimid: 0}},],
        bulletin: [{content:'fsdfsf', time: '2023-06-18 19:41:19', author: 'Wn1027'}, ],
        medal: '圣城居民',
        money:  0,
        recruitment: {
            bulletin: "欢迎加入!",
            application: [
                {xuid: "22222", realName: "Wn1028", content: "我想加入你的联盟", time:"2023-07-15 23:19:39"}
            ]
        },
        playerList: {
            '213123123': {
                xuid: '213123123',
                realName: 'WN1027',
                level: 0,
                lastLogTime: '2023-06-18 19:41:19',
                joinTime: '2023-06-18 19:41:19',
            },
        }
    }
};

// 联盟基础信息
function display_unionInfo(unionGuid){
    let union = loadUnion(CONF.UNIONSDATA.get(unionGuid));
    let res = ``;
    res += tr('gui.union.name', union.name);
    res += tr('gui.union.level', unionLevelText(union.score.blockPlace));
    res += tr('gui.union.money', union.money, CONF.CONFIG.get('EconomyName'));
    res += tr('gui.union.lord', union.lord.realName);
    res += tr('gui.union.creator', union.founder.realName);
    res += tr('gui.union.creationTime', union.regTime);
    res += tr('gui.union.discription', union.discription);
    return res;
}

//===============================================================================================================
//===============================================================================================================
mc.listen('onServerStarted', ()=>{
    {
        let cmd = mc.newCommand("union", tr('gui.cmd.union'), PermType.Any);
        cmd.setEnum("LordAction", ["lord"]);
        cmd.mandatory("action", ParamType.Enum, "LordAction", 1);
        cmd.mandatory("name", ParamType.String);
        cmd.mandatory("switch", ParamType.Bool);
        cmd.overload([]);
        cmd.overload(["LordAction", "name", "switch"]);
        cmd.setCallback((_cmd, _ori, out, res)=>{
            let pl = _ori.player;
            if (res.action != undefined){
                if (res.action == 'lord'){
                    if (pl!= null){
                        if (!pl.isOP()){
                            out.error(tr('gui.cmd.notOP'));
                            return;
                        }
                    }
                    let playerDatas = JSON.parse(CONF.PLAYERDATA.read());
                    for (let xuid in playerDatas){
                        if (playerDatas[xuid].realName == res.name){
                            playerDatas[xuid].unionLord = res.switch;
                            CONF.PLAYERDATA.set(xuid, playerDatas[xuid]);
                            if (res.switch == true){
                                out.success(tr('gui.cmd.union.lord.success', playerDatas[xuid].realName));
                                mc.broadcast(tr('gui.cmd.union.lord.success', playerDatas[xuid].realName));
                                return;
                            }else{
                                out.success(tr('gui.cmd.union.lord.faild', playerDatas[xuid].realName));
                                mc.broadcast(tr('gui.cmd.union.lord.faild', playerDatas[xuid].realName));
                                return;
                            }
                        }
                    }
                    out.error(tr('gui.cmd.union.lord.noPlayer'));
                }
                return;
            }
    
            if (pl != null){
                let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
                if (playerData?.unionGuid == null){
                    unionList(pl);
                }else{
                    unionInfo(pl, playerData.unionGuid);
                }
                out.success('');
            }else{
                out.error(tr('gui.cmd.onlyOP'));
            }
        });
        cmd.setup();
    }

    {
        if (CONF.CONFIG.get('UnionChat')){
            let cmd = mc.newCommand("umsg", tr('gui.cmd.umsg'), PermType.Any);
            cmd.mandatory("msg", ParamType.RawText);
            cmd.overload(["msg"]);
            cmd.setCallback((_cmd, _ori, out, res)=>{
                let pl = _ori.player;
                if (pl != null){
                    let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
                    if (playerData?.unionGuid != null){
                        let union = loadUnion(CONF.UNIONSDATA.get(playerData.unionGuid));
                        sendUnionMsg(playerData.unionGuid, [0,1,2], `${union.name} |§r§a <${pl.realName}>§r ${res.msg}`);
                    }
                    out.success('');
                }else{
                    out.error(tr('gui.cmd.onlyOP'));
                }
            });
            cmd.setup();
        }
    }

    if (EconomyType == 'func'){
        if (ll.hasExported(EconomyFunc.namespace, EconomyFunc.getMoney)){
            LLImport._getMoney = ll.imports(EconomyFunc.namespace, EconomyFunc.getMoney);
            LLImport._setMoney = ll.imports(EconomyFunc.namespace, EconomyFunc.setMoney);
            LLImport._addMoney = ll.imports(EconomyFunc.namespace, EconomyFunc.addMoney);
            LLImport._reduceMoney = ll.imports(EconomyFunc.namespace, EconomyFunc.reduceMoney);
        }else{
            logger.error(tr('gui.api.money.faild'));
        }
    }
    if (CONF.CONFIG.get('PAPI')){
        let PAPI = require("./GMLIB-LegacyRemoteCallApi/lib/BEPlaceholderAPI-JS").PAPI;
        //let PAPI = require('./lib/BEPlaceholderAPI-JS').PAPI;
        PAPI.registerPlayerPlaceholder(PAPI_UnionMedal, 'WnUnion', 'player_UnionMedal');
        PAPI.registerPlayerPlaceholder(PAPI_UnionLevel, 'WnUnion', 'player_UnionLevel');
        
    }

    function PAPI_UnionMedal(pl){
        return getMedal(pl.xuid);
    }
    function PAPI_UnionLevel(pl){
        return getPlayerUnionLevel(pl.xuid);
    }
});

mc.listen('onJoin', (pl)=>{
    if(pl.isSimulatedPlayer()){return;}
    // 玩家信息初始化
    if (CONF.PLAYERDATA.get(pl.xuid) == null){
        let playerData = loadPlData({
            xuid: pl.xuid,
            realName: pl.realName,
        });
        CONF.PLAYERDATA.set(pl.xuid, playerData);
    }

    let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
    
    // 更新上次登录时间
    if (playerData.unionGuid != null){
        let union = loadUnion(CONF.UNIONSDATA.get(playerData.unionGuid));
        union.playerList[pl.xuid].lastLogTime = system.getTimeStr();
        saveUnion(union.guid, union);
    }
    // 即时通知
    if (Object.keys(playerData.notice).length != 0){
        for (let title in playerData.notice){
            pl.tell(`§b[${playerData.notice[title].num}]§r[${playerData.notice[title].title}§r] ${playerData.notice[title].msg} §r${playerData.notice[title].time ?? ''}`);
            pl.sendToast(`§b[${playerData.notice[title].num}]§r${playerData.notice[title].title}`, playerData.notice[title].msg);
        }
        playerData.notice = {};
        CONF.PLAYERDATA.set(pl.xuid, playerData);
    }
});

if (CONF.CONFIG.get('BlockPlaceEnabled')){
    mc.listen('afterPlaceBlock', (pl, bl)=>{
        if (bl.isWaterBlockingBlock && !bl.isHeavyBlock){
            addPlayerConstruct(pl.xuid, 1);
        }
    });
}

mc.listen('onMobHurt', (mob, source ,damage, cause)=>{
    if (mob == undefined || source == undefined){return true;}
    if (mob.isPlayer() && source.isPlayer()){
        let pl = source.toPlayer();
        let pl_1 = mob.toPlayer();
        if(pl.isSimulatedPlayer()) return true;
        if(pl_1.isSimulatedPlayer()) return true;
        let playerData = loadPlData(pl.xuid);
        let playerData_1 = loadPlData(pl_1.xuid);
        if (playerData.unionGuid != null && playerData_1.unionGuid != null){
            if (playerData.unionGuid == playerData_1.unionGuid){
                if (loadUnion(playerData.unionGuid).friendlyFire == false){
                    setTimeout(() => {
                        if (pl_1 != null){pl_1.stopFire();}
                    }, 100);
                    return false; // 关闭队友伤害
                }
            }
        }
    }
});

mc.listen("onAttackEntity", (pl, en, damage)=>{
    if (en!= null && en.isPlayer()){
        let pl_1 = en.toPlayer();
        if(pl_1.isSimulatedPlayer()) return true;
        let playerData = loadPlData(pl.xuid);
        let playerData_1 = loadPlData(pl_1.xuid);
        if (playerData.unionGuid != null && playerData_1.unionGuid != null){
            if (playerData.unionGuid == playerData_1.unionGuid){
                if (loadUnion(playerData.unionGuid).friendlyFire == false){
                    return false; // 关闭队友伤害
                }
            }
        }
    }
});

mc.listen('onChat', (pl, msg)=>{
    if (CONF.CONFIG.get('ChatMedal')){
        let playerData = loadPlData(CONF.PLAYERDATA.get(pl.xuid));
        if (playerData?.unionGuid != null){
            let union = loadUnion(CONF.UNIONSDATA.get(playerData.unionGuid));
            mc.broadcast(`[§r${union.medal}§r]<${pl.realName}> ${msg}`);
            return false;
        }
    }
});

//===============================================================================================================
//================================================================================================================
// 联盟等级
function unionLevelText(blockPlace){
    let level = Math.floor(blockPlace/20000);
    if (level < 3){return `§2「${level}」`;}
    if (level >= 3 && level < 5){return `§r§3「${level}」§r`;}
    if (level >= 5 && level < 10){return `§r§e「${level}」§r`;}
    if (level >= 10 && level < 20){return `§r§6「${level}」§r`;}
    if (level >= 20 && level < 30){return `§r§c「${level}」§r`;}
    if (level >= 30 && level < 40){return `§r§c「${level}」 ✿§r`;}
    if (level >= 40 && level < 50){return `§r§c「${level}」 ✿✿§r`;}
    if (level >= 50 && level < 60){return `§r§c「${level}」 ✿✿✿§r`;}
    if (level >= 60 && level < 70){return `§r§c「${level}」 ✿✿✿✿§r`;}
    if (level >= 80){return `§r§c「${level}」 ✿✿✿✿✿§r`;}
}

// 数字加逗号
function toThousands(num) {
    num = (num || 0).toString();
    let result = '';
    while (num.length > 3) {
        result = ',' + num.slice(-3) + result;
        num = num.slice(0, num.length - 3);
    }
    if (num) { result = num + result; }
    return result;
}

// 获取当天日期字符串
function nowDayStr(){
    let date = new Date();
    return `${date.getFullYear()}-${date.getMonth()+1}-${date.getDate()}`;
}

// 解析日期字符串
function parseDayStr(DateStr){
    let list = DateStr.split('-');
    list[1] --;
    return new Date(...list);
}

function parseDateStr(str){
    let d = str.split(" ")[0].split("-").map(n => Number(n));
    let t = str.split(" ")[1].split(":").map(n => Number(n));
    return new Date(d[0], d[1]-1, d[2], t[0], t[1], t[2]);
}

function menu_end(pl, title, endmsg, lastForm, lastForm_run){
    //ver.23.7.12 lastForm作为回调函数，由需要pl参数改为无参
    var menu_end = mc.newSimpleForm();
    menu_end.setTitle(title);
    menu_end.setContent('\n\n'+endmsg+'\n\n\n\n');
    menu_end.addButton(tr('gui.end.close'));
    if (lastForm != undefined){
        menu_end.addButton(tr('gui.end.lastPage'));
    }
    
    pl.sendForm(menu_end,function(pl,id){
        if (id == null){return;}
        if (id == 1){
			if (typeof lastForm == "function"){
				return lastForm();
			}else{
				return pl.sendForm(lastForm,lastForm_run);
			}
        }
    });
}

// 多经济模块
let EconomyType = CONF.CONFIG.get('EconomyType'); // llmoney、scoreboard、func
let EconomyScoreBoardName = CONF.CONFIG.get('EconomyScoreBoardName');
let EconomyFunc = CONF.CONFIG.get('EconomyFunc');
let LLImport = {};


// mc.listen('onUseItem', (pl, item)=>{
//     if (item.type == 'minecraft:snowball'){
//        //ECOtest(pl);
//     }
// });


function ECOtest(pl){
    let fm_eco = mc.newSimpleForm()
    .setTitle(`经济测试`)
    .addButton(`§lGet`)
    .addButton(`§lSet 10`)
    .addButton(`§lAdd 10`)
    .addButton(`§lReduce 7`);
    
    pl.sendForm(fm_eco, (pl, id)=>{
        if (id == null){return;}
        if (id == 0){
            let res = getMoney(pl.xuid);
            pl.sendToast(`Get`, `[${res}] Money:§e ${getMoney(pl.xuid)}`);
            pl.tell(`[${res}] Get§e ${getMoney(pl.xuid)}`);
            return ECOtest(pl);
        }
        if (id == 1){
            let res = setMoney(pl.xuid, 10);
            pl.sendToast(`Set`, `[${res}] Set 10 | Money:§e ${getMoney(pl.xuid)}`);
            pl.tell(`[${res}] Set 10 | Money:§e ${getMoney(pl.xuid)}`);
            return ECOtest(pl);
        }
        if (id == 2){
            let res = addMoney(pl.xuid, 10);
            pl.sendToast(`Add`, `[${res}] Add 10 | Money:§e ${getMoney(pl.xuid)}`);
            pl.tell(`[${res}] Add 10 | Money:§e ${getMoney(pl.xuid)}`);
            return ECOtest(pl);
        }
        if (id == 3){
            let res = reduceMoney(pl.xuid, 7);
            pl.sendToast(`Reduce`, `[${res}] Reduce 7 | Money:§e ${getMoney(pl.xuid)}`);
            pl.tell(`[${res}] Reduce 7 | Money:§e ${getMoney(pl.xuid)}`);
            return ECOtest(pl);
        }
    });
}

function getMoney(xuid){
    if (EconomyType == 'llmoney'){
        return money.get(xuid);   
    }
    if (EconomyType == 'scoreboard'){
        let uuid = data.xuid2uuid(xuid);
        if (uuid == null){return null;}
        let res = mc.getPlayerScore(uuid, EconomyScoreBoardName);
        if (res == undefined){res = null;}
        return res;
    }
    if (EconomyType == 'func'){
        return LLImport._getMoney(xuid);
    }
}

function addMoney(xuid, value, note = ''){
    if (EconomyType == 'llmoney'){
        return money.add(xuid, value);   
    }
    if (EconomyType == 'scoreboard'){
        let uuid = data.xuid2uuid(xuid);
        if (uuid == null){return false;}
        return mc.addPlayerScore(uuid, EconomyScoreBoardName, value);
    }
    if (EconomyType == 'func'){
        return LLImport._addMoney(xuid, value, note);
    }
}

function reduceMoney(xuid, value, note = ''){
    if (EconomyType == 'llmoney'){
        return money.reduce(xuid, value);   
    }
    if (EconomyType == 'scoreboard'){
        let uuid = data.xuid2uuid(xuid);
        if (uuid == null){return false;}
        let balance = getMoney(xuid);
        if (balance == null){return false;}
        if (getMoney(xuid) < value){return false;}
        return mc.reducePlayerScore(uuid, EconomyScoreBoardName, value);
    }
    if (EconomyType == 'func'){
        let balance = LLImport._getMoney(xuid);
        if (balance == null){return false;}
        if (balance < value){return false;}
        return LLImport._reduceMoney(xuid, value, note);
    }
}

function setMoney(xuid, value, note = ''){
    if (EconomyType == 'llmoney'){
        return money.set(xuid, value);   
    }
    if (EconomyType == 'scoreboard'){
        let uuid = data.xuid2uuid(xuid);
        if (uuid == null){return false;}
        return mc.setPlayerScore(uuid, EconomyScoreBoardName, value);
    }
    if (EconomyType == 'func'){
        return LLImport._setMoney(xuid, value, note);
    }
}

//===============================================================================
function Language(){ // 'en_US'、'zh_CN'
    var default_lang = {
        "zh_CN": {
            "#VERSION": "1.2.0.0",
            "#1": "zh_CN 中文原版: Wn1027",
            ".member": "成员",
            ".manager": "§3管理",
            ".lord": "§d盟主",
            ".overworld": "§2主世界",
            ".theNether": "§c下届",
            ".theEnd": "§3末地",
            ".confirm": "§l确认",
            ".delete": "§l§c删除",
            ".cancel": "§2§l取消",
            ".refuse": "§c§l拒绝",
            "gui.input.positiveInteger": "请输入正整数",
            "gui.error.positiveInteger": "§cERROR 请输入正整数",
            "gui.error.rightIndex": "§c请输入正确的序号",
            "gui.allUnion": "§l所有联盟",
            "gui.create.button": "§l>> 创建联盟 <<",
            "gui.allUnion.order.default": "§l︽ 默认排序 ︾",
            "gui.allUnion.order.level": "§l︽ 按建设等级排序 ︾",
            "gui.allUnion.order.money": "§l︽ 按联盟财富排序 ︾",
            "gui.allUnion.order.reg": "§l︽ 最新创建排序 ︾",
            "gui.allUnion.order.member": "§l︽ 成员数量排序 ︾",
            "gui.create.title": "§l创建联盟",
            "gui.create.insufficientmoney": "§c创建联盟需花费§r {}§r * {}§c, 余额不足",
            "gui.create.noAccess": "§c你没有创建联盟的资格, 请向服务器管理员申请",
            "gui.create.haveJoined": "§c你已经加入了联盟, 不能创建新的联盟",
            "gui.create.cost.label": "\n§b创建联盟费用:§r {}§r * {}\n ",
            "gui.create.newName": "§a新联盟名称",
            "gui.create.unionNameLimit": "§cERROR 联盟名称过长 ( {} > {} )",
            "gui.create.success.tip": "§a创建联盟:§r {}",
            "gui.create.success.tip.1": "<{}> §a创建了联盟 §r{} !",
            "gui.unionInfo.title": "联盟详情 - {}",
            "gui.bulletin": "§3§l联盟公告",
            "gui.bulletin.new": "§l>> 发布新公告 <<",
            "gui.bulletin.top": "§c§l[置顶] ",
            "gui.bulletin.new.tip": "\n §7玩家在上线时会收到提示\n ",
            "gui.bulletin.new.topic": "§b公告标题",
            "gui.bulletin.new.content": "§l§e内容",
            "gui.bulletin.new.notempty": "§cERROR 不能为空", 
            "gui.bulletin.new.success": "<{}> §b发布了公告§r {}", 
            "gui.bulletin.title": "§3§l公告详情",
            "gui.bulletin.topic": " §c# 标题:§r {}\n§r",
            "gui.bulletin.sender": " §7# 发布者:§r {}\n§r",
            "gui.bulletin.sendTime": " §7# 发布时间:§7 {}\n§r",
            "gui.bulletin.content": " §b# 内容:\n\n §r{}§r\n ",
            "gui.bulletin.setTop": "§c§l【管理】 §3设为置顶",
            "gui.bulletin.setTop.success": "§e已置顶§r {}",
            "gui.bulletin.delete": "§c§l【管理】 删除公告",
            "gui.bulletin.delete.confirm": "\n§c确认删除公告§r {} §c?",
            "gui.bulletin.delete.success": "<{}> §c删除了公告§r {}",
            "gui.memberList": "§d§l成员列表§7({})",
            "gui.memberList.title": "§d§l成员列表 - §r§l{}",
            "gui.contribute": "§3§l联盟贡献",
            "gui.application": "§2§l>> 申请加入 <<",
            "gui.tpPos": "§2§l传送锚点",
            "gui.unionLog": "§l联盟日志",
            "gui.unionManager": "§c§l【管理】 联盟管理",
            "gui.memberInfo": "§l成员详情",
            "gui.member.name": " §a# 成员昵称:§r {}\n",
            "gui.member.level": " §e# 权限等级:§r {}\n",
            "gui.member.contribute": " §b# 成员贡献:§r {}\n",
            "gui.member.lastLogTime": " §6# 上次登录:§r {}\n",
            "gui.member.joinTime": " §6# 加入时间:§r {}",
            "gui.member.tp.button": "§l§3传送至该成员§r",
            "gui.member.online": "§l§2玩家在线",
            "gui.member.offline": "§l§7玩家离线",
            "gui.member.exit.button": "§c§l退出联盟",
            "gui.member.kick.button": "§c§l【管理】移出联盟",
            "gui.member.setOp.button": "§c§l【管理】设为管理员",
            "gui.member.deop.button": "§c§l【管理】撤除管理员",
            "gui.member.tpa.title": "§3§l传送到玩家",
            "gui.member.tpa.notself": "§c不能传送到自己",
            "gui.member.tpa.insufficientmoney": " §c无法传送, 余额不足",
            "gui.member.tpa.postContent": "\n<{}> §b请求传送到你身边",
            "gui.member.tpa.agree": "§2§l接受传送",
            "gui.member.tpa.refuse.tip": " §c<{}> 拒绝了你的传送请求",
            "gui.member.tpa.note": "[{}§r] §3传送至§r {}",
            "gui.member.tpa.agree.tip": " §r<{}> §b接受了你的传送请求",
            "gui.member.tpa.post.tip": " §b请求传送至§r <{}>",
            "gui.member.kick.title": "§c§l移出联盟",
            "gui.member.exit.error.noLord": "§cERROR 无法将盟主移出联盟",
            "gui.member.exit.error.onlyLord": "§cERROR 只有盟主才有权限将管理员移出联盟",
            "gui.member.exit.kick.tip": "\n§c确认将§r {} §c移出联盟: §r{} §r?",
            "gui.member.exit.kick.title": "§l成员退出",
            "gui.member.exit.kick.msg": "§c你已被§r {} §c移出联盟:§r {}",
            "gui.member.exit.kick.log": "<{}> §c已被§r {} §c移出联盟§r {}",
            "gui.member.exit.broadcast": "<{}> §c退出了联盟:§r {}",
            "gui.member.exit.confirm": "\n§c确认退出联盟: §r{} §r?",
            "gui.member.exit.title": "§c§l退出联盟",
            "gui.member.exit.success": "§c你已退出§r {}",
            "gui.member.manager.title": "§d§l联盟管理员",
            "gui.member.manager.confirm": "\n§d确认将§r {} §d设为联盟管理员?",
            "gui.member.manager.success": "<{}> §d已被§r {} §d设为联盟管理员",
            "gui.member.manager.dismissal.confirm": "\n§b确认取消§r {} §b的联盟管理员?",
            "gui.member.manager.dismissal.success": "<{}> §b已被§r {} §b撤除联盟管理员",
            "gui.tpPos.title": "§3§l传送锚点",
            "gui.tpPos.unenabled": "§cERROR 服务器未启用联盟传送点功能",
            "gui.tpPos.noAccess": "§cERROR 非联盟成员无查看权限",
            "gui.tpPos.set": "§3§l设置传送锚点",
            "gui.tpPos.set.tip.1": "\n §a> 设置当前坐标为联盟传送锚点",
            "gui.tpPos.set.tip.2": " §3> 创建锚点花费 §b联盟财富 * §r{}\n`}",
            "gui.tpPos.set.tip.3": " §7> §o最多拥有 {} 个传送锚点\n ",
            "gui.tpPos.create.name": "§b新传送点名称",
            "gui.tpPos.delete.input": "§c删除传送点(输入传送点序号)",
            "gui.tpPos.create.title": "§3§l创建传送锚点",
            "gui.tpPos.create.error.maxPos": "§c联盟最多拥有 {} 个传送锚点",
            "gui.tpPos.create.error.notEnoughMoney": "§c联盟财富不足",
            "gui.tpPos.notice.title": "§3§l传送锚点",
            "gui.tpPos.create.success.log": "<{}> §3创建传送锚点§r {}",
            "gui.tpPos.create.success.tip": "§3创建传送锚点§r {}",
            "gui.tpPos.delete.title": "§c§l删除传送锚点",
            "gui.tpPos.delete.success.log": "<{}> §c删除传送锚点§r {}",
            "gui.tpPos.delete.success.tip": "§c删除传送锚点§r {}",
            "gui.tpPos.tpTo": "§l§2传送到",
            "gui.contribute.title": "§3§l联盟贡献",
            "gui.contribute.construct": "§r§e联盟建设度",
            "gui.contribute.bank.1": "§r§b联盟财富",
            "gui.contribute.bank": "§3§l联盟财富",
            "gui.contribute.contribute.construct": "§l贡献建设度",
            "gui.bank.unenabled": "§cERROR 服务器未启用联盟财富功能",
            "gui.bank.tip": "§b# 联盟财富:§r {} {}\n\n§a联盟财富属于联盟公共资源\n节约资源,按需存取\n ",
            "gui.bank.deposit.button": "§l存 款",
            "gui.bank.withdrawal.button": "§l取 款",
            "gui.bank.bill.button": "§l§2存取记录",
            "gui.bank.deposith.title": "§l联盟存款",
            "gui.bank.withdrawal.title": "§l联盟取款",
            "gui.bank.deposith.wallet": "§c# 个人钱包:§r {} \n\n§6警惕移动支付, 谨防电信诈骗\n ",
            "gui.bank.deposith.input": "§e# 存入金额",
            "gui.bank.deposith.note": "§a# 备注",
            "gui.bank.wealth.wallet": "§b# 联盟财富:§r {} \n\n§6警惕移动支付, 谨防电信诈骗\n ",
            "gui.bank.withdrawal.input": "§b# 取款金额",
            "gui.bank.withdrawal.reason": "§a# 取款缘由",
            "gui.bank.deposith.error.notEnoughMoney": "§cERROR 存款失败, 余额不足 | 剩余: {}",
            "gui.bank.deposith.billnote": "[§r{}§r] §e存入联盟",
            "gui.bank.deposith.error": "§cERROR 存款失败",
            "gui.bank.deposith.success": "<{}> §e存入了§r {} {}\n§7{}",
            "gui.bank.deposith.success.log": "[{}] <{}> 存入了 {} {}\n§7{}",
            "gui.bank.withdrawal.error.notEnoughMoney": "§cERROR 联盟余额不足",
            "gui.bank.withdrawal.error.notNote": "§cERROR 请输入取款理由",
            "gui.bank.withdrawal.billnote": "[§r{}§r] §b联盟取款",
            "gui.bank.withdrawal.error.addmoney": "§cERROR 取款失败, addMoney失败",
            "gui.bank.withdrawal.success": "<{}> §b取走了§r {} {}\n§7{}",
            "gui.bank.withdrawal.success.log": "[{}] <{}> 取走了 {} {}\n§7{}",
            "gui.construct.title": "§l联盟建设度",
            "gui.construct.personal": " §6# 个人建设度: §r{}\n",
            "gui.construct.input": "§d# 贡献给:§r {}",
            "gui.construct.note": "§e# 留言",
            "gui.construct.bill": "§a查看贡献记录",
            "gui.construct.error.tooLittle": "§c单次贡献需大于 1000",
            "gui.construct.error.notEnough": "§c贡献度不足, 你只有 {} (< {})",
            "gui.construct.success": "{}§r <{}> §e贡献了§r {} §r§e点建设度\n§7{}",
            "gui.construct.success.log": "[{}] {}§r <{}> §e贡献了§r {} §r§e点建设度\n§7{}",
            "gui.application.title": "§2§l申请加入",
            "gui.application.error.onlyOne": "§cERROR 你只能加入一个联盟",
            "gui.application.error.refuse": "§b联盟§r {} §r§b暂不接受任何申请",
            "gui.application.tip": "\n§a你正在申请加入: §r{}§r\n\n{} ",
            "gui.application.reason": "§l# 申请理由",
            "gui.application.send.success": "<{}> §a申请加入联盟: §r{}",
            "gui.manager.title": "§c§l联盟管理",
            "gui.manager.error.noAccess": "§cERROR 权限不足，无法查看",
            "gui.manager.setInfo.button": "§l设置联盟基本信息",
            "gui.manager.application.button": "§l§2招新申请",
            "gui.manager.other.button": "§l辅助功能",
            "gui.manager.transference.button": "§l§c联盟转让",
            "gui.manager.dissolution.button": "§l§c解散联盟",
            "gui.manager.setInfo.name.input": "§a# 联盟名称",
            "gui.manager.setInfo.discription.input": "§a# 联盟描述§7(换行符:\\n)",
            "gui.manager.setInfo.medal.input": "§d# 联盟勋章",
            "gui.manager.setInfo.icon.input": "§e# 联盟图标",
            "gui.manager.setInfo.name.error.notEmpty": "§cERROR 联盟名称不能为空",
            "gui.manager.setInfo.name.error.tooLong": "§cERROR 联盟名称过长 ({} > {})",
            "gui.manager.setInfo.discription.error.tooLong": "§cERROR 联盟描述过长 ({} > {})",
            "gui.manager.setInfo.success.log": "<{}> §e更新了联盟信息\n§a名称:§r {}§r\n§a描述:§r {}§r\n§a勋章:§r {}\n§a图标:§r {}",
            "gui.manager.setInfo.success.tip": "已更新联盟基本信息",
            "gui.manager.setInfo.success": "<{}> §a更新了联盟信息",
            "gui.manager.recruitment.title": "§2§l联盟招新",
            "gui.manager.recruitment.acceptAll": "§2§l状态: 接受所有玩家申请",
            "gui.manager.recruitment.refuseAll": "§c§l状态: 拒绝所有玩家申请",
            "gui.manager.recruitment.setNotice.button": "§l>> 设置招新公告 <<",
            "gui.manager.recruitment.change.refuseAll": "<{}> §e将招新状态设置为: §a允许申请",
            "gui.manager.recruitment.change.acceptAll": "<{}> §e将招新状态设置为: §c拒绝所有申请",
            "gui.manager.recruitment.setNotice.title": "§l设置招新公告",
            "gui.manager.recruitment.setNotice.tip": "§b# 招新公告§7(换行符:\\n)",
            "gui.manager.recruitment.setNotice.error.tooLong": "§cERROR 招新公告描述过长 ({} > {})",
            "gui.manager.recruitment.setNotice.success": "<{}> §e更新了招新公告\n§r{}§r",
            "gui.manager.recruitment.setNotice.success.tip": "已更新招新公告",
            "gui.manager.recruitment.application.name": "§l{} 的申请",
            "gui.manager.recruitment.application.content": "\n§b# 申请人:§r §a{}§r\n§7# 申请时间: {}§r\n§e# 申请理由:§r {}\n\n ",
            "gui.manager.recruitment.application.accept": "§2§l接受申请",
            "gui.manager.recruitment.application.refuse": "§c§l拒绝申请",
            "gui.manager.recruitment.application.error": "§cERROR 该玩家已加入了其他联盟, 该申请被系统撤回",
            "gui.manager.recruitment.application.success": "<{}> §a加入了联盟:§r {} §7| 处理人: <{}>",
            "gui.manager.recruitment.application.broascast": "<{}> §a加入了联盟:§r {}",
            "gui.manager.recruitment.application.success.tip": "§a已同意§r <{}> §a加入§r {} §r§a的申请",
            "gui.manager.recruitment.application.faild": "§c加入联盟§r {} §c的申请被拒绝 §7| 处理人: <{}>",
            "gui.manager.recruitment.application.faild.1": "<{}> §c加入联盟的申请被拒绝  §7| 处理人: <{}>",
            "gui.manager.recruitment.application.faild.tip": "§c已拒绝§r <{}> §c加入§r {}",
            "gui.manager.other.title": "§l辅助功能",
            "gui.manager.other.friendlyFire.switch": "队友伤害",
            "gui.manager.other.friendlyFire.title": "§l联盟队友伤害",
            "gui.manager.other.friendlyFire.open": "<{}> §a已开启队友伤害",
            "gui.manager.other.friendlyFire.close": "<{}> §7已关闭队友伤害",
            "gui.manager.other.friendlyFire.open.tip": "已开启队友伤害",
            "gui.manager.other.friendlyFire.close.tip": "已关闭队友伤害",
            "gui.manager.transference.title": "§c§l联盟转让",
            "gui.manager.transference.content": "\n§c转让联盟盟主权限:\n",
            "gui.manager.transference.error.lord": "§cERROR {} 已经是盟主了",
            "gui.manager.transference.error.notlord": "§cERROR {} 没有创建联盟的权限, 请联系服务器管理员",
            "gui.manager.transference.confirm": "§c确认将联盟盟主权限转让给:§r {}?",
            "gui.manager.transference.success": "<{}> §c已将联盟盟主转让给§r {}",
            "gui.manager.transference.success.tip": "转让成功",
            "gui.manager.dissolution.title": "§c§l解散联盟",
            "gui.manager.dissolution.error.notEmptyMoney": "§cERROR 联盟财富未清零, 无法解散联盟",
            "gui.manager.dissolution.error.noAccess": "§cERROR 只有盟主或服务器管理员可以解散联盟",
            "gui.manager.dissolution.confirm": "§c§l确认解散联盟: {}?\n\n 所有玩家被移出联盟\n联盟数据将被完全清除,无法恢复",
            "gui.manager.dissolution.success": "§c联盟§r {} §r§c已被§a <{}> §c解散",
            "gui.manager.dissolution.success.tip": "已解散联盟 {}",
            "gui.manager.dissolution.success.broadcast": "§c联盟§r {} §r§c已被§a <{}> §c解散 | {}",
            "gui.bill.bank.today": "§3§l今日财富",
            "gui.bill.bank.history": "§3§l历史账单",
            "gui.bill.construct.today": "§c§l今日建设度",
            "gui.bill.construct.history": "§c§l历史建设度",
            "gui.bill.notice.today": "§l今日通知",
            "gui.bill.notice.history": "§l历史通知",
            "gui.bill.EnterSerialNumber": "§2§l输入序号",
            "gui.bill.bank.notice.plus": "§e[+{}]§r <{}> §e存入\n§r§7备注:§r {}\n§r§7时间: {}",
            "gui.bill.bank.notice.minus": "§b[{}]§r <{}> §b取出\n§r§7理由:§r {}\n§r§7时间: {}",
            "gui.bill.construct.notice.plus": "§e[+{}]§r 由 <{}> 贡献\n§r§7留言:§r {}\n§r§7时间: {}",
            "gui.bill.construct.notice.minus": "§b[{}]§r 由 <{}> 取走\n§r§7留言:§r {}\n§r§7时间: {}",
            "gui.union.name": " §a# 联盟名称:§r {}\n§r",
            "gui.union.level": " §a# 联盟等级:§r {}\n§r",
            "gui.union.money": " §b# 联盟财富:§r {} {}\n§r",
            "gui.union.lord": " §d# 盟主:§r {}\n§r",
            "gui.union.creator": " §7# 创建者: {}\n§r",
            "gui.union.creationTime": " §7# 创建时间: {}\n§r",
            "gui.union.discription": " §7# 联盟描述:§r {}",
            "gui.cmd.union": "§b联盟系统",
            "gui.cmd.notOP": "无法执行OP指令",
            "gui.cmd.union.lord.success": "<{}> 已获得创建联盟权限",
            "gui.cmd.union.lord.faild": "<{}> 已失去创建联盟权限",
            "gui.cmd.union.lord.noPlayer": "无此玩家, 请该玩家先进入游戏",
            "gui.cmd.onlyOP": "仅允许玩家执行",
            "gui.cmd.umsg": "§b联盟聊天",
            "gui.api.money.faild": "经济API获取失败, 请检查经济插件状态以及本插件经济配置",
            "gui.end.close": "§6§l关闭表单",
            "gui.end.lastPage": "§l返回上一页"
        },
        "en_US":{
            "#VERSION": "1.2.0.0",
            "#1": "en_US Translation by: Wn1027",
            ".member": "Member",
            ".manager": "§3Manager",
            ".lord": "§dLord",
            ".overworld": "§2Overworld",
            ".theNether": "§cTheNether",
            ".theEnd": "§3TheEnd",
            ".confirm": "§lConfirm",
            ".delete": "§l§cDelete",
            ".cancel": "§2§lCancel",
            ".refuse": "§c§lRefuse",
            "gui.input.positiveInteger": "Please enter a positive integer.",
            "gui.error.positiveInteger": "§cERROR: Please enter a positive integer.",
            "gui.error.rightIndex": "§cPlease enter the correct serial number",
            "gui.allUnion": "§lAll Unions",
            "gui.create.button": "§l>> Create a new union <<",
            "gui.allUnion.order.default": "§l︽ Default ︾",
            "gui.allUnion.order.level": "§l︽ Union's Level ︾",
            "gui.allUnion.order.money": "§l︽ Union's Money ︾",
            "gui.allUnion.order.reg": "§l︽ Creation time ︾",
            "gui.allUnion.order.member": "§l︽ Union's population ︾",
            "gui.create.title": "§lCreate a new union",
            "gui.create.insufficientmoney": "§cYou don't have enough money to create a union§r {}§r * {}",
            "gui.create.noAccess": "§cYou do not have the qualification to create a union.\n Please apply to the server administrator",
            "gui.create.haveJoined": "§cYou have joined the union and cannot create a new one",
            "gui.create.cost.label": "\n§bCreate Union Fee:§r {}§r * {}\n ",
            "gui.create.newName": "§aNew Union Name",
            "gui.create.unionNameLimit": "§cERROR: union's name is too long ( {} > {} )",
            "gui.create.success.tip": "§aCreate a new union:§r {}",
            "gui.create.success.tip.1": "<{}> §aCreate a new union §r{} !",
            "gui.unionInfo.title": "Union's details - {}",
            "gui.bulletin": "§3§lUnion Bulletin",
            "gui.bulletin.new": "§l>> Release new<<",
            "gui.bulletin.top": "§c§l[Top] ",
            "gui.bulletin.new.tip": "\n §7Players will receive messages when they go online\n ",
            "gui.bulletin.new.topic": "§bAnnouncement Theme",
            "gui.bulletin.new.content": "§l§eAnnouncement content",
            "gui.bulletin.new.notempty": "§cERROR: Content cannot be empty", 
            "gui.bulletin.new.success": "<{}> §bhas released a notice§r {}", 
            "gui.bulletin.title": "§3§lDetails of announcement",
            "gui.bulletin.topic": " §c# Topic:§r {}\n§r",
            "gui.bulletin.sender": " §7# Sender:§r {}\n§r",
            "gui.bulletin.sendTime": " §7# Date:§7 {}\n§r",
            "gui.bulletin.content": " §b# Content:\n\n §r{}§r\n ",
            "gui.bulletin.setTop": "§c§l【OP】 §3Set to Top",
            "gui.bulletin.setTop.success": "§eSet to Top§r {}",
            "gui.bulletin.delete": "§c§l【OP】 Delete Notice",
            "gui.bulletin.delete.confirm": "\n§cAre you sure to delete the notice?§r {} §c?",
            "gui.bulletin.delete.success": "<{}> §chas deleted the notice§r {}",
            "gui.memberList": "§d§lMembers§7({})",
            "gui.memberList.title": "§d§lMembers - §r§l{}",
            "gui.contribute": "§3§lContributions",
            "gui.application": "§2§l>> Apply to join <<",
            "gui.tpPos": "§2§lTeleport Pos",
            "gui.unionLog": "§lUnion's log",
            "gui.unionManager": "§c§l【OP】 Set up",
            "gui.memberInfo": "§lMember Details",
            "gui.member.name": " §a# Name:§r {}\n",
            "gui.member.level": " §e# Level:§r {}\n",
            "gui.member.contribute": " §b# Contribute:§r {}\n",
            "gui.member.lastLogTime": " §6# LastLog:§r {}\n",
            "gui.member.joinTime": " §6# Joining Time:§r {}",
            "gui.member.tp.button": "§l§3Transfer to this member§r",
            "gui.member.online": "§l§2Online",
            "gui.member.offline": "§l§7Offline",
            "gui.member.exit.button": "§c§lExit from Union",
            "gui.member.kick.button": "§c§l【OP】Kick out the member",
            "gui.member.setOp.button": "§c§l【OP】Set as manager",
            "gui.member.deop.button": "§c§l【OP】Dismissal",
            "gui.member.tpa.title": "§3§lTeleport",
            "gui.member.tpa.notself": "§cYou cannot teleport to yourself",
            "gui.member.tpa.insufficientmoney": " §cUnable to teleport, not enough money",
            "gui.member.tpa.postContent": "\n<{}> §brequests to be teleport to your side",
            "gui.member.tpa.agree": "§2§lAccept",
            "gui.member.tpa.refuse.tip": " §c<{}> has rejected your teleport request",
            "gui.member.tpa.note": "[{}§r] §3teleport to§r {}",
            "gui.member.tpa.agree.tip": " §r<{}> §bhas accepted your teleport request",
            "gui.member.tpa.post.tip": " §brequests to§r <{}>",
            "gui.member.kick.title": "§c§lKick from union",
            "gui.member.exit.error.noLord": "§cERROR: You cannot remove the lord from the union",
            "gui.member.exit.error.onlyLord": "§cERROR: Only the lord of the union has the authority to remove manager from the union",
            "gui.member.exit.kick.tip": "\n§cAre you sure to kick§r {} §cout of §r{}§c ?§r",
            "gui.member.exit.kick.title": "§lMember Exit",
            "gui.member.exit.kick.msg": "§cYou have been kicked out of the union by §r {} §c|§r {}",
            "gui.member.exit.kick.log": "<{}> has been kicked out of the union by §r {} §c |§r {}",
            "gui.member.exit.broadcast": "<{}> §chas withdrawn from the union:§r {}",
            "gui.member.exit.confirm": "\n§cAre you sure you want to withdraw from the union: §r{} §r?",
            "gui.member.exit.title": "§c§lWithdrawal from the union",
            "gui.member.exit.success": "§cYou have withdraw form the§r {}",
            "gui.member.manager.title": "§d§lUnion's Manager",
            "gui.member.manager.confirm": "\n§dAre you sure to set §r {} §d as the manager?",
            "gui.member.manager.success": "<{}> §dis set as a manager by §r {}",
            "gui.member.manager.dismissal.confirm": "\n§bAre you sure to dismiss §r {} §b?",
            "gui.member.manager.dismissal.success": "<{}> §bwas dismissed by §r {}",
            "gui.tpPos.title": "§3§lTP Pos",
            "gui.tpPos.unenabled": "§cERROR: feature not enabled",
            "gui.tpPos.noAccess": "§cERROR: Non union's members do not have viewing permissions",
            "gui.tpPos.set": "§3§lSet TP Pos",
            "gui.tpPos.set.tip.1": "\n §a> Set the current coordinate as the TP Pos",
            "gui.tpPos.set.tip.2": " §3> Create a point cost §bUnion's money * §r{}\n`}",
            "gui.tpPos.set.tip.3": " §7> §oUp to {} points\n ",
            "gui.tpPos.create.name": "§bNew TP point name",
            "gui.tpPos.delete.input": "§cDelete TP point(Enter serial number)",
            "gui.tpPos.create.title": "§3§lCreate TP point",
            "gui.tpPos.create.error.maxPos": "§cThe union can have up to {} teleportation point",
            "gui.tpPos.create.error.notEnoughMoney": "§cThe unon doesn't have enough money",
            "gui.tpPos.notice.title": "§3§lTP Pos",
            "gui.tpPos.create.success.log": "<{}> §3created TP point§r {}",
            "gui.tpPos.create.success.tip": "§3Created TP point§r {}",
            "gui.tpPos.delete.title": "§c§lDelete TP point",
            "gui.tpPos.delete.success.log": "<{}> §cdeleted TP point§r {}",
            "gui.tpPos.delete.success.tip": "§cDeleted TP point§r {}",
            "gui.tpPos.tpTo": "§l§2tp to",
            "gui.contribute.title": "§3§lContributions",
            "gui.contribute.construct": "§r§eUnion Score",
            "gui.contribute.bank.1": "§r§bUnion Bank",
            "gui.contribute.bank": "§3§lUnion Bank",
            "gui.contribute.contribute.construct": "§lContribute to the union",
            "gui.bank.unenabled": "§cERROR: Bank function not enabled",
            "gui.bank.tip": "§b# Union Wealth:§r {} {}\n\n§aUnion Wealth Belongs to Union Public Resources\nSave resources and access on demand\n ",
            "gui.bank.deposit.button": "§lDeposit",
            "gui.bank.withdrawal.button": "§lWithdrawal",
            "gui.bank.bill.button": "§l§2Bill",
            "gui.bank.deposith.title": "§lDeposit",
            "gui.bank.withdrawal.title": "§lWithdrawal",
            "gui.bank.deposith.wallet": "§c# Wallet:§r {} \n\n§6Beware of Mobile Payment and Phone fraud\n ",
            "gui.bank.deposith.input": "§e# Deposit amount",
            "gui.bank.deposith.note": "§a# Note",
            "gui.bank.wealth.wallet": "§b# Union Wealth:§r {} \n\n§6Beware of Mobile Payment and Phone fraud\n ",
            "gui.bank.withdrawal.input": "§b# Withdrawal amount",
            "gui.bank.withdrawal.reason": "§a# Reason",
            "gui.bank.deposith.error.notEnoughMoney": "§cERROR: Deposit failed due to insufficient balance | Remaining: {}",
            "gui.bank.deposith.billnote": "[§r{}§r] §eDeposit to Union",
            "gui.bank.deposith.error": "§cERROR Deposit failed",
            "gui.bank.deposith.success": "<{}> §edeposited§r {} {}\n§7{}",
            "gui.bank.deposith.success.log": "[{}] <{}> deposited {} {}\n§7{}",
            "gui.bank.withdrawal.error.notEnoughMoney": "§cERROR: Insufficient union balance",
            "gui.bank.withdrawal.error.notNote": "§cERROR: Please enter the withdrawal reason",
            "gui.bank.withdrawal.billnote": "[§r{}§r] §bUnion withdrawal",
            "gui.bank.withdrawal.error.addmoney": "§cERROR Withdrawal failed, addMoney failed",
            "gui.bank.withdrawal.success": "<{}> §bwithdrawed§r {} {}\n§7{}",
            "gui.bank.withdrawal.success.log": "[{}] <{}> withdrawed {} {}\n§7{}",
            "gui.construct.title": "§lConstruction score",
            "gui.construct.personal": " §6# Personal Score: §r{}\n",
            "gui.construct.input": "§d# Contribute to:§r {}",
            "gui.construct.note": "§e# note",
            "gui.construct.bill": "§aView contribution records",
            "gui.construct.error.tooLittle": "§cSingle contribution must be greater than 1000",
            "gui.construct.error.notEnough": "§cInsufficient score, you only have {} (< {})",
            "gui.construct.success": "{}§r <{}> §econtributed§r {} §r§epoints\n§7{}",
            "gui.construct.success.log": "[{}] {}§r <{}> §econtributed§r {} §r§epoints\n§7{}",
            "gui.application.title": "§2§lApply to join",
            "gui.application.error.onlyOne": "§cERROR: You can only join one union",
            "gui.application.error.refuse": "§bUnion §r {} §r§b does not accept any applications at the moment",
            "gui.application.tip": "\n§aYou are applying to join: §r{}§r\n\n{} ",
            "gui.application.reason": "§l# Application Reason",
            "gui.application.send.success": "<{}> §aapplies to join the alliance: §r{}",
            "gui.manager.title": "§c§lManagement",
            "gui.manager.error.noAccess": "§cERROR: Insufficient permissions to view",
            "gui.manager.setInfo.button": "§lSet basic information",
            "gui.manager.application.button": "§l§2Recruitment application",
            "gui.manager.other.button": "§lOther",
            "gui.manager.transference.button": "§l§cUnion Transfer",
            "gui.manager.dissolution.button": "§l§cDissolve the alliance",
            "gui.manager.setInfo.name.input": "§a# Union's Name",
            "gui.manager.setInfo.discription.input": "§a# Discription(line break:\\n)",
            "gui.manager.setInfo.medal.input": "§d# Union's Medal",
            "gui.manager.setInfo.icon.input": "§e# Union's icon",
            "gui.manager.setInfo.name.error.notEmpty": "§cERROR: Union's name cannot be empty",
            "gui.manager.setInfo.name.error.tooLong": "§cERROR The Union's name is too long ({} > {})",
            "gui.manager.setInfo.discription.error.tooLong": "§cERROR The Union's description is too long ({} > {})",
            "gui.manager.setInfo.success.log": "<{}> §eUpdated union's information\n§aName:§r {}§r\n§aDiscription:§r {}§r\n§aMedal:§r {}\n§aIcon:§r {}",
            "gui.manager.setInfo.success.tip": "Updated union's basic information",
            "gui.manager.setInfo.success": "<{}> §aupdated union's basic information",
            "gui.manager.recruitment.title": "§2§lUnion recruitment",
            "gui.manager.recruitment.acceptAll": "§2§lStatus: Accept all",
            "gui.manager.recruitment.refuseAll": "§c§lStatus: Refuse all",
            "gui.manager.recruitment.setNotice.button": "§l>> Set up Notice <<",
            "gui.manager.recruitment.change.refuseAll": "<{}> §eset the recruitment status to: §cRefuse all",
            "gui.manager.recruitment.change.acceptAll": "<{}> §eset the recruitment status t: §cAccept all",
            "gui.manager.recruitment.setNotice.title": "§lSet up Notice",
            "gui.manager.recruitment.setNotice.tip": "§b# Notice§7(line break:\\n)",
            "gui.manager.recruitment.setNotice.error.tooLong": "§cERROR: The description of the recruitment announcement is too long ({} > {})",
            "gui.manager.recruitment.setNotice.success": "<{}> §eupdated the recruitment announcement\n§r{}§r",
            "gui.manager.recruitment.setNotice.success.tip": "Updated recruitment announcement",
            "gui.manager.recruitment.application.name": "§l{}'s application",
            "gui.manager.recruitment.application.content": "\n§b# Applicant:§r §a{}§r\n§7# Time: {}§r\n§e# Reason:§r {}\n\n ",
            "gui.manager.recruitment.application.accept": "§2§lAccept",
            "gui.manager.recruitment.application.refuse": "§c§lRefuse",
            "gui.manager.recruitment.application.error": "§cERROR: The player has joined another union and the application has been withdrawn by the system",
            "gui.manager.recruitment.application.success": "<{}> §ajoined the union:§r {} §7| Processed by: <{}>",
            "gui.manager.recruitment.application.broascast": "<{}> §ajoined the union:§r {}",
            "gui.manager.recruitment.application.success.tip": "§aAgreed to §r<{}>§a's application to join the §r{}",
            "gui.manager.recruitment.application.faild": "§cYour application to join the §r{}§c has been rejected §7| Processed by: <{}>",
            "gui.manager.recruitment.application.faild.1": "<{}>§c's application to join the union has been rejected §7| Processed by: <{}>",
            "gui.manager.recruitment.application.faild.tip": "§c§r<{}> §chas been refused to join the §r{}",
            "gui.manager.other.title": "§lOther settings",
            "gui.manager.other.friendlyFire.switch": "Friendly Fire",
            "gui.manager.other.friendlyFire.title": "§lFriendly Fire",
            "gui.manager.other.friendlyFire.open": "<{}> §ahas activated teammate damage",
            "gui.manager.other.friendlyFire.close": "<{}> §7has turned off teammate damage",
            "gui.manager.other.friendlyFire.open.tip": "Teammate damage has been activated",
            "gui.manager.other.friendlyFire.close.tip": "Teammate damage has been turned off",
            "gui.manager.transference.title": "§c§lUnion's Transfer",
            "gui.manager.transference.content": "\n§cTransfer of Union Leader's Authority:\n",
            "gui.manager.transference.error.lord": "§cERROR: {} was originally the leader of the union",
            "gui.manager.transference.error.notlord": "§cERROR: {} does not have permission to create a union. Please contact the server administrator",
            "gui.manager.transference.confirm": "§cConfirm the transfer of the union leader's authority to:§r {}?",
            "gui.manager.transference.success": "<{}> §chas transferred the union leader to§r {}",
            "gui.manager.transference.success.tip": "Transfer successful",
            "gui.manager.dissolution.title": "§c§lDissolve",
            "gui.manager.dissolution.error.notEmptyMoney": "§cERROR: The union's money has not been cleared and cannot be dissolve",
            "gui.manager.dissolution.error.noAccess": "§cERROR: Only the union's leader or server administrator can dissolve the union",
            "gui.manager.dissolution.confirm": "§c§lConfirm the dissolution of the union: {}?\n\n All players are removed from the union\nThe union's data will be completely cleared and cannot be restored",
            "gui.manager.dissolution.success": "§cAlliance§r {} §r§chas been dissolved by §a <{}> §c",
            "gui.manager.dissolution.success.tip": "Dissolved Union {}",
            "gui.manager.dissolution.success.broadcast": "§cAlliance§r {} §r§chas been dissolved by§a <{}> §c | {}",
            "gui.bill.bank.today": "§3§lToday's Wealth Record",
            "gui.bill.bank.history": "§3§lHistorical billing",
            "gui.bill.construct.today": "§c§lToday's score",
            "gui.bill.construct.history": "§c§lHistorical Score",
            "gui.bill.notice.today": "§lToday's notification",
            "gui.bill.notice.history": "§lHistorical notifications",
            "gui.bill.EnterSerialNumber": "§2§lEnter serial number",
            "gui.bill.bank.notice.plus": "§e[+{}]§r <{}> §edeposited\n§r§7Note:§r {}\n§r§7Time: {}",
            "gui.bill.bank.notice.minus": "§b[{}]§r <{}> §bwithdrew\n§r§7Reason:§r {}\n§r§7Time: {}",
            "gui.bill.construct.notice.plus": "§e[+{}]§r donated by <{}>\n§r§7Note:§r {}\n§r§7Time: {}",
            "gui.bill.construct.notice.minus": "§b[{}]§r withdrew by <{}>\n§r§7Note:§r {}\n§r§7Time: {}",
            "gui.union.name": " §a# Name:§r {}\n§r",
            "gui.union.level": " §a# Level:§r {}\n§r",
            "gui.union.money": " §b# Wealth:§r {} {}\n§r",
            "gui.union.lord": " §d# Lord:§r {}\n§r",
            "gui.union.creator": " §7# Creator: {}\n§r",
            "gui.union.creationTime": " §7# Creation Time: {}\n§r",
            "gui.union.discription": " §7# Discription:§r {}",
            "gui.cmd.union": "§bUnion System",
            "gui.cmd.notOP": "You do not have permission to execute OP commands",
            "gui.cmd.union.lord.success": "<{}> has obtained the permission to create a union",
            "gui.cmd.union.lord.faild": "<{}> has lost the permission to create a union",
            "gui.cmd.union.lord.noPlayer": "No player found, please enter the game first",
            "gui.cmd.onlyOP": "Only allow players in game to execute",
            "gui.cmd.umsg": "§bUnionChat",
            "gui.api.money.faild": "Economic API acquisition failed. Please check the status of the economic plugin and the economic configuration of this plugin",
            "gui.end.close": "§6§lClose Gui",
            "gui.end.lastPage": "§lLast Page"
        }
    };
    for (let key in default_lang.en_US){
        if (key != '#VERSION' && key != '#1'){
            default_lang.en_US[key] = '§小'+default_lang.en_US[key];
        }
    }
    i18n.load(`${PATH.root}/lang/language.json`, 'zh_CN', default_lang); 
}


// 函数导出
function getUnionData(guid){
    return loadUnion(CONF.UNIONSDATA.get(guid));
}
function getPlayerData(xuid){
    return loadPlData(CONF.PLAYERDATA.get(xuid));
}
function getMedal(xuid, brackets = false){
    let medal = loadUnion(CONF.UNIONSDATA.get(loadPlData(CONF.PLAYERDATA.get(xuid))?.unionGuid??''))?.medal;
    if (brackets){
        if (medal != undefined){
            return `§r[${medal}§r]`;
        }else{
            return '';
        }
    }else{
        if (medal != undefined){
            return medal;
        }else{
            return '';
        }
    }
}
function getPlayerUnionLevel(xuid){
    let score = loadUnion(CONF.UNIONSDATA.get(loadPlData(CONF.PLAYERDATA.get(xuid))?.unionGuid??''))?.score.blockPlace;
    if (score != undefined){
        return String(Math.floor(score/20000));
    }else{
        return '';
    }
}


ll.exports(getUnionData, 'WnUnion', 'getUnionData');                 // 根据联盟guid, 获取联盟信息, 自行查看loadUnion函数返回对象
ll.exports(getPlayerData, 'WnUnion', 'getPlayerData');               // 根据玩家xuid, 获取玩家信息, 自行查看loadPlData函数返回对象
ll.exports(getMedal, 'WnUnion', 'getMedal');                         // 获取玩家的联盟勋章. 默认不带白括号
ll.exports(saveUnion, 'WnUnion', 'getPlayerData');                   // 写入联盟信息（自定义修改数据后写入）

ll.exports(sendPlayerNotice, 'WnUnion', 'sendPlayerNotice');         // 发送玩家通知
ll.exports(sendUnionNotice, 'WnUnion', 'sendUnionNotice');           // 群发成员通知
ll.exports(addUnionLog, 'WnUnion', 'addUnionLog');                   // 记录联盟日志

ll.exports(addUnionMoney, 'WnUnion', 'addUnionMoney');               // 增加联盟财富
ll.exports(addPlayerConstruct, 'WnUnion', 'addPlayerConstruct');     // 增加个人建设度
ll.exports(addUnionConstruct, 'WnUnion', 'addUnionConstruct');       // 增加联盟建设度（联盟升级）

ll.exports(getMoney, 'WnUnion', 'getMoney');
ll.exports(addMoney, 'WnUnion', 'addMoney');
ll.exports(reduceMoney, 'WnUnion', 'reduceMoney');
ll.exports(setMoney, 'WnUnion', 'setMoney');





/* 
导入示例:
let WnUnion = {};
mc.listen('onServerStarted', ()=>{
    if (ll.hasExported('WnUnion', 'getUnionData')){
        WnUnion.getUnionData = ll.imports('WnUnion', 'getUnionData');
        WnUnion.getPlayerData = ll.imports('WnUnion', 'getPlayerData');
        WnUnion.getMedal = ll.imports('WnUnion', 'getMedal');
        WnUnion.saveUnion = ll.imports('WnUnion', 'saveUnion');
        WnUnion.sendPlayerNotice = ll.imports('WnUnion', 'sendPlayerNotice');
        WnUnion.sendUnionNotice = ll.imports('WnUnion', 'sendUnionNotice');
        WnUnion.addUnionLog = ll.imports('WnUnion', 'addUnionLog');
        WnUnion.addUnionMoney = ll.imports('WnUnion', 'addUnionMoney');
        WnUnion.addPlayerConstruct = ll.imports('WnUnion', 'addPlayerConstruct');
        WnUnion.addUnionConstruct = ll.imports('WnUnion', 'addUnionConstruct');
        WnUnion.getMoney = ll.imports('WnUnion', 'getMoney');
        WnUnion.addMoney = ll.imports('WnUnion', 'addMoney');
        WnUnion.reduceMoney = ll.imports('WnUnion', 'reduceMoney');
        WnUnion.setMoney = ll.imports('WnUnion', 'setMoney');
    }else{
        logger.error(`WnUnion API获取失败, 请检查经WnUnion插件状态`);
    }
}); 

// 聊天头衔调用
mc.listen('onChat', (pl, msg)=>{
    mc.broadcast(`[${WnUnion.getMedal(pl.xuid)}§r]<${pl.realName}> ${msg}`)
})
*/