用户:
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)));
}
}
});