猫史档案馆


【Box3】全线战争-阵地战 源代码

用户:sky-*^O^*sky-*^O^*查看:5 回复:5 评论:5 创建时间:2023-08-21T10:00:19


console.clear();

world.breakVoxelSound.sample = '';//关闭打破方块音效,打破声音由脚本实现
world.placeVoxelSound.sample = '';//关闭放置方块音效,放置声音由脚本实现

voxels.setVoxel(127, 62, 127, 'barrier');
voxels.setVoxel(128, 62, 127, 'barrier');
voxels.setVoxel(127, 62, 128, 'barrier');
voxels.setVoxel(128, 62, 128, 'barrier');

world.addCollisionFilter('.red_bullet', '.red_player');
world.addCollisionFilter('.blue_bullet', '.blue_player');
world.addCollisionFilter('.shell', 'player');
world.addCollisionFilter('.喵oke_bomb', 'player');

world.lunarPhase = 0.75;
world.lightMode = 'nature';
world.sunFrequency = 1 / 9600;//10分钟一天
world.sunPhase = 0.125;
world.skyTopLight = new GameRGBColor(3, 3, 3);

const userStorage = storage.getDataStorage('users');

const Quat = new GameQuaternion(0, 0, 0, 1);

const BLOCK_DEFENSE_ABILITY = {
    'grass': 300,
    'dirt': 300,
    'light_gray': 1000,//混凝土
    'warm_yellow_light': 50,//灯
    'lab_material_03': 4000,//金属砖
}

const TEAM_CONFIG = {
    'red': {//进攻
        rgbColor: new GameRGBColor(1, 0, 0),
        rgbaColor: new GameRGBAColor(1, 0, 0, 1),
    },
    'blue': {//防御
        rgbColor: new GameRGBColor(0, 0, 1),
        rgbaColor: new GameRGBAColor(0, 0, 1, 1)
    }
};

const TEAM_C_E_TABLE = {
    '红队': 'red',
    '蓝队': 'blue'
};

const TEAM_E_C_TABLE = {
    'red': '红队',
    'blue': '蓝队'
};

const TAG_ENABLE_DAMAGE = 'enable_damage';

const DEFAULT_ITEM = '喵';
const ITEMS = {
    '喵': {
        mesh: 'mesh/喵.vb',
        orientation: Quat.rotateX(-Math.PI / 2),
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, 0, 0.6),
        money: 0,
        use: chop,
        interval: 1,
        speed: 0.3,
        sound: 'audio/break_block.mp3',
    },
    '喵': {
        mesh: 'mesh/喵.vb',
        orientation: Quat.rotateZ(-Math.PI / 2),
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, -0.45, 0.3),
        money: 5,
        use: fireBullet('mesh/子弹.vb', 5, 40),
        interval: 3,
        speed: 0.35,
        sound: 'audio/gunshot.mp3',
    },
    '喵': {
        mesh: 'mesh/喵.vb',
        orientation: Quat,
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, -0.2, 0),
        money: 15,
        use: fireBullet('mesh/子弹.vb', 10, 70),
        interval: 8,
        speed: 0.25,
        sound: 'audio/gunshot.mp3',
    },
    '狙击喵': {
        mesh: 'mesh/狙击喵.vb',
        orientation: Quat,
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, 0, 0),
        money: 50,
        use: fireBullet('mesh/子弹.vb', 20, 150),
        interval: 48,
        speed: 0.2,
        sound: 'audio/gunshot.mp3',
    },
    '迫击炮': {
        mesh: 'mesh/迫击炮.vb',
        orientation: Quat,
        scale: new GameVector3(0.2, 0.2, 0.2),
        offset: new GameVector3(0, 0, 0),
        money: 300,
        use: fireShell,
        interval: 80,
        speed: 0.1,
        sound: 'audio/gunshot.mp3',
    },
    '火箭筒': {
        mesh: 'mesh/火箭筒.vb',
        orientation: Quat,
        scale: new GameVector3(0.2, 0.2, 0.2),
        offset: new GameVector3(0, 0, 0),
        money: 800,
        use: fireRocket,
        interval: 128,
        speed: 0.15,
        sound: 'audio/gunshot.mp3',
    },
    '水雷': {
        mesh: 'mesh/水雷.vb',
        orientation: Quat,
        scale: new GameVector3(0.2, 0.2, 0.2),
        offset: new GameVector3(0, 0, 0),
        money: 40,
        use: useMine,
        interval: 48,
        speed: 0.25,
        sound: 'audio/place_block.mp3',
    },
    '铁锹': {
        mesh: 'mesh/铁锹.vb',
        orientation: Quat,
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, 0, 0),
        money: 0,
        use: (entity, raycast) => {
            if (entity.position.distance(raycast.hitPosition) > 6) {
                entity.player.directMessage('距离太远!');
                return false;
            }
            let pos = raycast.voxelIndex.clone();
            destroyVoxel(pos.x, pos.y, pos.z, 300);
            return true;
        },
        interval: 0,
        speed: 0.35,
        sound: 'audio/break_block.mp3',
    },
    '混凝土': {
        mesh: 'mesh/混凝土.vb',
        scale: new GameVector3(1, 1, 1),
        offset: new GameVector3(0, 0, 0),
        money: 2,
        use: putVoxel('light_gray'),
        interval: 0,
        speed: 0.25,
        sound: 'audio/place_block.mp3',
    },
    '金属砖': {
        mesh: 'mesh/金属砖.vb',
        scale: new GameVector3(1, 1, 1),
        offset: new GameVector3(0, 0, 0),
        money: 40,
        use: putVoxel('lab_material_03'),
        interval: 0,
        speed: 0.15,
        sound: 'audio/place_block.mp3',
    },
    '灯': {
        mesh: 'mesh/灯.vb',
        scale: new GameVector3(1, 1, 1),
        offset: new GameVector3(0, 0, 0),
        money: 20,
        use: putVoxel('warm_yellow_light'),
        interval: 0,
        speed: 0.25,
        sound: 'audio/place_block.mp3',
    },
    // '喵': {
    //     mesh: 'mesh/喵.vb',
    //     orientation: Quat.rotateZ(-Math.PI / 2),
    //     scale: new GameVector3(0.25, 0.25, 0.25),
    //     offset: new GameVector3(0, 0, 0),
    //     money: 5,
    //     use: useSmokeBomb,
    //     interval: 0,
    //     speed: 0.35,
    // },
    '信号喵': {
        mesh: 'mesh/信号喵.vb',
        orientation: Quat.rotateZ(-Math.PI / 2),
        scale: new GameVector3(0.5, 0.5, 0.5),
        offset: new GameVector3(0, -0.4, 0.1),
        money: 5,
        use: fireSignalGunBullet,
        interval: 0,
        speed: 0.35,
        sound: 'audio/gunshot.mp3',
    },
};

