完成打乱斗数据调试

This commit is contained in:
kron
2026-02-07 10:52:56 +08:00
parent 09d8e7b3da
commit 6101cd80ce
20 changed files with 444 additions and 473 deletions

View File

@@ -417,12 +417,6 @@ export const getUserGameState = () => {
return request("GET", "/user/state");
};
export const getCurrentGameAPI = async () => {
// uni.$emit("update-header-loading", true);
// const result = await request("GET", "/user/join/battle");
// return result.currentGame || {};
};
export const getPointBookConfigAPI = async () => {
return request("GET", "/user/score/sheet/option");
};
@@ -554,8 +548,7 @@ export const getReadyAPI = (roomId) => {
};
export const getBattleAPI = async (battleId) => {
const result = await request("POST", "/user/match/info", {
return request("POST", "/user/match/info", {
id: battleId,
});
return result;
};

View File

@@ -46,14 +46,14 @@ const onClick = debounce(async () => {
loading.value = true;
const result = await getBattleAPI();
if (result && result.matchId) {
// await uni.$checkAudio();
if (result.way === 1) {
await uni.$checkAudio();
if (result.mode <= 3) {
uni.navigateTo({
url: `/pages/team-battle?battleId=${result.matchId}`,
});
} else if (result.way === 2) {
} else {
uni.navigateTo({
url: `/pages/melee-match?battleId=${result.matchId}`,
url: `/pages/melee-battle?battleId=${result.matchId}`,
});
}
return;

View File

@@ -5,7 +5,7 @@ import AppBackground from "@/components/AppBackground.vue";
import Header from "@/components/Header.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import BackToGame from "@/components/BackToGame.vue";
import { getCurrentGameAPI, laserAimAPI } from "@/apis";
import { laserAimAPI, getBattleAPI } from "@/apis";
import { capsuleHeight, debounce } from "@/util";
import AudioManager from "@/audioManager";
const props = defineProps({
@@ -111,15 +111,19 @@ const backToGame = debounce(async () => {
try {
isLoading.value = true;
const game = await getCurrentGameAPI();
if (!game || !game.gameId) {
uni.showToast({
title: "没有进行中的对局",
icon: "none",
const result = await getBattleAPI();
if (result && result.matchId) {
await checkAudioProgress();
if (result.mode <= 3) {
uni.navigateTo({
url: `/pages/team-battle?battleId=${result.matchId}`,
});
} else {
uni.navigateTo({
url: `/pages/melee-battle?battleId=${result.matchId}`,
});
}
showHint.value = false;
}
} catch (error) {
console.error("获取当前游戏失败:", error);
} finally {

View File

@@ -44,6 +44,8 @@ const createRoom = debounce(async () => {
url: "/pages/battle-room?roomNumber=" + result.number,
});
}
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}

View File

@@ -56,7 +56,6 @@ const signin = () => {
};
const loading = ref(false);
const showLoader = ref(false);
const pointBook = ref(null);
const showProgress = ref(false);
const heat = ref(0);
@@ -80,20 +79,12 @@ onMounted(() => {
pointBook.value = uni.getStorageSync("last-point-book");
}
}
if (
currentPage.route === "pages/team-battle" ||
currentPage.route === "pages/melee-match"
) {
showLoader.value = true;
}
if (currentPage.route === "pages/team-battle") {
showProgress.value = true;
}
uni.$on("update-header-loading", updateLoading);
uni.$on("update-hot", updateHot);
});
onBeforeUnmount(() => {
uni.$off("update-header-loading", updateLoading);
uni.$off("update-hot", updateHot);
});
</script>
@@ -161,12 +152,6 @@ onBeforeUnmount(() => {
</view>
</block>
</view>
<image
:style="{ opacity: showLoader && loading ? 0 : 0 }"
src="../static/btn-loading.png"
mode="widthFix"
class="loading"
/>
<view v-if="pointBook" class="point-book-info">
<text>{{ pointBook.bowType.name }}</text>
<text>{{ pointBook.distance }} 米</text>
@@ -230,14 +215,6 @@ onBeforeUnmount(() => {
font-size: 16px;
color: #fff;
}
.loading {
width: 20px;
height: 20px;
margin-left: 10px;
transition: all 0.3s ease;
background-blend-mode: darken;
animation: rotate 2s linear infinite;
}
.point-book-info {
color: #333;
position: fixed;

View File

@@ -3,7 +3,7 @@ import useStore from "@/store";
import { storeToRefs } from "pinia";
const { user } = storeToRefs(useStore());
defineProps({
const props = defineProps({
player: {
type: Object,
default: () => ({}),
@@ -23,7 +23,10 @@ const rowCount = new Array(6).fill(0);
:style="{ borderColor: player.id === user.id ? '#FED847' : '#fff3' }"
>
<image
:style="{ opacity: scores.length === 12 ? 1 : 0 }"
:style="{
opacity:
(scores[0] || []).length + (scores[1] || []).length === 12 ? 1 : 0,
}"
src="../static/checked-green.png"
mode="widthFix"
/>
@@ -32,20 +35,24 @@ const rowCount = new Array(6).fill(0);
<view>
<view>
<view v-for="(_, index) in rowCount" :key="index">
<text>{{ scores[index] ? `${scores[index].ring}` : "-" }}</text>
<text>{{
scores[0] && scores[0][index] ? `${scores[0][index].ring}` : "-"
}}</text>
</view>
</view>
<view>
<view v-for="(_, index) in rowCount" :key="index">
<text>{{
scores[index + 6] ? `${scores[index + 6].ring}` : "-"
scores[1] && scores[1][index] ? `${scores[0][index].ring}` : "-"
}}</text>
</view>
</view>
</view>
<text
>{{
scores.map((s) => s.ring).reduce((last, next) => last + next, 0)
scores
.map((s) => s.reduce((last, next) => last + next.ring, 0))
.reduce((last, next) => last + next, 0)
}}</text
>
</view>

View File

@@ -9,7 +9,7 @@ defineProps({
type: String,
default: "",
},
scores: {
arrows: {
type: Array,
default: () => [],
},
@@ -21,10 +21,6 @@ defineProps({
type: Number,
default: 0,
},
totalRing: {
type: Number,
default: 0,
},
});
const rowCount = new Array(6).fill(0);
</script>
@@ -60,19 +56,19 @@ const rowCount = new Array(6).fill(0);
<view>
<view>
<view v-for="(_, index) in rowCount" :key="index">
<text>{{ scores[index] ? `${scores[index].ring}` : "-" }}</text>
<text>{{ arrows[index] ? `${arrows[index].ring}` : "-" }}</text>
</view>
</view>
<view>
<view v-for="(_, index) in rowCount" :key="index">
<text>{{
scores[index + 6] ? `${scores[index + 6].ring}` : "-"
arrows[index + 6] ? `${arrows[index + 6].ring}` : "-"
}}</text>
</view>
</view>
</view>
<view>
<text>{{ totalRing }}</text>
<text>{{ arrows.reduce((last, next) => last + next.ring, 0) }}</text>
<text>积分{{ totalScore }}</text>
</view>
</view>

View File

@@ -124,15 +124,16 @@ const seats = new Array(props.total).fill(1);
right: 0;
} */
.player-unknow {
width: 40px;
height: 40px;
width: 84rpx;
height: 84rpx;
margin: 0 24rpx;
border: 1px solid #fff3;
border: 1rpx solid #fff3;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
background-color: #69686866;
box-sizing: border-box;
}
.player-unknow > image {
width: 40%;

View File

@@ -1,7 +1,7 @@
<script setup>
import { ref, watch, onMounted, onBeforeUnmount, computed } from "vue";
import audioManager from "@/audioManager";
import { MESSAGETYPES } from "@/constants";
import { MESSAGETYPESV2 } from "@/constants";
import { getDirectionText } from "@/util";
import useStore from "@/store";
@@ -116,62 +116,77 @@ const updateSound = () => {
audioManager.setMuted(!sound.value);
};
async function onReceiveMessage(messages = []) {
if (ended.value) return;
messages.forEach((msg) => {
if (
(props.battleId && msg.constructor === MESSAGETYPES.ShootResult) ||
(!props.battleId && msg.constructor === MESSAGETYPES.ShootSyncMeArrowID)
) {
if (props.melee && msg.userId !== user.value.id) return;
if (!halfTime.value && msg.target) {
let key = [];
key.push(msg.target.ring ? `${msg.target.ring}` : "未上靶");
if (!msg.target.ring)
key.push(`${getDirectionText(msg.target.angle)}调整`);
audioManager.play(key);
}
} else if (msg.constructor === MESSAGETYPES.InvalidShot) {
if (msg.userId === user.value.id) {
uni.showToast({
title: "距离不足,无效",
icon: "none",
});
audioManager.play("射击无效");
}
} else if (msg.constructor === MESSAGETYPES.AllReady) {
async function onReceiveMessage(msg) {
if (Array.isArray(msg)) return;
if (msg.type === MESSAGETYPESV2.BattleStart) {
audioManager.play("比赛开始");
} else if (msg.constructor === MESSAGETYPES.MeleeAllReady) {
halfTime.value = false;
audioManager.play("比赛开始");
} else if (msg.constructor === MESSAGETYPES.CurrentRoundEnded) {
currentRoundEnded.value = true;
} else if (msg.constructor === MESSAGETYPES.HalfTimeOver) {
if (props.battleId) {
halfTime.value = true;
audioManager.play("中场休息");
return;
}
if (wait.value !== msg.wait) {
setTimeout(() => {
wait.value = msg.wait;
if (msg.wait === 20) {
halfTime.value = true;
audioManager.play("中场休息", false);
}
if (msg.wait === 0) {
halfTime.value = false;
}
}, 200);
}
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
audioManager.play("比赛结束");
} else if (msg.constructor === MESSAGETYPES.FinalShoot) {
audioManager.play("决金箭轮");
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
ended.value = true;
} else if (msg.type === MESSAGETYPESV2.ShootResult) {
if (msg.shootData.playerId !== user.value.id) return;
if (msg.shootData) {
let key = [];
key.push(msg.shootData.ring ? `${msg.shootData.ring}` : "未上靶");
if (!msg.shootData.ring)
key.push(`${getDirectionText(msg.shootData.angle)}调整`);
audioManager.play(key, false);
}
});
}
// if (ended.value) return;
// messages.forEach((msg) => {
// if (
// (props.battleId && msg.constructor === MESSAGETYPES.ShootResult) ||
// (!props.battleId && msg.constructor === MESSAGETYPES.ShootSyncMeArrowID)
// ) {
// if (props.melee && msg.userId !== user.value.id) return;
// if (!halfTime.value && msg.target) {
// let key = [];
// key.push(msg.target.ring ? `${msg.target.ring}环` : "未上靶");
// if (!msg.target.ring)
// key.push(`向${getDirectionText(msg.target.angle)}调整`);
// audioManager.play(key);
// }
// } else if (msg.constructor === MESSAGETYPES.InvalidShot) {
// if (msg.userId === user.value.id) {
// uni.showToast({
// title: "距离不足,无效",
// icon: "none",
// });
// audioManager.play("射击无效");
// }
// } else if (msg.constructor === MESSAGETYPES.AllReady) {
// audioManager.play("比赛开始");
// } else if (msg.constructor === MESSAGETYPES.MeleeAllReady) {
// halfTime.value = false;
// audioManager.play("比赛开始");
// } else if (msg.constructor === MESSAGETYPES.CurrentRoundEnded) {
// currentRoundEnded.value = true;
// } else if (msg.constructor === MESSAGETYPES.HalfTimeOver) {
// if (props.battleId) {
// halfTime.value = true;
// audioManager.play("中场休息");
// return;
// }
// if (wait.value !== msg.wait) {
// setTimeout(() => {
// wait.value = msg.wait;
// if (msg.wait === 20) {
// halfTime.value = true;
// audioManager.play("中场休息", false);
// }
// if (msg.wait === 0) {
// halfTime.value = false;
// }
// }, 200);
// }
// } else if (msg.constructor === MESSAGETYPES.MatchOver) {
// audioManager.play("比赛结束");
// } else if (msg.constructor === MESSAGETYPES.FinalShoot) {
// audioManager.play("决金箭轮");
// } else if (msg.constructor === MESSAGETYPES.MatchOver) {
// ended.value = true;
// }
// });
}
const playSound = (key) => {

View File

@@ -37,7 +37,7 @@ export const MESSAGETYPESV2 = {
ShootResult: 4,
NewRound: 5,
BattleEnd: 6,
halfRest: 7,
HalfRest: 7,
};
export const topThreeColors = ["#FFD947", "#D2D2D2", "#FFA515"];

View File

@@ -31,10 +31,13 @@
}
},
{
"path": "pages/battle-result"
"path": "pages/team-battle"
},
{
"path": "pages/team-battle"
"path": "pages/melee-battle"
},
{
"path": "pages/battle-result"
},
{
"path": "pages/point-book-edit"
@@ -102,9 +105,6 @@
{
"path": "pages/rank-list"
},
{
"path": "pages/melee-match"
},
{
"path": "pages/match-detail"
},

View File

@@ -16,6 +16,7 @@ const ifWin = ref(false);
const data = ref({});
const totalPoints = ref(0);
const rank = ref(0);
const players = ref([]);
function exit() {
if (data.value.roomId) {
@@ -30,27 +31,36 @@ function exit() {
onLoad(async (options) => {
if (!options.battleId) return;
const myId = user.value.id;
const result = await getBattleAPI(options.battleId || "58351917302157312");
const result = await getBattleAPI(options.battleId || "59090720979554304");
console.log("result", result);
data.value = result;
if (result.winTeam) {
ifWin.value = result.teams[result.winTeam].players.some(
(p) => p.id === myId
);
}
if (result.way === 1) {
if (result.mode <= 3) {
audioManager.play(ifWin.value ? "胜利" : "失败");
} else if (result.way === 2) {
// if (data.value.battleMode === 1) {
// if (rank.value <= data.value.playerStats.length * 0.3) {
// audioManager.play("胜利");
// }
// } else if (data.value.battleMode === 2) {
// if (totalPoints.value > 0) {
// audioManager.play("胜利");
// } else if (totalPoints.value < 0) {
// audioManager.play("失败");
// }
// }
} else {
players.value = result.resultList.map((item, index) => {
const plist = result.teams[0] ? result.teams[0].players : [];
const p = plist.find((p) => p.id === item.userId);
if (p.id === user.value.id) {
totalPoints.value = p.score;
rank.value = index + 1;
}
return {
...item,
rank: index + 1,
name: p.name,
avatar: p.avatar || "",
};
});
if (rank.value <= players.value * 0.3) {
audioManager.play("胜利");
} else {
audioManager.play("胜利");
}
}
});
@@ -71,7 +81,7 @@ const checkBowData = () => {
<template>
<view class="container">
<block v-if="data.way === 1">
<block v-if="data.mode <= 3">
<view class="header-team" :style="{ marginTop: '25%' }">
<image src="../static/battle-result.png" mode="widthFix" />
<view class="header-solo" v-if="data.mode === 1">
@@ -161,7 +171,7 @@ const checkBowData = () => {
/>
</view>
</block>
<block v-if="data.way === 2">
<block v-else>
<view class="header-melee">
<view />
<image src="../static/battle-result.png" mode="widthFix" />
@@ -170,11 +180,11 @@ const checkBowData = () => {
<view
class="players"
:style="{
height: `${Math.max(data.playerStats.length > 5 ? '330' : '300')}px`,
height: `${Math.max(players.length > 5 ? '330' : '300')}px`,
}"
>
<view
v-for="(player, index) in data.playerStats"
v-for="(player, index) in players"
:key="index"
:style="{
border: player.id === user.id ? '1px solid #B04630' : 'none',
@@ -229,7 +239,7 @@ const checkBowData = () => {
<text>{{ getLvlName(player.rank_lvl) }}</text>
</view>
<text
><text :style="{ color: '#fff' }">{{ player.totalRings }}</text>
><text :style="{ color: '#fff' }">{{ player.totalRing }}</text>
</text
>
</view>

View File

@@ -35,6 +35,7 @@ const battleType = ref(0);
const ready = ref(false);
const allReady = ref(false);
const timer = ref(null);
const goBattle = ref(false);
async function refreshRoomData() {
if (!roomNumber.value) return;
@@ -147,18 +148,16 @@ async function onReceiveMessage(message) {
}
});
} else if (message.type === MESSAGETYPESV2.AboutToStart) {
goBattle.value = true;
if (message.mode <= 3) {
uni.setStorageSync("blue-team", message.teams[1].players || []);
uni.setStorageSync("red-team", message.teams[2].players || []);
uni.removeStorageSync("current-battle");
roomNumber.value = "";
let params = `?gameMode=1&battleId=${message.matchId}`;
if (message.way == 1) {
uni.redirectTo({
url: "/pages/team-battle" + params,
url: "/pages/team-battle?battleId" + message.matchId,
});
} else if (message.way == 2) {
} else {
uni.redirectTo({
url: "/pages/melee-match" + params,
url: "/pages/melee-battle?battleId=" + message.matchId,
});
}
}
@@ -188,6 +187,7 @@ onShareAppMessage(() => {
});
onShow(() => {
goBattle.value = false;
refreshRoomData();
});
@@ -207,7 +207,7 @@ onBeforeUnmount(() => {
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
if (roomNumber.value) exitRoomAPI(roomNumber.value);
if (!goBattle.value) exitRoomAPI(roomNumber.value);
if (timer.value) clearInterval(timer.value);
timer.value = null;
});

View File

@@ -7,21 +7,39 @@ import Avatar from "@/components/Avatar.vue";
import PlayerScore2 from "@/components/PlayerScore2.vue";
import { getBattleAPI } from "@/apis";
const blueTeam = ref([]);
const redTeam = ref([]);
const roundsData = ref([]);
const goldenRoundsData = ref([]);
const battleId = ref("");
const data = ref({
teams: [],
rounds: [],
});
const players = ref([]);
onLoad(async (options) => {
if (!options.id) return;
battleId.value = options.id || "57943107462893568";
battleId.value = options.id || "59090720979554304";
const result = await getBattleAPI(battleId.value);
data.value = result;
if (result.mode > 3) {
players.value = result.resultList.map((item, index) => {
const plist = result.teams[0] ? result.teams[0].players : [];
const p = plist.find((p) => p.id === item.userId);
const arrows = new Array(12);
result.rounds.forEach((r, index) => {
if (r.shoots[item.userId]) {
r.shoots[item.userId].forEach((s, index2) => {
arrows[index2 + index * 6] = s;
});
}
});
return {
...item,
rank: index + 1,
name: p.name,
avatar: p.avatar || "",
arrows,
};
});
}
});
const checkBowData = (selected) => {
@@ -44,10 +62,10 @@ const checkBowData = (selected) => {
:winner="data.winTeam"
:blueTeam="data.teams[1] ? data.teams[1].players : []"
:redTeam="data.teams[2] ? data.teams[2].players : []"
:players="data.players"
:players="players"
/>
<view
v-if="data.mode > 3"
v-if="data.mode >= 3"
class="score-header"
:style="{ border: 'none', padding: '5px 15px' }"
>
@@ -58,17 +76,17 @@ const checkBowData = (selected) => {
</view>
</view>
<PlayerScore2
v-if="data.mode > 3"
v-for="(player, index) in data.players"
v-if="data.mode >= 3"
v-for="(player, index) in players"
:key="index"
:name="player.name"
:avatar="player.avatar"
:scores="player.arrowHistory"
:arrows="player.arrows"
:totalScore="player.totalScore"
:totalRing="player.totalRings"
:rank="index + 1"
/>
<view
v-if="data.mode <= 3"
v-for="(round, index) in data.rounds"
:key="index"
:style="{ marginBottom: '5px' }"

View File

@@ -29,14 +29,13 @@ async function onReceiveMessage(messages = []) {
...msg.groupUserStatus.blueTeam,
]);
}
uni.removeStorageSync("current-battle");
if (gameType.value == 1) {
uni.redirectTo({
url: `/pages/team-battle?battleId=${msg.id}&gameMode=2`,
});
} else if (gameType.value == 2) {
uni.redirectTo({
url: `/pages/melee-match?battleId=${msg.id}&gameMode=2`,
url: `/pages/melee-battle?battleId=${msg.id}&gameMode=2`,
});
}
};

205
src/pages/melee-battle.vue Normal file
View File

@@ -0,0 +1,205 @@
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import BowTarget from "@/components/BowTarget.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import BattleHeader from "@/components/BattleHeader.vue";
import PlayerScore from "@/components/PlayerScore.vue";
import SButton from "@/components/SButton.vue";
import Avatar from "@/components/Avatar.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import TestDistance from "@/components/TestDistance.vue";
import audioManager from "@/audioManager";
import { getBattleAPI, laserCloseAPI } from "@/apis";
import { isGameEnded } from "@/util";
import { MESSAGETYPESV2 } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const title = ref("");
const start = ref(null);
const battleId = ref("");
const currentRound = ref(1);
const tips = ref("即将开始...");
const players = ref([]);
const playersSorted = ref([]);
const playersScores = ref([]);
const halfTimeTip = ref(false);
const halfRest = ref(false);
function recoverData(battleInfo, { force = false } = {}) {
console.log("battleInfo", battleInfo);
if (battleInfo.way === 1) title.value = "好友约战 - 大乱斗";
if (battleInfo.way === 2) title.value = "排位赛 - 大乱斗";
players.value = battleInfo.teams[0].players;
start.value = battleInfo.status !== 0;
if (battleInfo.status === 0) {
const readyRemain = (Date.now() - battleInfo.createTime) / 1000;
console.log(`对局已进行${readyRemain}`);
if (readyRemain > 0 && readyRemain < 15) {
setTimeout(() => {
uni.$emit("update-timer", 15 - readyRemain - 0.2);
}, 200);
}
return;
}
tips.value =
(battleInfo.rounds.length !== 2 ? "上" : "下") + "半场请先射6箭";
playersScores.value = battleInfo.rounds.map((r) => ({ ...r.shoots }));
const totals = {};
players.value.forEach((p) => {
const total = playersScores.value.reduce((acc, round) => {
const arr = round[p.id] || [];
return acc + arr.length;
}, 0);
totals[p.id] = total;
});
playersSorted.value = players.value.slice().sort((a, b) => {
return totals[b.id] - totals[a.id];
});
if (battleInfo.status === 3) {
halfTimeTip.value = true;
halfRest.value = true;
tips.value = "准备下半场";
// 剩余休息时间
// const remain = (Date.now() - battleInfo.timeoutTime) / 1000;
setTimeout(() => {
uni.$emit("update-remain", 0);
}, 200);
return;
}
if (force) {
const remain = (Date.now() - battleInfo.current.startTime) / 1000;
console.log(
`当前轮已进行${remain}秒,${Date.now()},${
battleInfo.current.startTimeText
}`
);
if (remain > 0 && remain < 90) {
setTimeout(() => {
uni.$emit("update-remain", 90 - remain - 0.2);
}, 200);
}
} else {
uni.$emit("update-remain", battleInfo.readyTime);
}
}
onLoad(async (options) => {
if (options.battleId) battleId.value = options.battleId;
uni.enableAlertBeforeUnload({
message: "离开比赛可能导致比赛失败,是否继续?",
success: (res) => {
console.log("已启用离开提示");
},
});
});
async function onReceiveMessage(msg) {
if (Array.isArray(msg)) return;
if (msg.type === MESSAGETYPESV2.BattleStart) {
halfTimeTip.value = false;
halfRest.value = false;
recoverData(msg);
} else if (msg.type === MESSAGETYPESV2.ShootResult) {
recoverData(msg);
} else if (msg.type === MESSAGETYPESV2.HalfRest) {
halfTimeTip.value = true;
halfRest.value = true;
tips.value = "准备下半场";
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
uni.redirectTo({
url: "/pages/battle-result?battleId=" + msg.matchId,
});
}
}
onMounted(async () => {
uni.setKeepScreenOn({
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
await laserCloseAPI();
});
onBeforeUnmount(() => {
uni.setKeepScreenOn({
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
audioManager.stopAll();
});
onShow(async () => {
if (battleId.value) {
const result = await getBattleAPI(battleId.value);
if (result.status === 2) {
uni.showToast({
title: "比赛已结束",
icon: "none",
});
uni.navigateBack({
delta: 2,
});
} else {
recoverData(result, { force: true });
}
}
});
</script>
<template>
<Container :title="title" :bgType="1">
<view class="container">
<BattleHeader v-if="!start" :players="players" />
<TestDistance v-if="start === false" :guide="false" :isBattle="true" />
<ShootProgress
:show="start"
:start="start && !halfRest"
:tips="tips"
:total="90"
:melee="true"
:battleId="battleId"
/>
<view v-if="start" class="user-row">
<Avatar :src="user.avatar" :size="35" />
<BowPower />
</view>
<BowTarget
v-if="start"
:currentRound="
playersScores.map((s) => s[user.id].length).reduce((a, b) => a + b, 0)
"
:totalRound="12"
:scores="playersScores.map((r) => r[user.id]).flat()"
:stop="halfRest"
/>
<view :style="{ paddingBottom: '20px' }">
<PlayerScore
v-if="start"
v-for="(player, index) in playersSorted"
:key="index"
:player="player"
:scores="playersScores.map((s) => s[player.id])"
/>
</view>
<ScreenHint
:show="halfTimeTip"
mode="small"
:onClose="() => (halfTimeTip = false)"
>
<view class="half-time-tip">
<text>上半场结束休息一下吧:</text>
<text>20秒后开始下半场</text>
</view>
</ScreenHint>
</view>
</Container>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
}
</style>

View File

@@ -4,28 +4,39 @@ import { onLoad } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import BowTarget from "@/components/BowTarget.vue";
import Avatar from "@/components/Avatar.vue";
import { getGameAPI } from "@/apis";
import { getBattleAPI } from "@/apis";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const scores = ref([]);
const currentUser = ref({});
const data = ref({});
const onSelect = (userId) => {
const user = data.value.players.find((p) => p.playerId === userId);
currentUser.value = user;
if (user && user.arrowHistory) {
scores.value = user.arrowHistory;
}
};
const { user } = storeToRefs(useStore());
const currentUser = ref({
arrows: [],
});
const players = ref([]);
onLoad(async (options) => {
if (options.battleId) {
const result = await getGameAPI(options.battleId);
data.value = result;
if (result.players && result.players[0]) {
onSelect(result.players[0].playerId);
if (!options.id) return;
const result = await getBattleAPI(options.battleId || "59090720979554304");
players.value = result.resultList.map((item, index) => {
const plist = result.teams[0] ? result.teams[0].players : [];
const p = plist.find((p) => p.id === item.userId);
const arrows = new Array(12);
result.rounds.forEach((r, index) => {
if (r.shoots[item.userId]) {
r.shoots[item.userId].forEach((s, index2) => {
arrows[index2 + index * 6] = s;
});
}
});
return {
...item,
rank: index + 1,
name: p.name,
avatar: p.avatar || "",
arrows,
};
});
if (players.value[0]) {
currentUser.value = players.value[0];
}
});
</script>
@@ -33,22 +44,26 @@ onLoad(async (options) => {
<template>
<Container title="靶纸">
<view class="container">
<image src="../static/battle-header-melee.png" mode="widthFix" />
<view class="players" v-if="data.players">
<image
src="../static/battle-header-melee.png"
mode="widthFix"
:style="{ top: '-50rpx' }"
/>
<view class="players">
<view
v-for="(player, index) in data.players"
v-for="(player, index) in players"
:key="index"
:style="{
width: `${Math.max(100 / data.players.length, 18)}vw`,
color: player.playerId === currentUser.playerId ? '#000' : '#fff9',
width: `${Math.max(100 / players.length, 18)}vw`,
color: player.userId === currentUser.userId ? '#000' : '#fff9',
}"
@click="() => onSelect(player.playerId)"
@click="currentUser = player"
>
<image
v-if="player.playerId === currentUser.playerId"
v-if="player.userId === currentUser.userId"
src="../static/player-bg2.png"
:style="{
width: `${Math.max(100 / data.players.length, 18)}vw`,
width: `${Math.max(100 / players.length, 18)}vw`,
}"
class="player-bg"
/>
@@ -57,18 +72,20 @@ onLoad(async (options) => {
</view>
</view>
<view :style="{ marginTop: '10px' }">
<BowTarget :scores="scores" />
<BowTarget :scores="currentUser.arrows" />
</view>
<view class="score-text"
><text :style="{ color: '#fed847' }">{{ scores.length }}</text
><text :style="{ color: '#fed847' }">{{
currentUser.arrows.length
}}</text
>支箭<text :style="{ color: '#fed847' }">{{
scores.reduce((last, next) => last + next.ring, 0)
currentUser.arrows.reduce((last, next) => last + next.ring, 0)
}}</text
></view
>
<view class="score-row">
<view class="score-row" v-if="currentUser.arrows">
<view
v-for="(score, index) in scores"
v-for="(score, index) in currentUser.arrows"
:key="index"
class="score-item"
:style="{ width: '13vw', height: '13vw' }"
@@ -97,7 +114,7 @@ onLoad(async (options) => {
display: flex;
width: 100%;
overflow-x: auto;
margin-top: 25px;
margin-top: 50rpx;
}
.players::-webkit-scrollbar {
width: 0;

View File

@@ -1,248 +0,0 @@
<script setup>
import { ref, watch, onMounted, onBeforeUnmount } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import BowTarget from "@/components/BowTarget.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import BattleHeader from "@/components/BattleHeader.vue";
import PlayerScore from "@/components/PlayerScore.vue";
import SButton from "@/components/SButton.vue";
import Avatar from "@/components/Avatar.vue";
import ScreenHint from "@/components/ScreenHint.vue";
import TestDistance from "@/components/TestDistance.vue";
import audioManager from "@/audioManager";
import { getCurrentGameAPI, laserCloseAPI } from "@/apis";
import { isGameEnded } from "@/util";
import { MESSAGETYPES } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const title = ref("大乱斗");
const start = ref(false);
const startCount = ref(true);
const battleId = ref("");
const currentRound = ref(1);
const scores = ref([]);
const tips = ref("即将开始...");
const players = ref([]);
const playersSorted = ref([]);
const playersScores = ref({});
const halfTimeTip = ref(false);
const isEnded = ref(false);
watch(
() => [players.value, playersScores.value],
([n_players, n_scores]) => {
if (n_players.length) {
playersSorted.value = Object.keys(n_scores)
.sort((a, b) => n_scores[b].length - n_scores[a].length)
.map((pid) => n_players.find((p) => p.id == pid));
}
},
{
deep: true, // 添加深度监听
immediate: true,
}
);
function recoverData(battleInfo) {
uni.removeStorageSync("last-awake-time");
// 注释用于测试
battleId.value = battleInfo.id;
players.value = [...battleInfo.blueTeam, ...battleInfo.redTeam];
players.value.forEach((p) => {
playersScores.value[p.id] = [...p.arrows];
if (p.id === user.value.id) scores.value = [...p.arrows];
});
const remain = Date.now() / 1000 - battleInfo.startTime;
console.log(`当前局已进行${remain}`);
if (battleInfo.status === 0) {
if (remain > 0) {
setTimeout(() => {
uni.$emit("update-timer", 15 - remain);
}, 200);
}
} else {
start.value = true;
}
if (battleInfo.status === 2) {
const elapsedTime = (Date.now() - Date.parse(battleInfo.createdAt)) / 1000;
console.log("elapsedTime:", elapsedTime);
startCount.value = true;
// 这里的开始时间不是游戏开始时间,而是上半场或者下半场或者中场的开始时间,还要根据状态来判断
tips.value = battleInfo.halfGame
? "下半场请再射6箭"
: "上半场请先射6箭";
setTimeout(() => {
uni.$emit("update-remain", 90 - remain);
}, 200);
} else if (battleInfo.status === 9) {
startCount.value = false;
tips.value = "准备下半场";
setTimeout(() => {
uni.$emit("update-remain", 0);
}, 200);
}
}
onLoad(async (options) => {
if (options.gameMode == 1) title.value = "好友约战 - 大乱斗";
if (options.gameMode == 2) title.value = "排位赛 - 大乱斗";
if (options.battleId) {
battleId.value = options.battleId;
const players = uni.getStorageSync("melee-players");
if (players) {
players.value = players;
players.value.forEach((p) => {
playersScores.value[p.id] = [];
});
}
const battleInfo = uni.getStorageSync("current-battle");
if (battleInfo) {
recoverData(battleInfo);
setTimeout(getCurrentGameAPI, 2000);
}
}
uni.enableAlertBeforeUnload({
message: "离开比赛可能导致比赛失败,是否继续?",
success: (res) => {
console.log("已启用离开提示");
},
});
});
async function onReceiveMessage(messages = []) {
messages.forEach((msg) => {
if (msg.id !== battleId.value) return;
if (msg.constructor === MESSAGETYPES.MeleeAllReady) {
start.value = true;
startCount.value = true;
tips.value = scores.value.length
? "下半场请再射6箭"
: "上半场请先射6箭";
halfTimeTip.value = false;
}
if (msg.constructor === MESSAGETYPES.ShootResult) {
if (!start.value) getCurrentGameAPI();
if (msg.userId === user.value.id) {
scores.value.push({ ...msg.target });
}
playersScores.value[msg.userId].push({ ...msg.target });
}
if (msg.constructor === MESSAGETYPES.HalfTimeOver) {
uni.$emit("update-remain", 0);
[...msg.groupUserStatus.redTeam, ...msg.groupUserStatus.blueTeam].forEach(
(player) => {
playersScores.value[player.id] = [...player.arrows];
if (player.id === user.value.id) scores.value = [...player.arrows];
}
);
startCount.value = false;
halfTimeTip.value = true;
tips.value = "准备下半场";
}
if (msg.constructor === MESSAGETYPES.MatchOver) {
isEnded.value = true;
uni.setStorageSync("last-battle", msg.endStatus);
setTimeout(() => {
uni.redirectTo({
url: "/pages/battle-result",
});
}, 1000);
}
if (msg.constructor === MESSAGETYPES.BackToGame) {
uni.$emit("update-header-loading", false);
if (msg.battleInfo) recoverData(msg.battleInfo);
}
});
}
onMounted(async () => {
uni.setKeepScreenOn({
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
await laserCloseAPI();
});
onBeforeUnmount(() => {
uni.setKeepScreenOn({
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
audioManager.stopAll();
});
const refreshTimer = ref(null);
onShow(async () => {
if (battleId.value) {
if (!isEnded.value && (await isGameEnded(battleId.value))) return;
getCurrentGameAPI();
const refreshData = () => {
const lastAwakeTime = uni.getStorageSync("last-awake-time");
if (lastAwakeTime) {
getCurrentGameAPI();
} else {
clearInterval(refreshTimer.value);
}
};
refreshTimer.value = setInterval(refreshData, 2000);
}
});
onHide(() => {
if (refreshTimer.value) clearInterval(refreshTimer.value);
uni.setStorageSync("last-awake-time", Date.now());
});
</script>
<template>
<Container :title="title" :bgType="1">
<view class="container">
<BattleHeader v-if="!start" :players="players" />
<TestDistance v-if="!start" :guide="false" :isBattle="true" />
<ShootProgress
:show="start"
:start="start && startCount"
:tips="tips"
:total="90"
:melee="true"
:battleId="battleId"
/>
<view v-if="start" class="user-row">
<Avatar :src="user.avatar" :size="35" />
<BowPower />
</view>
<BowTarget
v-if="start"
:currentRound="scores.length"
:totalRound="12"
:scores="scores"
:stop="!startCount"
/>
<view :style="{ paddingBottom: '20px' }">
<PlayerScore
v-if="start"
v-for="(player, index) in playersSorted"
:key="index"
:player="player"
:scores="playersScores[player.id] || []"
/>
</view>
<ScreenHint
:show="halfTimeTip"
mode="small"
:onClose="() => (halfTimeTip = false)"
>
<view class="half-time-tip">
<text>上半场结束休息一下吧:</text>
<text>20秒后开始下半场</text>
</view>
</ScreenHint>
</view>
</Container>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
}
</style>

View File

@@ -12,7 +12,7 @@ import RoundEndTip from "@/components/RoundEndTip.vue";
import TestDistance from "@/components/TestDistance.vue";
import TeamAvatars from "@/components/TeamAvatars.vue";
import ShootProgress2 from "@/components/ShootProgress2.vue";
import { getCurrentGameAPI, laserCloseAPI, getBattleAPI } from "@/apis";
import { laserCloseAPI, getBattleAPI } from "@/apis";
import { isGameEnded, formatTimestamp } from "@/util";
import { MESSAGETYPES, MESSAGETYPESV2, roundsName } from "@/constants";
import audioManager from "@/audioManager";
@@ -43,8 +43,8 @@ const recoverData = (battleInfo, { force = false, arrowOnly = false } = {}) => {
battleId.value = battleInfo.matchId;
blueTeam.value = battleInfo.teams[1].players || [];
redTeam.value = battleInfo.teams[2].players || [];
start.value = battleInfo.status !== 0;
if (battleInfo.status === 0) {
start.value = false;
const readyRemain = (Date.now() - battleInfo.createTime) / 1000;
console.log(`对局已进行${readyRemain}`);
if (readyRemain > 0 && readyRemain < 15) {
@@ -55,7 +55,6 @@ const recoverData = (battleInfo, { force = false, arrowOnly = false } = {}) => {
return;
}
if (!arrowOnly) {
start.value = true;
currentShooterId.value = battleInfo.current.playerId;
const redPlayer = battleInfo.teams[2].players.find(
(item) => item.id === battleInfo.current.playerId

View File

@@ -56,31 +56,7 @@ function createWebSocket(token, onMessage) {
const msg = data.updates[0];
if (msg) {
console.log("收到消息:", getMessageTypeName(msg.constructor), msg);
if (msg.constructor === MESSAGETYPES.BackToGame) {
const pages = getCurrentPages();
const currentPage = pages[pages.length - 1];
if (
currentPage.route === "pages/battle-room" ||
currentPage.route === "pages/team-battle" ||
currentPage.route === "pages/melee-match"
) {
return;
}
const { battleInfo } = msg;
uni.setStorageSync("current-battle", battleInfo);
// console.log("----battleInfo", battleInfo);
if (battleInfo.config.mode === 1) {
uni.navigateTo({
url: `/pages/team-battle?battleId=${battleInfo.id}&gameMode=${battleInfo.config.battleMode}`,
});
} else if (battleInfo.config.mode === 2) {
uni.navigateTo({
url: `/pages/melee-match?battleId=${battleInfo.id}&gameMode=${battleInfo.config.battleMode}`,
});
}
} else if (msg.constructor === MESSAGETYPES.MatchOver) {
uni.$emit("game-over");
} else if (msg.constructor === MESSAGETYPES.RankUpdate) {
if (msg.constructor === MESSAGETYPES.RankUpdate) {
uni.setStorageSync("latestRank", msg.lvl);
} else if (msg.constructor === MESSAGETYPES.LvlUpdate) {
uni.setStorageSync("latestLvl", msg.lvl);