wangfukang 4 days ago
parent
commit
51046d07bb
  1. 112
      package1/planet/adventure.vue

112
package1/planet/adventure.vue

@ -41,7 +41,7 @@
<view class="game-card">
<view class="section-head">
<text>今日关卡</text>
<text>{{cleared ? '已通关' : '约2分钟'}}</text>
<text>{{cleared ? '已通关' : timerText}}</text>
</view>
<view class="start-strip" v-if="!playing && !cleared" @tap="startLevel">
<text>开始助推</text>
@ -54,7 +54,7 @@
<view class="tips">
<text>规则点选未被压住的卡牌底部槽内3张相同自动消除宇宙垃圾2张会触发清理</text>
<text>陷阱难度 · 剩余 {{remainingCount}} · 当前可点 {{availableCount}} · {{riskText}}</text>
<text>陷阱难度 · 剩余 {{remainingCount}} · 当前可点 {{availableCount}} · 倒计时 {{timeLeft}} · {{riskText}}</text>
</view>
<view class="debug-panel">
@ -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()
},

Loading…
Cancel
Save