const GAME_MAXTIME = 30 * 60 * 1000;//游戏最长时间,单位秒
const GAME_HALFTIME = 20;//两局游戏间隔时间,单位秒

const DEFAULT_PLAYER_DATA = {
    money: 4000,
    experience: 4000,
    killingNumber: 0,
    causingDamage: 0,
}

const Bounds = new GameBounds3(new GameVector3(-50, -50, -50), new GameVector3(305, 114, 305));

var StartTime = new Date();

var Tea喵amage = {};
var TeamNum = {};

var IsGameInProgress = true;

var PlayerState = {};
var PlayerData = {};

var FlagConfig = {
    '0A': 'red',
    '1A': 'blue',
    '1B': 'blue',
    '1C': 'blue',
    '1D': 'blue',
    '1E': 'blue',
    '2A': 'blue',
    '2B': 'blue',
    '2C': 'blue',
    '2D': 'blue',
    '2E': 'blue',
    '3A': 'blue',
    '3B': 'blue',
    '3C': 'blue',
    '3D': 'blue',
    '3E': 'blue',
    '4A': 'blue',
    '4B': 'blue',
    '4C': 'blue',
    '4D': 'blue',
    '4E': 'blue',
    '5A': 'blue',
    '5B': 'blue',
    '5C': 'blue',
    '5D': 'blue',
    '5E': 'blue',
};

var RedTeamPermanentDefenseLine = ['0A'];

var BlueTea喵efenseLine = 0;

Tea喵amage = objectMap(TEAM_CONFIG, () => {
    return { damage: 0, deathToll: 0 }
});
TeamNum = objectMap(TEAM_CONFIG, () => 0);

for (let v of Object.keys(FlagConfig)) {
    let e = world.querySelector(`.${v}`);
    e.meshColor = TEAM_CONFIG[FlagConfig[v]].rgbaColor;
    e.enableInteract = true;
    e.interactRadius = 2;
    e.interactHint = v;
    e.onInteract(({ entity }) => {
        if (entity.hasTag('red')) {
            if (getFlagTeam(v) === 'red') {
            } else if (getFlagTeam(v) === 'blue') {
                setFlagTeam(v, 'red');
                world.say(`@${entity.player.name}(红队)占领了${v}`);
            }
        } else if (entity.hasTag('blue')) {
            if (RedTeamPermanentDefenseLine.includes(v)) {
                entity.player.directMessage('此点已被红队永久占领!');
                return;
            } else {
                if (getFlagTeam(v) === 'blue') {
                } else if (getFlagTeam(v) === 'red') {
                    setFlagTeam(v, 'blue');
                    world.say(`@${entity.player.name}(蓝队)夺回了${v}`);
                }
            }
        }
        checkLineOfDefense();
    });
}

var timeOut = setTimeout(() => {
    win('blue');
}, GAME_MAXTIME);

let zone = world.addZone({
    bounds: Bounds,
    selector: '.bullet',
});

zone.onLeave(({ entity }) => {
    entity.destroy();
});

async function welcome(entity, team) {
    let t = '';
    if (team === 'red') {
        t = '红队的勇士,蓝队的援军3天后就会到达,你的目标是在3天(30min)内攻破蓝队五道防线\n\n详情见右键帮助';
    } else if (team === 'blue') {
        t = '蓝队的勇士,蓝队的援军3天后就会到达,你的目标是在3天(30min)内守住蓝队五道防线\n\n详情见右键帮助';
    }
    entity.player.dialog({
        type: GameDialogType.TEXT,
        title: "帮助",
        content: t,
    });
}

