Compare commits
3
Commits
8b11e41957
...
0a678cb387
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0a678cb387 | ||
|
|
3c7467fbdf | ||
|
|
6aa3c6fd6c |
@@ -40,6 +40,8 @@ export const audioFils = {
|
||||
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutya59b6pu0ur4um.mp3",
|
||||
比赛开始:
|
||||
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcuu5z3a3lumkutske.mp3",
|
||||
下半场开始:
|
||||
"https://static.shelingxingqiu.com/shootmini/static/audio/%E4%B8%8B%E5%8D%8A%E5%9C%BA%E5%BC%80%E5%A7%8B.mp3",
|
||||
请开始射击:
|
||||
"https://static.shelingxingqiu.com/attachment/2025-09-17/dcutzdrl5u0iromqhf.mp3",
|
||||
射击无效:
|
||||
|
||||
@@ -207,7 +207,7 @@ const isMember = (player = {}) => player.vip === true || player.sVip === true;
|
||||
justify-content: center;
|
||||
color: #fff9;
|
||||
font-size: 12px;
|
||||
padding-top: 7px;
|
||||
/* padding-top: 7px; */
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.player-name {
|
||||
|
||||
@@ -34,6 +34,7 @@ const phase = ref("idle");
|
||||
const activePlayKey = ref("");
|
||||
const animationKey = ref("");
|
||||
const impactEmitted = ref(false);
|
||||
const activeShot = ref(null);
|
||||
let timers = [];
|
||||
|
||||
const isActive = computed(() => phase.value !== "idle");
|
||||
@@ -54,9 +55,17 @@ const safeTargetSize = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
function hasShotPoint(shot) {
|
||||
const x = Number(shot?.x);
|
||||
const y = Number(shot?.y);
|
||||
return Number.isFinite(x) && Number.isFinite(y);
|
||||
}
|
||||
|
||||
const effectiveShot = computed(() => activeShot.value || props.shot);
|
||||
|
||||
const shotPoint = computed(() => {
|
||||
const x = Number(props.shot?.x);
|
||||
const y = Number(props.shot?.y);
|
||||
const x = Number(effectiveShot.value?.x);
|
||||
const y = Number(effectiveShot.value?.y);
|
||||
return {
|
||||
x: Number.isFinite(x) ? x : 0,
|
||||
y: Number.isFinite(y) ? y : 0,
|
||||
@@ -179,16 +188,20 @@ function finish(playKey) {
|
||||
clearTimers();
|
||||
phase.value = "idle";
|
||||
activePlayKey.value = "";
|
||||
activeShot.value = null;
|
||||
emit("complete", playKey);
|
||||
}
|
||||
|
||||
function play() {
|
||||
if (!props.playKey || !props.shot || !props.shot.ring) return;
|
||||
if (!props.playKey || !props.shot || !props.shot.ring || !hasShotPoint(props.shot)) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearTimers();
|
||||
activePlayKey.value = props.playKey;
|
||||
animationKey.value = `${props.playKey}`;
|
||||
impactEmitted.value = false;
|
||||
activeShot.value = { ...props.shot };
|
||||
phase.value = "playing";
|
||||
|
||||
queueTimer(() => {
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<script setup>
|
||||
import { ref, watch, onMounted, onBeforeUnmount, computed, nextTick } from "vue";
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
computed,
|
||||
nextTick,
|
||||
getCurrentInstance,
|
||||
} from "vue";
|
||||
import PointSwitcher from "@/components/PointSwitcher.vue";
|
||||
import BowShotEffect from "@/components/BowShotEffect.vue";
|
||||
|
||||
@@ -56,8 +64,6 @@ const props = defineProps({
|
||||
const pMode = ref(true);
|
||||
const latestOne = ref(null);
|
||||
const bluelatestOne = ref(null);
|
||||
const prevScores = ref([]);
|
||||
const prevBlueScores = ref([]);
|
||||
const timer = ref(null);
|
||||
const dirTimer = ref(null);
|
||||
const angle = ref(null);
|
||||
@@ -66,7 +72,9 @@ const shotEffect = ref(null);
|
||||
const hiddenRedLatestKey = ref("");
|
||||
const hiddenBlueLatestKey = ref("");
|
||||
const targetShaking = ref(false);
|
||||
const targetSize = ref({ width: 0, height: 0 });
|
||||
const shakeTimer = ref(null);
|
||||
const instance = getCurrentInstance();
|
||||
const ROUND_TIP_OFFSET_Y = -32;
|
||||
const EXPERIENCE_TIP_OFFSET_Y = -68;
|
||||
|
||||
@@ -82,8 +90,14 @@ function buildShotEffectKey(team, shot, index) {
|
||||
].join("-");
|
||||
}
|
||||
|
||||
function hasShotPoint(shot) {
|
||||
const x = Number(shot?.x);
|
||||
const y = Number(shot?.y);
|
||||
return Number.isFinite(x) && Number.isFinite(y);
|
||||
}
|
||||
|
||||
function shouldPlayShotEffect(shot) {
|
||||
return props.isSvip && !!shot && Number(shot.ring) > 0;
|
||||
return props.isSvip && !!shot && Number(shot.ring) > 0 && hasShotPoint(shot);
|
||||
}
|
||||
|
||||
function clearTipTimer() {
|
||||
@@ -154,6 +168,29 @@ function shakeTarget() {
|
||||
});
|
||||
}
|
||||
|
||||
function updateTargetSize() {
|
||||
nextTick(() => {
|
||||
const query = instance?.proxy
|
||||
? uni.createSelectorQuery().in(instance.proxy)
|
||||
: uni.createSelectorQuery();
|
||||
|
||||
query
|
||||
.select(".target")
|
||||
.boundingClientRect((rect) => {
|
||||
const width = Number(rect?.width);
|
||||
const height = Number(rect?.height);
|
||||
if (!Number.isFinite(width) || !Number.isFinite(height)) return;
|
||||
if (width <= 0 || height <= 0) return;
|
||||
targetSize.value = { width, height };
|
||||
})
|
||||
.exec();
|
||||
});
|
||||
}
|
||||
|
||||
function handleWindowResize() {
|
||||
updateTargetSize();
|
||||
}
|
||||
|
||||
function shouldHideRedHit(index) {
|
||||
return !!hiddenRedLatestKey.value && index === props.scores.length - 1;
|
||||
}
|
||||
@@ -163,46 +200,44 @@ function shouldHideBlueHit(index) {
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.scores,
|
||||
(newVal) => {
|
||||
if (newVal.length - prevScores.value.length === 1) {
|
||||
const latestShot = newVal[newVal.length - 1];
|
||||
() => props.scores.length,
|
||||
(newLen, oldLen) => {
|
||||
if (newLen === oldLen + 1) {
|
||||
const latestShot = props.scores[newLen - 1];
|
||||
if (shouldPlayShotEffect(latestShot)) {
|
||||
triggerShotEffect("red", latestShot, newVal.length - 1);
|
||||
triggerShotEffect("red", latestShot, newLen - 1);
|
||||
} else {
|
||||
showShotTip("red", latestShot);
|
||||
}
|
||||
} else if (newVal.length <= prevScores.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLen < oldLen) {
|
||||
latestOne.value = null;
|
||||
hiddenRedLatestKey.value = "";
|
||||
if (shotEffect.value?.team === "red") shotEffect.value = null;
|
||||
}
|
||||
prevScores.value = [...newVal];
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
watch(
|
||||
() => props.blueScores,
|
||||
(newVal) => {
|
||||
if (newVal.length - prevBlueScores.value.length === 1) {
|
||||
const latestShot = newVal[newVal.length - 1];
|
||||
() => props.blueScores.length,
|
||||
(newLen, oldLen) => {
|
||||
if (newLen === oldLen + 1) {
|
||||
const latestShot = props.blueScores[newLen - 1];
|
||||
if (shouldPlayShotEffect(latestShot)) {
|
||||
triggerShotEffect("blue", latestShot, newVal.length - 1);
|
||||
triggerShotEffect("blue", latestShot, newLen - 1);
|
||||
} else {
|
||||
showShotTip("blue", latestShot);
|
||||
}
|
||||
} else if (newVal.length <= prevBlueScores.value.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (newLen < oldLen) {
|
||||
bluelatestOne.value = null;
|
||||
hiddenBlueLatestKey.value = "";
|
||||
if (shotEffect.value?.team === "blue") shotEffect.value = null;
|
||||
}
|
||||
prevBlueScores.value = [...newVal];
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -349,6 +384,8 @@ async function onReceiveMessage(message) {
|
||||
|
||||
onMounted(() => {
|
||||
uni.$on("socket-inbox", onReceiveMessage);
|
||||
updateTargetSize();
|
||||
if (uni.onWindowResize) uni.onWindowResize(handleWindowResize);
|
||||
});
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -365,6 +402,7 @@ onBeforeUnmount(() => {
|
||||
shakeTimer.value = null;
|
||||
}
|
||||
uni.$off("socket-inbox", onReceiveMessage);
|
||||
if (uni.offWindowResize) uni.offWindowResize(handleWindowResize);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -461,6 +499,9 @@ onBeforeUnmount(() => {
|
||||
:shot="shotEffect && shotEffect.shot"
|
||||
:playKey="shotEffect ? shotEffect.key : ''"
|
||||
:targetRadius="safeTargetRadius"
|
||||
:targetWidth="targetSize.width"
|
||||
:targetHeight="targetSize.height"
|
||||
:hitOffsetPx="currentHitRadiusPx"
|
||||
@impact="shakeTarget"
|
||||
@complete="completeShotEffect"
|
||||
/>
|
||||
|
||||
@@ -38,6 +38,10 @@ const props = defineProps({
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
halfRest: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
onStop: {
|
||||
type: Function,
|
||||
default: () => {},
|
||||
@@ -136,8 +140,9 @@ const updateSound = () => {
|
||||
async function onReceiveMessage(msg) {
|
||||
if (Array.isArray(msg)) return;
|
||||
if (msg.type === MESSAGETYPESV2.BattleStart) {
|
||||
const audioKey = props.melee && (halfTime.value || props.halfRest) ? "下半场开始" : "比赛开始";
|
||||
halfTime.value = false;
|
||||
audioManager.play("比赛开始");
|
||||
audioManager.play(audioKey);
|
||||
} else if (msg.type === MESSAGETYPESV2.BattleEnd) {
|
||||
audioManager.play("比赛结束", false);
|
||||
} else if (msg.type === MESSAGETYPESV2.ShootResult) {
|
||||
|
||||
@@ -295,6 +295,7 @@ onShow(async () => {
|
||||
:tips="tips"
|
||||
:total="90"
|
||||
:melee="true"
|
||||
:halfRest="halfRest"
|
||||
:battleId="battleId"
|
||||
/>
|
||||
<view v-if="start" class="user-row">
|
||||
|
||||
+37
-13
@@ -145,6 +145,23 @@ const getMenuPrice = (item) => {
|
||||
return value ? String(value).replace("¥", "").replace("¥", "") : "";
|
||||
};
|
||||
|
||||
const getMenuSortValue = (item) => {
|
||||
const value = item && item.sort;
|
||||
if (value === undefined || value === null || value === "") return Number.MAX_SAFE_INTEGER;
|
||||
const sort = Number(value);
|
||||
return Number.isFinite(sort) ? sort : Number.MAX_SAFE_INTEGER;
|
||||
};
|
||||
|
||||
const sortMenusBySort = (menus = []) => {
|
||||
return menus
|
||||
.map((item, index) => ({ item, index }))
|
||||
.sort((a, b) => {
|
||||
const sortDiff = getMenuSortValue(a.item) - getMenuSortValue(b.item);
|
||||
return sortDiff || a.index - b.index;
|
||||
})
|
||||
.map(({ item }) => item);
|
||||
};
|
||||
|
||||
const getMenuType = (item) => {
|
||||
const vipType = Number(item.vipType);
|
||||
if (vipType === 1) return "normal";
|
||||
@@ -167,7 +184,7 @@ const getPackageMonths = (name) => {
|
||||
};
|
||||
|
||||
const matchPackageSource = (type, pack, index) => {
|
||||
const menus = configMenus.value;
|
||||
const menus = sortMenusBySort(configMenus.value);
|
||||
const typedMenus = menus.filter((item) => {
|
||||
const menuType = getMenuType(item);
|
||||
if (type.key === "super") return menuType === "super";
|
||||
@@ -184,17 +201,24 @@ const matchPackageSource = (type, pack, index) => {
|
||||
};
|
||||
|
||||
const getPackages = (type) => {
|
||||
return type.packages.map((item, index) => {
|
||||
const source = matchPackageSource(type, item, index);
|
||||
return {
|
||||
...item,
|
||||
source,
|
||||
name: source ? getMenuName(source) || item.name : item.name,
|
||||
price: source ? getMenuPrice(source) || item.price : item.price,
|
||||
icon: source && source.icon,
|
||||
id: source && source.id,
|
||||
};
|
||||
});
|
||||
return type.packages
|
||||
.map((item, index) => {
|
||||
const source = matchPackageSource(type, item, index);
|
||||
return {
|
||||
...item,
|
||||
source,
|
||||
name: source ? getMenuName(source) || item.name : item.name,
|
||||
price: source ? getMenuPrice(source) || item.price : item.price,
|
||||
icon: source && source.icon,
|
||||
id: source && source.id,
|
||||
};
|
||||
})
|
||||
.map((item, index) => ({ item, index }))
|
||||
.sort((a, b) => {
|
||||
const sortDiff = getMenuSortValue(a.item.source) - getMenuSortValue(b.item.source);
|
||||
return sortDiff || a.index - b.index;
|
||||
})
|
||||
.map(({ item }) => item);
|
||||
};
|
||||
|
||||
const currentPackages = computed(() => {
|
||||
@@ -463,7 +487,7 @@ onShow(loadVipConfig);
|
||||
<view class="package-list">
|
||||
<view
|
||||
v-for="(pack, index) in getPackages(type)"
|
||||
:key="`${type.key}-${pack.name}`"
|
||||
:key="`${type.key}-${pack.id || pack.name}`"
|
||||
class="package-card"
|
||||
:class="{ 'package-card--active': selectedPackageIndex === index }"
|
||||
@click="selectPackage(index)"
|
||||
|
||||
Reference in New Issue
Block a user