完成打乱斗数据调试

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"); 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 () => { export const getPointBookConfigAPI = async () => {
return request("GET", "/user/score/sheet/option"); return request("GET", "/user/score/sheet/option");
}; };
@@ -554,8 +548,7 @@ export const getReadyAPI = (roomId) => {
}; };
export const getBattleAPI = async (battleId) => { export const getBattleAPI = async (battleId) => {
const result = await request("POST", "/user/match/info", { return request("POST", "/user/match/info", {
id: battleId, id: battleId,
}); });
return result;
}; };

View File

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

View File

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

View File

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

View File

@@ -56,7 +56,6 @@ const signin = () => {
}; };
const loading = ref(false); const loading = ref(false);
const showLoader = ref(false);
const pointBook = ref(null); const pointBook = ref(null);
const showProgress = ref(false); const showProgress = ref(false);
const heat = ref(0); const heat = ref(0);
@@ -80,20 +79,12 @@ onMounted(() => {
pointBook.value = uni.getStorageSync("last-point-book"); 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") { if (currentPage.route === "pages/team-battle") {
showProgress.value = true; showProgress.value = true;
} }
uni.$on("update-header-loading", updateLoading);
uni.$on("update-hot", updateHot); uni.$on("update-hot", updateHot);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
uni.$off("update-header-loading", updateLoading);
uni.$off("update-hot", updateHot); uni.$off("update-hot", updateHot);
}); });
</script> </script>
@@ -161,12 +152,6 @@ onBeforeUnmount(() => {
</view> </view>
</block> </block>
</view> </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"> <view v-if="pointBook" class="point-book-info">
<text>{{ pointBook.bowType.name }}</text> <text>{{ pointBook.bowType.name }}</text>
<text>{{ pointBook.distance }} 米</text> <text>{{ pointBook.distance }} 米</text>
@@ -230,14 +215,6 @@ onBeforeUnmount(() => {
font-size: 16px; font-size: 16px;
color: #fff; 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 { .point-book-info {
color: #333; color: #333;
position: fixed; position: fixed;

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,7 @@
<script setup> <script setup>
import { ref, watch, onMounted, onBeforeUnmount, computed } from "vue"; import { ref, watch, onMounted, onBeforeUnmount, computed } from "vue";
import audioManager from "@/audioManager"; import audioManager from "@/audioManager";
import { MESSAGETYPES } from "@/constants"; import { MESSAGETYPESV2 } from "@/constants";
import { getDirectionText } from "@/util"; import { getDirectionText } from "@/util";
import useStore from "@/store"; import useStore from "@/store";
@@ -116,62 +116,77 @@ const updateSound = () => {
audioManager.setMuted(!sound.value); audioManager.setMuted(!sound.value);
}; };
async function onReceiveMessage(messages = []) { async function onReceiveMessage(msg) {
if (ended.value) return; if (Array.isArray(msg)) return;
messages.forEach((msg) => { if (msg.type === MESSAGETYPESV2.BattleStart) {
if ( audioManager.play("比赛开始");
(props.battleId && msg.constructor === MESSAGETYPES.ShootResult) || } else if (msg.type === MESSAGETYPESV2.BattleEnd) {
(!props.battleId && msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) audioManager.play("比赛结束");
) { } else if (msg.type === MESSAGETYPESV2.ShootResult) {
if (props.melee && msg.userId !== user.value.id) return; if (msg.shootData.playerId !== user.value.id) return;
if (!halfTime.value && msg.target) { if (msg.shootData) {
let key = []; let key = [];
key.push(msg.target.ring ? `${msg.target.ring}` : "未上靶"); key.push(msg.shootData.ring ? `${msg.shootData.ring}` : "未上靶");
if (!msg.target.ring) if (!msg.shootData.ring)
key.push(`${getDirectionText(msg.target.angle)}调整`); key.push(`${getDirectionText(msg.shootData.angle)}调整`);
audioManager.play(key); audioManager.play(key, false);
}
} 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;
} }
}); }
// 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) => { const playSound = (key) => {

View File

@@ -37,7 +37,7 @@ export const MESSAGETYPESV2 = {
ShootResult: 4, ShootResult: 4,
NewRound: 5, NewRound: 5,
BattleEnd: 6, BattleEnd: 6,
halfRest: 7, HalfRest: 7,
}; };
export const topThreeColors = ["#FFD947", "#D2D2D2", "#FFA515"]; 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" "path": "pages/point-book-edit"
@@ -102,9 +105,6 @@
{ {
"path": "pages/rank-list" "path": "pages/rank-list"
}, },
{
"path": "pages/melee-match"
},
{ {
"path": "pages/match-detail" "path": "pages/match-detail"
}, },

View File

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

View File

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

View File

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

View File

@@ -29,14 +29,13 @@ async function onReceiveMessage(messages = []) {
...msg.groupUserStatus.blueTeam, ...msg.groupUserStatus.blueTeam,
]); ]);
} }
uni.removeStorageSync("current-battle");
if (gameType.value == 1) { if (gameType.value == 1) {
uni.redirectTo({ uni.redirectTo({
url: `/pages/team-battle?battleId=${msg.id}&gameMode=2`, url: `/pages/team-battle?battleId=${msg.id}&gameMode=2`,
}); });
} else if (gameType.value == 2) { } else if (gameType.value == 2) {
uni.redirectTo({ 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 Container from "@/components/Container.vue";
import BowTarget from "@/components/BowTarget.vue"; import BowTarget from "@/components/BowTarget.vue";
import Avatar from "@/components/Avatar.vue"; import Avatar from "@/components/Avatar.vue";
import { getGameAPI } from "@/apis"; import { getBattleAPI } from "@/apis";
import useStore from "@/store"; import useStore from "@/store";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const store = useStore(); const { user } = storeToRefs(useStore());
const { user } = storeToRefs(store); const currentUser = ref({
const scores = ref([]); arrows: [],
const currentUser = ref({}); });
const data = ref({}); const players = 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;
}
};
onLoad(async (options) => { onLoad(async (options) => {
if (options.battleId) { if (!options.id) return;
const result = await getGameAPI(options.battleId); const result = await getBattleAPI(options.battleId || "59090720979554304");
data.value = result; players.value = result.resultList.map((item, index) => {
if (result.players && result.players[0]) { const plist = result.teams[0] ? result.teams[0].players : [];
onSelect(result.players[0].playerId); 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> </script>
@@ -33,22 +44,26 @@ onLoad(async (options) => {
<template> <template>
<Container title="靶纸"> <Container title="靶纸">
<view class="container"> <view class="container">
<image src="../static/battle-header-melee.png" mode="widthFix" /> <image
<view class="players" v-if="data.players"> src="../static/battle-header-melee.png"
mode="widthFix"
:style="{ top: '-50rpx' }"
/>
<view class="players">
<view <view
v-for="(player, index) in data.players" v-for="(player, index) in players"
:key="index" :key="index"
:style="{ :style="{
width: `${Math.max(100 / data.players.length, 18)}vw`, width: `${Math.max(100 / players.length, 18)}vw`,
color: player.playerId === currentUser.playerId ? '#000' : '#fff9', color: player.userId === currentUser.userId ? '#000' : '#fff9',
}" }"
@click="() => onSelect(player.playerId)" @click="currentUser = player"
> >
<image <image
v-if="player.playerId === currentUser.playerId" v-if="player.userId === currentUser.userId"
src="../static/player-bg2.png" src="../static/player-bg2.png"
:style="{ :style="{
width: `${Math.max(100 / data.players.length, 18)}vw`, width: `${Math.max(100 / players.length, 18)}vw`,
}" }"
class="player-bg" class="player-bg"
/> />
@@ -57,18 +72,20 @@ onLoad(async (options) => {
</view> </view>
</view> </view>
<view :style="{ marginTop: '10px' }"> <view :style="{ marginTop: '10px' }">
<BowTarget :scores="scores" /> <BowTarget :scores="currentUser.arrows" />
</view> </view>
<view class="score-text" <view class="score-text"
><text :style="{ color: '#fed847' }">{{ scores.length }}</text ><text :style="{ color: '#fed847' }">{{
currentUser.arrows.length
}}</text
>支箭<text :style="{ color: '#fed847' }">{{ >支箭<text :style="{ color: '#fed847' }">{{
scores.reduce((last, next) => last + next.ring, 0) currentUser.arrows.reduce((last, next) => last + next.ring, 0)
}}</text }}</text
></view ></view
> >
<view class="score-row"> <view class="score-row" v-if="currentUser.arrows">
<view <view
v-for="(score, index) in scores" v-for="(score, index) in currentUser.arrows"
:key="index" :key="index"
class="score-item" class="score-item"
:style="{ width: '13vw', height: '13vw' }" :style="{ width: '13vw', height: '13vw' }"
@@ -97,7 +114,7 @@ onLoad(async (options) => {
display: flex; display: flex;
width: 100%; width: 100%;
overflow-x: auto; overflow-x: auto;
margin-top: 25px; margin-top: 50rpx;
} }
.players::-webkit-scrollbar { .players::-webkit-scrollbar {
width: 0; 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 TestDistance from "@/components/TestDistance.vue";
import TeamAvatars from "@/components/TeamAvatars.vue"; import TeamAvatars from "@/components/TeamAvatars.vue";
import ShootProgress2 from "@/components/ShootProgress2.vue"; import ShootProgress2 from "@/components/ShootProgress2.vue";
import { getCurrentGameAPI, laserCloseAPI, getBattleAPI } from "@/apis"; import { laserCloseAPI, getBattleAPI } from "@/apis";
import { isGameEnded, formatTimestamp } from "@/util"; import { isGameEnded, formatTimestamp } from "@/util";
import { MESSAGETYPES, MESSAGETYPESV2, roundsName } from "@/constants"; import { MESSAGETYPES, MESSAGETYPESV2, roundsName } from "@/constants";
import audioManager from "@/audioManager"; import audioManager from "@/audioManager";
@@ -43,8 +43,8 @@ const recoverData = (battleInfo, { force = false, arrowOnly = false } = {}) => {
battleId.value = battleInfo.matchId; battleId.value = battleInfo.matchId;
blueTeam.value = battleInfo.teams[1].players || []; blueTeam.value = battleInfo.teams[1].players || [];
redTeam.value = battleInfo.teams[2].players || []; redTeam.value = battleInfo.teams[2].players || [];
start.value = battleInfo.status !== 0;
if (battleInfo.status === 0) { if (battleInfo.status === 0) {
start.value = false;
const readyRemain = (Date.now() - battleInfo.createTime) / 1000; const readyRemain = (Date.now() - battleInfo.createTime) / 1000;
console.log(`对局已进行${readyRemain}`); console.log(`对局已进行${readyRemain}`);
if (readyRemain > 0 && readyRemain < 15) { if (readyRemain > 0 && readyRemain < 15) {
@@ -55,7 +55,6 @@ const recoverData = (battleInfo, { force = false, arrowOnly = false } = {}) => {
return; return;
} }
if (!arrowOnly) { if (!arrowOnly) {
start.value = true;
currentShooterId.value = battleInfo.current.playerId; currentShooterId.value = battleInfo.current.playerId;
const redPlayer = battleInfo.teams[2].players.find( const redPlayer = battleInfo.teams[2].players.find(
(item) => item.id === battleInfo.current.playerId (item) => item.id === battleInfo.current.playerId

View File

@@ -56,31 +56,7 @@ function createWebSocket(token, onMessage) {
const msg = data.updates[0]; const msg = data.updates[0];
if (msg) { if (msg) {
console.log("收到消息:", getMessageTypeName(msg.constructor), msg); console.log("收到消息:", getMessageTypeName(msg.constructor), msg);
if (msg.constructor === MESSAGETYPES.BackToGame) { if (msg.constructor === MESSAGETYPES.RankUpdate) {
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) {
uni.setStorageSync("latestRank", msg.lvl); uni.setStorageSync("latestRank", msg.lvl);
} else if (msg.constructor === MESSAGETYPES.LvlUpdate) { } else if (msg.constructor === MESSAGETYPES.LvlUpdate) {
uni.setStorageSync("latestLvl", msg.lvl); uni.setStorageSync("latestLvl", msg.lvl);