world.onPlayerJoin(async ({ entity }) => {
    entity.player.showName = true;
    entity.enableDamage = true;
    entity.collides = false;
    entity.enableInteract = true;
    entity.interactRadius = 6;
    await loadUserData(entity);
    let t = selectTeam();
    joinTeam(entity, t);
    world.say(`@${entity.player.name}加入了${TEAM_E_C_TABLE[t]}`);
    await gui.init(entity, {
        "panel": {
            display: true,
            data: `<dialog width="300" percentHeight="100" top="60" right="20" id="">
            <group percentWidth="100" height="170" y="0">
                <label text="玩家状态" height="35" y="5"  x="10"  percentWidth="100" color="#fff" fontSize="20"></label>
                <label text="" height="25" y="35" x="10" percentWidth="100" color="#fff" id="money"></label>
                <label text="" height="25" y="60" x="10" percentWidth="100" color="#fff" id="experience"></label>
                <label text="" height="25" y="85" x="10" percentWidth="100" color="#fff" id="hp"></label>
                <label text="" height="25" y="110" x="10" percentWidth="100" color="#fff" id="akillingnumber"></label>
                <label text="" height="25" y="135" x="10" percentWidth="100" color="#fff" id="acausingdamage"></label>
            </group>

            <group percentWidth="100" height="145" y="180">
                <label text="物品信息" height="35" y="5"  x="10"  percentWidth="100" color="#fff" fontSize="20"></label>
                <label text="" height="25" y="35" x="10" percentWidth="100" color="#fff" id="item"></label>
                <label text="" height="25" y="60" x="10" percentWidth="100" color="#fff" id="imoney"></label>
                <label text="" height="25" y="85" x="10" percentWidth="100" color="#fff" id="iinterval"></label>
                <label text="" height="25" y="110" x="10" percentWidth="100" color="#fff" id="ispeed"></label>
            </group>

            <group percentWidth="100" height="70" y="335">
                <label text="战争计时器" height="35" y="5"  x="10"  percentWidth="100" color="#fff" fontSize="20"></label>
                <label text="" height="25" y="35" x="10" percentWidth="100" color="#fff" id="timer"></label>
            </group>

            <group percentWidth="100" height="220" y="415" display="none" id="battlefieldreport">
                <label text="战报" height="35" y="5"  x="10"  percentWidth="100" color="#fff" fontSize="20"></label>
                <label text="个人↓:" height="25" y="35" x="10" percentWidth="100" color="#fff"></label>
                <label text="" height="25" y="60" x="10" percentWidth="100" color="#fff" id="gkillingnumber"></label>
                <label text="" height="25" y="85" x="10" percentWidth="100" color="#fff" id="gcausingdamage"></label>
                <label text="队伍↓:" height="25" y="110" x="10" percentWidth="100" color="#fff"></label>
                <label text="" height="25" y="135" x="10" percentWidth="100" color="#fff" id="winningteam"></label>
                <label text="" height="25" y="160" x="10" percentWidth="100" color="#fff" id="damage"></label>
                <label text="" height="25" y="185" x="10" percentWidth="100" color="#fff" id="deathtoll"></label>
            </group>
        </dialog>`
        }
    });
    updateItemState(entity, PlayerState[entity.player.name].item);
    welcome(entity, t);

    entity.player.onPress(({ tick, entity, button, raycast }) => {
        if (entity.player.dead || !IsGameInProgress) return;
        // 判断是否是鼠标左键, 击中实体是否是玩家, 以及击中的玩家是否喵
        if (button === 'action0') {
            const state = PlayerState[entity.player.name];
            const data = PlayerData[entity.player.name];

            const item = ITEMS[state.item]; // 获取物品信息
            if (item.money > data.money) {
                entity.player.directMessage('钱不够!');
                return;
            }
            if (state.shootTick + item.interval > tick) {
                entity.player.directMessage('正在换弹,无法操作!');
                return;
            } // 小于物品间隔事件不处理
            let ok = item.use(entity, raycast);
            if (ok) {
                const sound = {
                    sample: item.sound,
                    radius: 128,
                    gain: 1,
                    pitch: 1,
                    gainRange: 0,
                    pitchRange: 0,
                };
                entity.sound(sound);
                data.money -= item.money;
                state.shootTick = tick; // 更新射击时间(tick)
            }
        } else if (button === 'action1') {
            (async () => {
                const result = await entity.player.dialog({
                    type: GameDialogType.SELECT,
                    title: '菜单',
                    content: '请进行操作:',
                    options: ['背包', '团队频道', '保存', '帮助'],
                });

                if (!result || result.value === '') {
                    return;
                }

                switch (result.value) {
                    case '背包':
                        const bag = await entity.player.dialog({
                            type: GameDialogType.SELECT,
                            title: '背包',
                            content: '请选择物品:',
                            options: Object.keys(ITEMS),
                        });

                        if (!bag || bag.value === '') {
                            return;
                        }

                        wearItem(entity, bag.value);
                        break;
                    case '团队频道':
                        let result = await entity.player.dialog({
                            type: GameDialogType.INPUT, title: "私聊", content: '你想说什么?', confirmText: '发送'
                        });

                        if (!result || result === '') {
                            return;
                        }
                        let team = getTeam(entity);
                        world.querySelectorAll('player').forEach((e) => {
                            if (getTeam(e) === team) {
                                e.player.directMessage(`@${entity.player.name}在团队频道中说:${result}`);
                            }
                        });
                        break;
                    case '保存':
                        save(entity);
                        break;
                    case '帮助':
                        help(entity);
                        break;
                }
            })()
        }
    });

    entity.onDie(async ({ entity, attacker }) => {
        world.say(`@${attacker.player.name}(${TEAM_E_C_TABLE[getTeam(attacker)]})击杀了@${entity.player.name}(${TEAM_E_C_TABLE[getTeam(entity)]})`);
        entity.player.directMessage(`您已喵,直接击杀者@${attacker.player.name},10秒后复活。`);
        let data = PlayerData[attacker.player.name];
        let state = PlayerState[attacker.player.name];
        data.killingNumber += 1;
        state.killingNumber += 1;
        addExperienceAndMoney(entity.player.name, 2000);
        Tea喵amage[getTeam(entity)].deathToll += 1;
        await sleep(10 * 1000);
        respawn(entity);
    });

    entity.onInteract(async ({ entity, targetEntity }) => {
        const result = await entity.player.dialog({
            type: GameDialogType.SELECT, title: "互动", content: '你想做什么?', options: ['给钱', '私聊']
        });

        if (!result || result.value === '') {
            return;
        }

        switch (result.value) {
            case '给钱':
                let data = PlayerData[entity.player.name];
                let tData = PlayerData[targetEntity.player.name];
                let r1 = await entity.player.dialog({
                    type: GameDialogType.INPUT, title: "转账", content: '你想给多少钱?', confirmText: '确定'
                });

                if (!r1 || r1 === '') {
                    return;
                }

                let num = Math.abs(Math.ceil(parseFloat(r1)));
                if (data.money < num) {
                    entity.player.directMessage('钱不够!');
                    return;
                }
                data.money -= num;
                tData.money += num;
                entity.player.directMessage('转账成功!');
                targetEntity.player.directMessage(`${entity.player.name} 给你了${num}元,请查收!`);
                break;
            case '私聊':
                let r2 = await entity.player.dialog({
                    type: GameDialogType.INPUT, title: "私聊", content: '你想说什么?', confirmText: '发送'
                });

                if (!r2 || r2 === '') {
                    return;
                }

                targetEntity.player.directMessage(`${entity.player.name} 对你说:${r2}`);
                break;
            default:
        }
    });
});

world.onPlayerLeave(({ entity }) => {
    save(entity);
    TeamNum[getTeam(entity)] -= 1;
    delete PlayerState[entity.player.name];
    delete PlayerData[entity.player.name];
});

