Files
shoot-miniprograms/src/pages/melee-match.vue
2025-07-17 16:14:30 +08:00

269 lines
7.7 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup>
import { ref, watch, onMounted, onUnmounted } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import Container from "@/components/Container.vue";
import BowTarget from "@/components/BowTarget.vue";
import ShootProgress from "@/components/ShootProgress.vue";
import Guide from "@/components/Guide.vue";
import BattleHeader from "@/components/BattleHeader.vue";
import Timer from "@/components/Timer.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 Matching from "@/components/Matching.vue";
import TestDistance from "@/components/TestDistance.vue";
import { matchGameAPI, readyGameAPI } from "@/apis";
import { MESSAGETYPES, getMessageTypeName } from "@/constants";
import useStore from "@/store";
import { storeToRefs } from "pinia";
const store = useStore();
const { user } = storeToRefs(store);
const gameType = ref(0);
const teamSize = ref(0);
const start = ref(false);
const startCount = ref(false);
const battleId = ref("");
const currentRound = ref(1);
const power = ref(0);
const scores = ref([]);
const tips = ref("即将开始...");
const seq = ref(0);
const timerSeq = ref(0);
const players = ref([]);
const playersSorted = ref([]);
const playersScores = ref({});
const halfTimeTip = ref(false);
const onComplete = ref(null);
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,
}
);
onLoad(async (options) => {
if (options.battleId) {
const battleInfo = uni.getStorageSync("current-battle");
console.log("----battleInfo", battleInfo);
if (battleInfo) {
battleId.value = battleInfo.id;
start.value = true;
startCount.value = true;
tips.value = "请连续射出6支箭";
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;
}
});
if (battleInfo.startTime) {
const remain = Date.now() / 1000 - battleInfo.startTime;
if (remain <= 90) {
setTimeout(() => {
uni.$emit("update-ramain", remain);
}, 300);
} else if (remain > 90 && remain <= 110) {
startCount.value = false;
tips.value = "准备下半场";
} else if (remain > 110) {
setTimeout(() => {
uni.$emit("update-ramain", remain - 110);
}, 300);
}
}
}
} else {
gameType.value = options.gameType;
teamSize.value = options.teamSize;
if (gameType.value && teamSize.value) {
await matchGameAPI(true, gameType.value, teamSize.value);
}
}
});
async function stopMatch() {
uni.$showHint(3);
}
async function readyToGo() {
if (battleId.value) {
await readyGameAPI(battleId.value);
scores.value = [];
start.value = true;
timerSeq.value = 0;
}
}
async function onReceiveMessage(messages = []) {
messages.forEach((msg) => {
if (
!msg.id ||
(battleId.value && msg.id === battleId.value) ||
msg.constructor === MESSAGETYPES.WaitForAllReady
) {
console.log("收到消息:", getMessageTypeName(msg.constructor), msg);
}
if (msg.constructor === MESSAGETYPES.WaitForAllReady) {
onComplete.value = () => {
// 这里会掉多次;
timerSeq.value += 1;
battleId.value = msg.id;
startCount.value = true;
players.value = [
...msg.groupUserStatus.redTeam,
...msg.groupUserStatus.blueTeam,
];
players.value.forEach((p) => {
playersScores.value[p.id] = [];
});
uni.$hideHint();
};
}
if (msg.id !== battleId.value) return;
if (msg.constructor === MESSAGETYPES.MeleeAllReady) {
start.value = true;
seq.value += 1;
timerSeq.value = 0;
tips.value = "请连续射出6支箭";
halfTimeTip.value = false;
}
if (msg.constructor === MESSAGETYPES.ShootResult) {
if (msg.userId === user.value.id) {
scores.value.push(msg.target);
power.value = msg.target.battery;
}
playersScores.value[msg.userId].push(msg.target);
}
if (msg.constructor === MESSAGETYPES.HalfTimeOver) {
uni.$emit("update-ramain", 0);
[...msg.groupUserStatus.redTeam, ...msg.groupUserStatus.blueTeam].forEach(
(player) => {
playersScores.value[player.id] = player.arrows;
}
);
startCount.value = false;
halfTimeTip.value = true;
tips.value = "准备下半场";
}
if (msg.constructor === MESSAGETYPES.MatchOver) {
uni.setStorageSync("last-battle", { ...msg.endStatus, lvl: msg.lvl });
if (msg.endStatus.noSaved) {
uni.showToast({
title: "游戏结束",
icon: "none",
});
uni.navigateBack();
} else {
uni.redirectTo({
url: `/pages/battle-result?battleId=${msg.id}`,
});
}
}
});
}
const onBack = () => {
if (battleId.value) {
uni.$showHint(2);
} else {
uni.$showHint(3);
}
};
onMounted(() => {
uni.setKeepScreenOn({
keepScreenOn: true,
});
uni.$on("socket-inbox", onReceiveMessage);
});
onUnmounted(() => {
uni.setKeepScreenOn({
keepScreenOn: false,
});
uni.$off("socket-inbox", onReceiveMessage);
if (gameType.value && teamSize.value) {
matchGameAPI(false, gameType.value, teamSize.value);
}
});
</script>
<template>
<Container
:title="battleId ? '大乱斗排位赛' : '搜索对手...'"
:bgType="1"
:onBack="onBack"
>
<view class="container">
<block v-if="battleId">
<BattleHeader :players="players" />
<TestDistance v-if="!start" :guide="false" />
<ShootProgress
v-if="start"
:seq="seq"
: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 :power="power" />
</view>
<BowTarget
v-if="start"
:currentRound="scores.length"
:totalRound="start ? 12 : 0"
:scores="scores"
:stop="!startCount"
/>
<view :style="{ paddingBottom: '20px' }">
<PlayerScore
v-if="start"
v-for="(player, index) in playersSorted"
:key="index"
:name="player.name"
:avatar="player.avatar"
:scores="playersScores[player.id] || []"
/>
</view>
<Timer :seq="timerSeq" :callBack="readyToGo" />
<ScreenHint
:show="halfTimeTip"
mode="small"
:onClose="() => (halfTimeTip = false)"
>
<view class="half-time-tip">
<text>上半场结束休息一下吧:</text>
<text>20秒后开始下半场</text>
</view>
</ScreenHint>
</block>
<block v-else>
<Matching
v-if="!battleId"
:stopMatch="stopMatch"
:onComplete="onComplete"
/>
</block>
</view>
<view :style="{ marginBottom: '20px' }">
<SButton v-if="battleId && !start" :onClick="readyToGo">准备完毕</SButton>
</view>
</Container>
</template>
<style scoped>
.container {
width: 100%;
height: 100%;
}
</style>