Files
shoot-miniprograms/src/pages/melee-match.vue
2025-06-22 01:41:51 +08:00

201 lines
5.9 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, 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 { 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 matching = ref(false);
const start = ref(false);
const startCount = ref(false);
const battleId = ref("");
const currentRound = ref(1);
const totalRounds = ref(0);
const power = ref(0);
const scores = ref([]);
const tips = ref("即将开始...");
const seq = ref(0);
const timerSeq = ref(0);
const players = ref([]);
const playersScores = ref({});
const halfTimeTip = ref(false);
onLoad((options) => {
gameType.value = options.gameType;
teamSize.value = options.teamSize;
});
async function startMatch() {
await matchGameAPI(true, gameType.value, teamSize.value);
matching.value = true;
}
async function stopMatch() {
await matchGameAPI(false, gameType.value, teamSize.value);
matching.value = false;
}
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) {
// 这里会掉多次;
timerSeq.value += 1;
battleId.value = msg.id;
players.value = [
...msg.groupUserStatus.redTeam,
...msg.groupUserStatus.blueTeam,
];
players.value.forEach((p) => {
playersScores.value[p.id] = [];
});
}
if (!start.value && msg.constructor === MESSAGETYPES.ShootSyncMeArrowID) {
scores.value.push(msg.target);
power.value = msg.target.battery;
}
if (msg.id !== battleId.value) return;
if (msg.constructor === MESSAGETYPES.MeleeAllReady) {
start.value = true;
startCount.value = true;
seq.value += 1;
timerSeq.value = 0;
tips.value = "请连续射出6支箭";
scores.value = [];
}
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) {
startCount.value = false;
halfTimeTip.value = true;
tips.value = "准备下半场";
}
if (msg.constructor === MESSAGETYPES.MatchOver) {
uni.redirectTo({
url: `/pages/battle-result?battleId=${msg.id}`,
});
}
});
}
onMounted(() => {
uni.$on("socket-inbox", onReceiveMessage);
});
onUnmounted(() => {
uni.$off("socket-inbox", onReceiveMessage);
if (startMatch.value) {
matchGameAPI(false, gameType.value, teamSize.value);
}
});
</script>
<template>
<Container title="大乱斗排位赛" :bgType="1">
<view class="container">
<BattleHeader v-if="!start && players.length" :players="players" />
<Guide noBg v-if="!start && matching">
<view :style="{ display: 'flex', justifyContent: 'space-between' }">
<view :style="{ display: 'flex', flexDirection: 'column' }">
<text :style="{ color: '#fed847' }">请预先射几箭测试</text>
<text>请确保射击距离只有5米</text>
</view>
<BowPower :power="45" />
</view>
</Guide>
<ShootProgress v-if="start" :seq="seq" :start="startCount" :tips="tips" />
<view v-if="start" class="infos">
<Avatar :src="user.avatar" :size="35" />
<BowPower :power="power" />
</view>
<BowTarget
v-if="matching"
:showE="start"
:currentRound="scores.length"
:totalRound="start ? 12 : 0"
:scores="scores"
/>
<PlayerScore
v-if="start"
v-for="(player, index) in players"
:key="index"
:name="player.name"
:avatar="player.avatar"
:scores="playersScores[player.id]"
:done="playersScores[player.id].length === 12"
/>
<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>
</view>
<view :style="{ marginBottom: '20px' }">
<SButton v-if="!battleId" :onClick="matching ? stopMatch : startMatch">{{
matching ? "取消匹配" : "开始匹配"
}}</SButton>
<SButton v-if="battleId && !start" :onClick="readyToGo">准备完毕</SButton>
</view>
</Container>
</template>
<style scoped>
.container {
width: 100%;
}
.infos {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
padding-top: 15px;
}
.half-time-tip {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding-top: 52px;
}
.half-time-tip > text:last-child {
margin-top: 20px;
color: #fff9;
}
</style>