world.onTick(() => {
    let now = new Date();
    let deltaT = now.getTime() - StartTime.getTime();
    let t = `${pad((~~(deltaT / 1000 / 60)) % 60, 2)}:${pad((~~(deltaT / 1000)) % 60, 2)}`;
    Object.keys(PlayerState).forEach(async (name) => {
        let state = PlayerState[name];
        let entity = state.entity;
        let data = PlayerData[name];
        gui.setAttribute(entity, "#money", "text", `钱:${data.money}`);
        gui.setAttribute(entity, "#experience", "text", `经验:${data.experience}`);
        gui.setAttribute(entity, "#hp", "text", `生命:${entity.hp}`);
        gui.setAttribute(entity, "#akillingnumber", "text", `累计击杀:${data.killingNumber}`);
        gui.setAttribute(entity, "#acausingdamage", "text", `累计造成伤害:${data.causingDamage}`);
        gui.setAttribute(entity, "#timer", "text", t);
        entity.interactHint=`经验:${data.experience}`;
    });
});

function pad(num, n) {
    let tbl = [];
    return (0 >= (n = n - num.toString().length)) ? num : (tbl[n] || (tbl[n] = Array(n + 1).join(0))) + num;
}

async function help(entity) {
    const result = await entity.player.dialog({
        type: GameDialogType.SELECT,
        title: "帮助",
        content: `          帮助
作品介绍↓:
本作品是一个战争游戏。
红队为攻方,蓝队为守方。
只有团结一心,与队友充分交流合作,才有可能赢得比赛。

操作说明↓:
右键背包选择物品,左键使用,击杀其他队伍人员获得经验和钱,每一场战争3天(30min),战后有2000¥奖励,战胜队伍有5000¥额外奖励。
占领防线方法:红队在旗帜旁按E键,夺取旗帜;蓝队在旗帜旁按E键,夺回旗帜。如果防线被攻破,则防线上的旗帜无法夺回。
红队目标:在3天(30min)内攻破蓝队全部五道防线。
蓝队目标:在3天(30min)内守住防线。

操作方法↓:
- 喵:
1.用混凝土搭建楼梯
2.用爆炸性武器挖坑,从地下松软的泥土层过去(第五道防线墙不可用)
3.用爆炸性武器炸开防御墙(成本高)
4.从旁边池塘绕过去(仅第一道防线墙可用,且对方可能布设水雷,很危险)

- 防守阵地
1.用水雷阻止敌方水上进攻路线
2.修理/搭建防火墙

- 狙击技巧
1.站在高处,最好是高塔上
2.注意隐蔽自己
3.枪口往上抬一点,以应对子弹下落

技巧↓:
    1.地道战
    2.挖坑埋人
    3.迫击炮/火箭弹破墙
    4.见信号弹行动
    5.密集水雷网
    6.拆高塔楼梯
    7.在敌方旗帜旁放置灯箱,方便袭击重生人员
    8.以灯箱为标记,定点火力集中轰炸敌方旗帜部位防护墙
    9.只要你不靠近敌人,敌人的砍刀就砍不到你

物品属性↓:
→喵:
    钱:5
    间隔(tick):3
    玩家行动速度:0.35
→步枪:
    钱:15
    间隔(tick):8
    玩家行动速度:0.25
→狙击步枪:
    钱:50
    间隔(tick):48
    玩家行动速度:0.2
→迫击炮:
    钱:300
    间隔(tick):80
    玩家行动速度:0.1
→火箭筒:
    钱:800
    间隔(tick):128
    玩家行动速度:0.15
→水雷:
    钱:40
    间隔(tick):48
    玩家行动速度:0.25
→铁锹:
    钱:0
    间隔(tick):0
    玩家行动速度:0.35
→混凝土:
    钱:2
    间隔(tick):0
    玩家行动速度:0.25
→金属砖:
    钱:40
    间隔(tick):0
    玩家行动速度:0.15
→灯:
    钱:20
    间隔(tick):0
    玩家行动速度:0.25
→信号枪:
    钱:5
    间隔(tick):0
    玩家行动速度:0.35

方块防御能力↓:
→泥土:300
→混凝土:1000
→灯:50
→金属砖:4000

有bug、建议在评论区里留言哦!
`,
        options: ['查看详情'],
    });

    if (!result || result.value === '') {
        return;
    }

    if (result.value === '查看详情') {
        entity.player.link('https://shequ.codemao.cn/community/559151');
    }
}

function getFlags(team) {
    let flags = [];
    for (let k of Object.keys(FlagConfig)) {
        let v = FlagConfig[k];
        if (v === team) {
            flags.push(k);
        }
    }
    return flags;
}

async function respawn(entity) {
    while (1) {
        const result = await entity.player.dialog({
            type: GameDialogType.SELECT,
            title: '重生',
            content: `请选择重生点:`,
            options: getFlags(getTeam(entity)),
        });

        if (!result || result.value === '') {
            continue;
        }

        entity.player.spawnPoint = world.querySelector('.' + result.value).position.clone();
        entity.player.forceRespawn();
        entity.hp = entity.maxHp;
        entity.collides = true;
        break;
    }
}

function joinTeam(entity, team) {
    TeamNum[team]++; // 队伍人数+1
    // 设置玩家属性
    entity.enableDamage = true;
    entity.player.color = TEAM_CONFIG[team].rgbColor;
    entity.addTag(`${team}_player`);
    entity.addTag(team);
    entity.addTag(TAG_ENABLE_DAMAGE);
    entity.interactColor=TEAM_CONFIG[team].rgbColor;
    // 初始化玩家状态
    PlayerState[entity.player.name] = {
        entity: entity,
        team: team,
        shootTick: 0,
        killingNumber: 0,
        causingDamage: 0,
    };
    wearItem(entity, DEFAULT_ITEM);

    respawn(entity);
}

function removeAllRightHandWearable(entity) {
    const rightHandWears = entity.player.wearables(GameBodyPart.RIGHT_HAND);
    entity.player.removeWearable(rightHandWears[0]);
}

