From 51046d07bb00bef71477e2ed17487e6ebea7b673 Mon Sep 17 00:00:00 2001 From: wangfukang <15630117759@163.com> Date: Tue, 2 Jun 2026 15:49:48 +0800 Subject: [PATCH] 1 --- package1/planet/adventure.vue | 112 +++++++++++++++++++++++++++++++--- 1 file changed, 105 insertions(+), 7 deletions(-) diff --git a/package1/planet/adventure.vue b/package1/planet/adventure.vue index f6648f5..9eb3f9a 100644 --- a/package1/planet/adventure.vue +++ b/package1/planet/adventure.vue @@ -41,7 +41,7 @@ 今日关卡 - {{cleared ? '已通关' : '约2分钟'}} + {{cleared ? '已通关' : timerText}} 开始助推 @@ -54,7 +54,7 @@ 规则:点选未被压住的卡牌,底部槽内3张相同自动消除,宇宙垃圾2张会触发清理。 - 陷阱难度 · 剩余 {{remainingCount}} 张 · 当前可点 {{availableCount}} 张 · {{riskText}} + 陷阱难度 · 剩余 {{remainingCount}} 张 · 当前可点 {{availableCount}} 张 · 倒计时 {{timeLeft}} 秒 · {{riskText}} @@ -147,6 +147,9 @@ cards: [], slots: [], slotLimit: 7, + timeLimit: 150, + timeLeft: 150, + timer: null, moveCount: 0, modal: { show: false, title: '', sub: '' } } @@ -205,6 +208,9 @@ } return cells }, + timerText() { + return this.playing ? `剩余 ${this.timeLeft} 秒` : '限时150秒' + }, riskText() { if (this.slots.length >= this.slotLimit - 1) return '危险:差1格就满' if (this.slots.length >= this.slotLimit - 2) return '注意槽位' @@ -226,6 +232,22 @@ this.loadHome() this.buildLevel() }, + onUnload() { + this.clearTimer() + }, + onHide() { + this.clearTimer() + }, + onShow() { + if (!this.playing || !this.startTs) return + const elapsed = Math.floor((Date.now() - this.startTs) / 1000) + this.timeLeft = Math.max(0, this.timeLimit - elapsed) + if (this.timeLeft <= 0) { + this.failLevel('时间到了', '超过150秒,本关失败。不消耗次数,调整顺序再来一次。') + } else { + this.startTimer(false) + } + }, methods: { loadColleges() { this.tui.request('/app/planet/college/list', 'POST', { @@ -278,6 +300,7 @@ this.startTs = Date.now() this.buildLevel() if (!this.cards.length) this.buildLevel() + this.startTimer() }) }, buildLevel() { @@ -297,6 +320,9 @@ if (!cards.length) { cards = this.assignEmergencyIcons(this.buildCardPositions(this.levelSeed() + '_emergency'), icons, garbageIcons) } + if (this.shouldBuildUnwinnableLevel()) { + this.applyUnwinnableTwist(cards, icons, garbageIcons) + } this.cards = cards this.slots = [] this.moveCount = 0 @@ -448,6 +474,47 @@ }) return positions }, + shouldBuildUnwinnableLevel() { + const rand = this.seededRandom(this.levelSeed() + '_win_rate') + return rand() < 0.5 + }, + applyUnwinnableTwist(cards, icons, garbageIcons) { + const iconMap = {} + cards.forEach(card => { + if (this.isGarbageIcon(card.icon)) return + if (!iconMap[card.icon]) iconMap[card.icon] = [] + iconMap[card.icon].push(card) + }) + const candidates = Object.keys(iconMap).filter(icon => iconMap[icon].length >= 3) + if (!candidates.length) return + const rand = this.seededRandom(this.levelSeed() + '_unwinnable') + this.shuffleWithRandom(candidates, rand) + const trapIcons = candidates.slice(0, 4) + trapIcons.forEach((targetIcon, trapIndex) => { + const group = iconMap[targetIcon].slice().sort((a, b) => { + const scoreA = (a.locked ? 10 : 0) + a.layer * 3 + (this.isCenterCard(a) ? 2 : 0) + const scoreB = (b.locked ? 10 : 0) + b.layer * 3 + (this.isCenterCard(b) ? 2 : 0) + return scoreB - scoreA + }) + const key = group[0] + const replacement = garbageIcons[trapIndex % garbageIcons.length] + if (!key || !replacement) return + key.icon = replacement + key.displayIcon = this.displayIcon(key.icon) + key.unwinnableKey = true + key.trap = true + this.exposePairEarly(iconMap[targetIcon].filter(card => card.id !== key.id).slice(0, 2), trapIndex) + }) + }, + exposePairEarly(pair, index) { + const baseX = 74 + index * 88 + pair.forEach((card, i) => { + card.layer = 2 + card.x = baseX + i * 8 + card.y = 26 + i * 78 + card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${this.cardZIndex(card)};` + }) + }, repositionKeyCovers(cards, seed) { const rand = this.seededRandom(seed + '_cover') const keys = cards.filter(card => card.keyCard && card.layer === 0 && this.isCenterCard(card)).slice(0, 10) @@ -572,19 +639,42 @@ this.moveCount++ this.tryClear(card.icon) this.refreshCardState() - if (this.cards.every(item => item.removed || item.selected)) { + if (this.cards.every(item => item.removed) && this.slots.length === 0) { this.finishClear() + } else if (this.cards.every(item => item.removed || item.selected) && this.slots.length > 0) { + this.failLevel() } else if (this.slots.length >= this.slotLimit) { this.failLevel() } }, + startTimer(reset) { + this.clearTimer() + if (reset !== false) this.timeLeft = this.timeLimit + this.timer = setInterval(() => { + if (!this.playing) { + this.clearTimer() + return + } + const elapsed = Math.floor((Date.now() - this.startTs) / 1000) + this.timeLeft = Math.max(0, this.timeLimit - elapsed) + if (this.timeLeft <= 0) { + this.failLevel('时间到了', '超过150秒,本关失败。不消耗次数,调整顺序再来一次。') + } + }, 1000) + }, + clearTimer() { + if (this.timer) { + clearInterval(this.timer) + this.timer = null + } + }, tryClear(icon) { if (this.isGarbageIcon(icon)) { - const garbage = this.slots.filter(item => item.icon === icon) + const garbage = this.slots.filter(item => item.icon === icon && !item.unwinnableKey) if (garbage.length < 2) return let removedGarbage = 0 this.slots = this.slots.filter(item => { - if (item.icon === icon && removedGarbage < 2) { + if (item.icon === icon && !item.unwinnableKey && removedGarbage < 2) { item.removed = true removedGarbage++ return false @@ -615,6 +705,7 @@ const duration = Math.max(20, Math.floor((Date.now() - this.startTs) / 1000)) const progress = Math.max(100, 260 - duration + Math.max(0, 80 - this.moveCount)) this.playing = false + this.clearTimer() this.tui.request('/app/planet/adventure/submit', 'POST', { userId: this.userId, regionId: this.regionId, @@ -638,13 +729,20 @@ } }) }, - failLevel() { + failLevel(title, sub) { this.playing = false - this.modal = { show: true, title: '槽位满了', sub: '本关失败,不消耗次数。换个顺序再推一次。' } + this.clearTimer() + this.modal = { + show: true, + title: title || '槽位满了', + sub: sub || '本关失败,不消耗次数。换个顺序再推一次。' + } }, resetLevel() { if (this.cleared) return this.playing = false + this.clearTimer() + this.timeLeft = this.timeLimit this.buildLevel() if (!this.cards.length) this.buildLevel() },