function updateItemState(entity, item) {
    let i = ITEMS[item];
    gui.setAttribute(entity, "#item", "text", `物品:${item}`);
    gui.setAttribute(entity, "#imoney", "text", `价格:${i.money}`);
    gui.setAttribute(entity, "#iinterval", "text", `间隔:${i.interval}`);
    gui.setAttribute(entity, "#ispeed", "text", `行动速度:${i.speed}`);
}

function wearItem(entity, item) {
    removeAllRightHandWearable(entity);
    let i = ITEMS[item];
    PlayerState[entity.player.name].item = item;
    PlayerState[entity.player.name].shootTick = world.currentTick;
    entity.player.addWearable({
        bodyPart: GameBodyPart.RIGHT_HAND,
        mesh: i.mesh,
        orientation: i.orientation,
        scale: i.scale,
        offset: i.offset,
    });

    updateItemState(entity, item);

    entity.player.walkSpeed = i.speed;
    entity.player.runSpeed = i.speed;
    entity.player.crouchSpeed = i.speed;
}

function selectTeam() {
    if (TeamNum['red'] > TeamNum['blue']) {
        return 'blue'
    } else {
        return 'red'
    }
}

function getFlagTeam(flagName) {
    return FlagConfig[flagName];
}

function setFlagTeam(flagName, team) {
    if (RedTeamPermanentDefenseLine.includes(flagName) && team === 'blue') {
        log_warning('cannot occupy this point' + flagName);
        return;
    }
    FlagConfig[flagName] = team;
    let entity = world.querySelector(`.${flagName}`);
    entity.meshColor = TEAM_CONFIG[team].rgbaColor;
    entity.removeTag(theOtherTeam(team));
    entity.addTag(team);
}

function getTeam(entity) {
    return PlayerState[entity.player.name].team;
}

function checkLineOfDefense() {
    let num = 0;
    let sum = 0;
    for (let i of Object.keys(FlagConfig)) {
        let t = FlagConfig[i];
        if (i.includes(`${BlueTea喵efenseLine + 1}`)) {
            if (t === 'red') {
                num += 1;
            }
            sum += 1;
        }
    }

    if (num === sum) {
        BlueTea喵efenseLine += 1;
        if (BlueTea喵efenseLine === 5) {
            win('red');
            return;
        }
        for (let i of Object.keys(FlagConfig)) {
            if (i.includes(`${BlueTea喵efenseLine}`)) {
                RedTeamPermanentDefenseLine.push(i);
            }
        }
        world.say(`第${BlueTea喵efenseLine}道防线被攻破!`);
    }
}

function chop(entity, raycast) {
    if (entity.position.distance(raycast.hitPosition) > 5) {
        entity.player.directMessage('距离太远!');
        return false;
    }
    if (raycast.hitEntity && raycast.hitEntity.hasTag(theOtherTeam(getTeam(entity))) && raycast.hitEntity.hasTag(TAG_ENABLE_DAMAGE)) {
        hurt(entity, raycast.hitEntity, 50);
        return true;
    }
    return false;
}

function fireBullet(mesh, force, damage) {
    return (entity, raycast) => {
        let team = getTeam(entity);
        let e = world.createEntity({
            mesh: mesh,
            tags: [team, `${team}_bullet`, 'bullet'],
            position: entity.position.add(new GameVector3(0, 0.4, 0)),
            collides: true,
            gravity: true,
            friction: 1,
            restitution: 0,
            meshScale: new GameVector3(1 / 32, 1 / 32, 1 / 32),
            meshOrientation: Quat,
            particleRate: 100,
            particleLifetime: 1,
            particleSize: [5, 4, 3, 2, 1],
            particleColor: [
                new GameRGBColor(10, 9, 2),
                new GameRGBColor(5, 0.25, 0.1),
                new GameRGBColor(3, 0.05, 0.05),
                new GameRGBColor(0, 0, 0),
                new GameRGBColor(0, 0, 0),
            ],
        });

        let launcher = entity;

        e.onEntityContact(({ entity, other }) => {
            entity.destroy();
            if (other.hasTag(theOtherTeam(team)) && other.hasTag(TAG_ENABLE_DAMAGE)) {
                hurt(launcher, other, damage);
            }
        });

        e.onVoxelContact(({ x, y, z }) => {
            e.destroy();
            destroyVoxel(x, y, z, damage);
        });
        e.velocity = raycast.direction.clone().scale(force);

        return true;
    };
}

function fireSignalGunBullet(entity, raycast) {
    let team = getTeam(entity);
    // 创建子弹实体
    let e = world.createEntity({
        mesh: `mesh/信号喵弹丸_${team}.vb`,
        tags: [team, `${team}_bullet`, 'bullet'],
        position: entity.position.add(new GameVector3(0, 0.4, 0)),
        collides: false,
        gravity: true,
        meshScale: new GameVector3(1 / 32, 1 / 32, 1 / 32),
        velocity: raycast.direction.clone().scale(3),
        particleRate: 100,
        particleLifetime: 1,
        particleSize: [5, 4, 3, 2, 1],
        particleColor: [
            new GameRGBColor(10, 9, 2),
            new GameRGBColor(5, 0.25, 0.1),
            new GameRGBColor(3, 0.05, 0.05),
            new GameRGBColor(0, 0, 0),
            new GameRGBColor(0, 0, 0),
        ],
    });
    sleep(3 * 1000).finally(() => {
        e.destroy();
    });
    return true;
}

function vToQ(direction) {
    const dir = direction.clone();
    let dist = Math.sqrt(dir.x * dir.x + dir.z * dir.z);
    const rotx = Math.atan2(dir.y, dist);
    return Quat.rotateY(Math.PI / 2).rotateX(rotx).rotateY(Math.atan2(dir.z, dir.x));
}

function fireShell(entity, raycast) {//发射迫击炮弹
    let team = getTeam(entity);
    let e = world.createEntity({
        mesh: 'mesh/炮弹.vb',
        tags: [team, 'bullet', 'shell'],
        position: entity.position.add(new GameVector3(0, 0.4, 0)),
        collides: true,
        gravity: true,
        meshScale: new GameVector3(1 / 32, 1 / 32, 1 / 32),
        meshOrientation: vToQ(raycast.direction),
        velocity: raycast.direction.clone().scale(5),
    });

    let tick = world.onTick(() => {
        e.meshOrientation = vToQ(e.velocity);
    });

    e.onEntityContact(({ entity }) => {
        entity.destroy();
    });

    e.onVoxelContact(({ entity }) => {
        entity.destroy();
    });

    e.onDestroy(() => {
        tick.cancel();
        let pos = e.position.clone();

        explode(pos, team, entity);
    });
    return true;
}

function fireRocket(entity, raycast) {//发射喵
    let team = getTeam(entity);
    let e = world.createEntity({
        mesh: 'mesh/喵.vb',
        tags: [team, 'bullet', `${team}_bullet`,],
        position: entity.position.add(new GameVector3(0, 0.4, 0)),
        collides: true,
        gravity: false,
        meshScale: new GameVector3(1 / 32, 1 / 32, 1 / 32),
        meshOrientation: vToQ(raycast.direction),
        velocity: raycast.direction.clone().scale(5),
        particleRate: 100,
        particleLifetime: 1,
        particleSize: [5, 4, 3, 2, 1],
        particleColor: [
            new GameRGBColor(10, 9, 2),
            new GameRGBColor(5, 0.25, 0.1),
            new GameRGBColor(3, 0.05, 0.05),
            new GameRGBColor(0, 0, 0),
            new GameRGBColor(0, 0, 0),
        ],
    });

    e.onEntityContact(({ entity }) => {
        entity.destroy();
    });

    e.onVoxelContact(({ entity }) => {
        entity.destroy();
    });

    e.onDestroy(() => {
        let pos = e.position.clone();

        explode(pos, team, entity);
    });
    return true;
}

function useMine(entity, raycast) {
    if (entity.position.distance(raycast.hitPosition) > 20) {
        entity.player.directMessage('距离太远!');
        return false;
    }
    if (raycast.hitVoxel != voxels.id('water')) {
        entity.player.directMessage('只能在水上使用!');
        return false;
    }
    let pos = raycast.voxelIndex.add(new GameVector3(0.5, 0.5, 0.5));
    let team = getTeam(entity);
    let min = world.createEntity({
        mesh: 'mesh/水雷.vb',
        tags: [team, TAG_ENABLE_DAMAGE],
        position: pos,
        collides: false,
        gravity: false,
        fixed: true,
        meshScale: new GameVector3(0.01, 0.01, 0.01),
    });
    let z = world.addZone({
        bounds: {
            lo: [pos.x - 7, pos.y - 7, pos.z - 7],
            hi: [pos.x + 7, pos.y + 7, pos.z + 7],
        },
        selector: 'player',
    });
    let e = entity;
    z.onEnter(({ entity }) => {
        let t = getTeam(entity);
        if (t != team) {
            z.remove();
            min.destroy();
            explode(pos, getTeam(e), e);
        }
    });
    entity.player.directMessage('水雷部署成功!');
    return true;
}

function explode(pos, team, launcher) {
    let en = world.createEntity({
        mesh: 'mesh/火球.vb',
        position: pos.clone(),
        collides: false,
        gravity: false,
        fixed: true,
        meshScale: new GameVector3(0.25, 0.25, 0.25),
    });
    sleep(2 * 1000).finally(() => {
        en.destroy();
    });
    let r = new GameVector3(7, 7, 7);
    world.searchBox(new GameBounds3(pos.clone().sub(r), pos.clone().add(r))).forEach((entity) => {
        if (entity.hasTag(theOtherTeam(team)) && entity.hasTag(TAG_ENABLE_DAMAGE)) {
            hurt(launcher, entity, 300);
        }
    });
    let radius = 7
    let xend = pos.x + radius
    let yend = pos.y + radius
    let zend = pos.z + radius
    for (let x = pos.x - radius; x <= xend; x++) {
        for (let y = pos.y - radius; y <= yend; y++) {
            for (let z = pos.z - radius; z <= zend; z++) {
                let dx = x - pos.x;
                let dy = y - pos.y;
                let dz = z - pos.z;
                if (Math.round(Math.sqrt(dx * dx + dy * dy + dz * dz)) <= radius) {
                    destroyVoxel(x, y, z, 600);
                }
            }
        }
    }
}

// function useSmokeBomb(entity, raycast) {
//     let team = getTeam(entity);
//     let e = world.createEntity({
//         mesh: 'mesh/喵.vb',
//         tags: [team, 'bullet', '喵oke_bomb'],
//         position: entity.position,
//         collides: true,
//         gravity: true,
//         meshScale: new GameVector3(1 / 32, 1 / 32, 1 / 32),
//         velocity: raycast.direction.clone().scale(3),
//     });

//     e.onEntityContact(({ entity }) => {
//         entity.destroy();
//     });

//     e.onVoxelContact(({ entity }) => {
//         entity.destroy();
//     });

//     e.onDestroy(() => {
//         let pos = e.position.clone();

//         let z=world.addZone({
//             bounds: {
//                 lo: [pos.x-20, pos.y-20, pos.z-20],
//                 hi: [pos.x+20, pos.y+20, pos.z+20],
//             },
//             fogEnabled:true,
//             fogColor:new GameRGBColor(1,1,1),
//             fogHeightOffset:-10,
//             fogDensity:1,
//             fogMax:1,
//             fogStartDistance:0,
//         });
//         sleep(5*1000).finally(()=>{
//             z.remove();
//         });
//     });
//     return true;
// }

function hurt(entity, other, damage) {
    let d = damage;
    if (damage > other.hp) {
        d = other.hp;
    }
    other.hurt(d, { attacker: entity });
    other.player.directMessage(`[剩余HP: ${other.hp}]: 你受到了${d}点来自@${entity.player.name}的伤害`);
    let data = PlayerData[entity.player.name];
    let state = PlayerState[entity.player.name];
    data.causingDamage += d;
    state.causingDamage += d;
    addExperienceAndMoney(entity.player.name, 5 * d);
    Tea喵amage[getTeam(other)].damage += d;
}

function destroyVoxel(x, y, z, damage) {
    let v = voxels.name(voxels.getVoxelId(x, y, z));
    if (!Object.keys(BLOCK_DEFENSE_ABILITY).includes(v)) return false;//判断是否可挖
    if (getRandomInt(0, BLOCK_DEFENSE_ABILITY[v]) < damage) {//概率挖掘
        voxels.setVoxel(x, y, z, 'air');
        return true;
    }
    return false;
}

function putVoxel(vox) {
    return (entity, raycast) => {
        if (entity.position.distance(raycast.hitPosition) > 6) {
            entity.player.directMessage('距离太远!');
            return false;
        }
        if (raycast.hitVoxel === 0) {
            return false;
        }
        const pos = raycast.voxelIndex.add(raycast.normal);
        voxels.setVoxel(pos.x, pos.y, pos.z, vox);
        return true;
    };
}

function showBattlefieldReport(winningTeam) {
    let dam = `总伤害:   红队:${Tea喵amage['red'].damage}   蓝队:${Tea喵amage['blue'].damage}`;
    let dt = `喵亡人数:   红队:${Tea喵amage['red'].deathToll}   蓝队:${Tea喵amage['blue'].deathToll}`;
    Object.keys(PlayerState).forEach(async (name) => {
        let state = PlayerState[name];
        let entity = state.entity;
        gui.setAttribute(entity, "#gkillingnumber", "text", `本局击杀:${state.killingNumber}`);
        gui.setAttribute(entity, "#gcausingdamage", "text", `本局造成伤害:${state.causingDamage}`);
        gui.setAttribute(entity, "#winningteam", "text", `胜利队伍:${TEAM_E_C_TABLE[winningTeam]}`);
        gui.setAttribute(entity, "#damage", "text", dam);
        gui.setAttribute(entity, "#deathtoll", "text", dt);
        gui.setAttribute(entity, "#battlefieldreport", "display", 'block');
    });
}

function endGame(winningTeam) {
    showBattlefieldReport(winningTeam);
    IsGameInProgress = false;
    clearTimeout(timeOut);
    for (let n of Object.keys(PlayerData)) {
        if (PlayerState[n].team === winningTeam) {
            addExperienceAndMoney(n, 5000);
        }
        addExperienceAndMoney(n, 2000);
    }
    PlayerState = {};
}

function win(winningTeam) {
    endGame(winningTeam);
    saveAll();
    world.say(`${TEAM_E_C_TABLE[winningTeam]}胜利!${GAME_HALFTIME}秒后开始下一局游戏`);
    sleep(GAME_HALFTIME).finally(() => {
        reboot();
    });
}

function reboot() {
    while (1) { }
}

function saveAll() {
    world.querySelectorAll('player').forEach((entity) => save(entity));
}

async function save(entity) {
    if (entity.player.boxId === '') {
        return;
    }
    await userStorage.set(entity.player.userKey, PlayerData[entity.player.name]);
    entity.player.directMessage('保存成功!');
}

async function loadUserData(entity) {
    if (entity.player.userKey === '') {
        PlayerData[entity.player.name] = DEFAULT_PLAYER_DATA;
        entity.player.directMessage('游客数据创建成功!');
    }
    const v = userStorage.get(entity.player.userKey);
    return await v.then(resolve => {
        for (let v in DEFAULT_PLAYER_DATA) {
            if (typeof (resolve.value[v]) === 'undefined') {
                resolve.value[v] = DEFAULT_PLAYER_DATA[v];
            }
        }
        PlayerData[entity.player.name] = resolve.value;
        entity.player.directMessage('数据加载成功!');
        return true;
    }).catch(reject => {
        PlayerData[entity.player.name] = DEFAULT_PLAYER_DATA;
        userStorage.set(entity.player.userKey, DEFAULT_PLAYER_DATA);
        entity.player.directMessage('已为您建立数据!');
        return false;
    });
}

function addExperienceAndMoney(name, value) {
    PlayerData[name].experience += value;
    PlayerData[name].money += value;
}

function theOtherTeam(team) {
    if (team === 'red') {
        return 'blue';
    } else if (team === 'blue') {
        return 'red';
    } else {
        log_error(`no team ${team}`);
    }
}

// 随机打乱数组
function arrayShuffle(array) {
    for (let i = 1; i < array.length; ++i) {
        const temp = array[i];
        const x = (Math.random() * (i + 1)) | 0;
        array[i] = array[x];
        array[x] = temp;
    }
    return array;
}

// 为对象实现类似数组的map功能
function objectMap(obj, func) {
    const o = {};
    for (const key in obj) {
        o[key] = func(obj[key], key);
    }
    return o;
}

function getRandomInt(min, max) {
    min = Math.ceil(min);
    max = Math.floor(max);
    return Math.floor(Math.random() * (max - min)) + min; //不含最大值,含最小值
}

function log_error(string) {
    console.log(`ERROR:${string}`);
}

function log_warning(string) {
    console.log(`WARNING:${string}`);
}


//构造:

//池塘灌水
// for (let y = 0; y < 9; y++) {
//     for (let x = 0; x < 256; x++) {
//         for (let z = 0; z < 256; z++) {
//             if (voxels.getVoxel(x, y, z) == 0) {
//                 voxels.setVoxel(x, y, z, 'water');
//             }
//         }
//     }
// }


//防御墙
// for (let z = 0; z < 256; z++) {
//     let i = 209;
//     if (z < 221) {
//         voxels.setVoxel(i + 3, 5, z, 'light_gray');
//         voxels.setVoxel(i + 3, 6, z, 'light_gray');
//         voxels.setVoxel(i + 3, 7, z, 'light_gray');
//         voxels.setVoxel(i + 3, 8, z, 'light_gray');
//         voxels.setVoxel(i + 3, 9, z, 'light_gray');
//         voxels.setVoxel(i + 3, 10, z, 'light_gray');
//         voxels.setVoxel(i + 3, 11, z, 'light_gray');
//         voxels.setVoxel(i + 3, 12, z, 'light_gray');
//         voxels.setVoxel(i + 3, 13, z, 'light_gray');
//         voxels.setVoxel(i + 3, 14, z, 'light_gray');
//         voxels.setVoxel(i + 3, 15, z, 'light_gray');
//         voxels.setVoxel(i + 3, 16, z, 'light_gray');
//         voxels.setVoxel(i + 2, 13, z, 'light_gray');
//         voxels.setVoxel(i + 2, 14, z, 'light_gray');
//         voxels.setVoxel(i + 2, 15, z, 'light_gray');
//         voxels.setVoxel(i + 1, 11, z, 'light_gray');
//         voxels.setVoxel(i + 1, 12, z, 'light_gray');
//         voxels.setVoxel(i, 9, z, 'light_gray');
//         voxels.setVoxel(i, 10, z, 'light_gray');
//     }

//     i = 124;
//     if (z < 222) {
//         voxels.setVoxel(i + 3, 5, z, 'light_gray');
//         voxels.setVoxel(i + 3, 6, z, 'light_gray');
//         voxels.setVoxel(i + 3, 7, z, 'light_gray');
//         voxels.setVoxel(i + 3, 8, z, 'light_gray');
//         voxels.setVoxel(i + 3, 9, z, 'light_gray');
//         voxels.setVoxel(i + 3, 10, z, 'light_gray');
//         voxels.setVoxel(i + 3, 11, z, 'light_gray');
//         voxels.setVoxel(i + 3, 12, z, 'light_gray');
//         voxels.setVoxel(i + 3, 13, z, 'light_gray');
//         voxels.setVoxel(i + 3, 14, z, 'light_gray');
//         voxels.setVoxel(i + 3, 15, z, 'light_gray');
//         voxels.setVoxel(i + 3, 16, z, 'light_gray');
//         voxels.setVoxel(i + 2, 13, z, 'light_gray');
//         voxels.setVoxel(i + 2, 14, z, 'light_gray');
//         voxels.setVoxel(i + 2, 15, z, 'light_gray');
//         voxels.setVoxel(i + 1, 11, z, 'light_gray');
//         voxels.setVoxel(i + 1, 12, z, 'light_gray');
//         voxels.setVoxel(i, 9, z, 'light_gray');
//         voxels.setVoxel(i, 10, z, 'light_gray');
//     }

//     i = 24;
//     voxels.setVoxel(i + 3, 1, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 2, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 3, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 4, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 5, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 6, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 7, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 8, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 9, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 10, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 11, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 12, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 13, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 14, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 15, z, 'lab_material_03');
//     voxels.setVoxel(i + 3, 16, z, 'lab_material_03');
//     voxels.setVoxel(i + 2, 13, z, 'lab_material_03');
//     voxels.setVoxel(i + 2, 14, z, 'lab_material_03');
//     voxels.setVoxel(i + 2, 15, z, 'lab_material_03');
//     voxels.setVoxel(i + 1, 11, z, 'lab_material_03');
//     voxels.setVoxel(i + 1, 12, z, 'lab_material_03');
//     voxels.setVoxel(i, 9, z, 'lab_material_03');
//     voxels.setVoxel(i, 10, z, 'lab_material_03');
// }

// for (let z = 0; z < 256; z++) {
//     let i = 162;
//     if (z < 210) {
//         voxels.setVoxel(i + 6, 8, z, '');
//         voxels.setVoxel(i + 5, 7, z, '');
//         voxels.setVoxel(i + 5, 8, z, '');
//         voxels.setVoxel(i + 4, 5, z, '');
//         voxels.setVoxel(i + 4, 6, z, '');
//         voxels.setVoxel(i + 4, 7, z, '');
//         voxels.setVoxel(i + 4, 8, z, '');
//         voxels.setVoxel(i + 3, 5, z, '');
//         voxels.setVoxel(i + 3, 6, z, '');
//         voxels.setVoxel(i + 3, 7, z, '');
//         voxels.setVoxel(i + 3, 8, z, '');
//         voxels.setVoxel(i + 2, 5, z, '');
//         voxels.setVoxel(i + 2, 6, z, '');
//         voxels.setVoxel(i + 2, 7, z, '');
//         voxels.setVoxel(i + 2, 8, z, '');
//         voxels.setVoxel(i + 1, 7, z, '');
//         voxels.setVoxel(i + 1, 8, z, '');
//         voxels.setVoxel(i, 8, z, '');
//     }

//     i = 72;
//     voxels.setVoxel(i + 6, 8, z, '');
//     voxels.setVoxel(i + 5, 7, z, '');
//     voxels.setVoxel(i + 5, 8, z, '');
//     voxels.setVoxel(i + 4, 5, z, '');
//     voxels.setVoxel(i + 4, 6, z, '');
//     voxels.setVoxel(i + 4, 7, z, '');
//     voxels.setVoxel(i + 4, 8, z, '');
//     voxels.setVoxel(i + 3, 5, z, '');
//     voxels.setVoxel(i + 3, 6, z, '');
//     voxels.setVoxel(i + 3, 7, z, '');
//     voxels.setVoxel(i + 3, 8, z, '');
//     voxels.setVoxel(i + 2, 5, z, '');
//     voxels.setVoxel(i + 2, 6, z, '');
//     voxels.setVoxel(i + 2, 7, z, '');
//     voxels.setVoxel(i + 2, 8, z, '');
//     voxels.setVoxel(i + 1, 7, z, '');
//     voxels.setVoxel(i + 1, 8, z, '');
//     voxels.setVoxel(i, 8, z, '');
// }

world.onChat(async ({ entity, message }) => {//管喵
    if (message[0] === '$') {
        if (entity.isPlayer && entity.player.userId === '12893142'/*作者*/) {
            console.log(eval(message.substring(1, message.length)));
        }
    }
});


回复

上一页1 页 / 共 1下一页
代码小神代码小神

不行了不行了,这么长的代码,我眼都快花了

点赞0


评论


幻影游船幻影游船

我可以用吗?

 

点赞0


评论


严肃的小电鼠VwWr严肃的小电鼠VwWr

66666666666

点赞0


评论


骑士之刃骑士之刃

喵d

点赞0


评论


追梦ez追梦ez

有没有地图链接,我玩玩)(

点赞0


评论