diff --git a/package1/planet/adventure.vue b/package1/planet/adventure.vue index c4b25da..83bc1d4 100644 --- a/package1/planet/adventure.vue +++ b/package1/planet/adventure.vue @@ -88,6 +88,7 @@ :style="card.style" @tap="pickCard(card)"> {{card.displayIcon}} + {{card.layer}} @@ -162,8 +163,8 @@ cards: [], slots: [], slotLimit: 7, - timeLimit: 150, - timeLeft: 150, + timeLimit: 300, + timeLeft: 300, timer: null, roundSeed: '', moveCount: 0, @@ -199,7 +200,9 @@ return Math.max(16, Math.min(100, Math.round((mine.score || 0) * 100 / top.score))) }, visibleCards() { - return this.cards.filter(item => !item.removed && !item.selected) + return this.cards + .filter(item => !item.removed && !item.selected) + .sort((a,b)=>this.cardZIndex(a)-this.cardZIndex(b)) }, remainingCount() { return this.cards.filter(item => !item.removed && !item.selected).length @@ -225,7 +228,7 @@ return cells }, timerText() { - return this.playing ? `${this.timeLeft}s` : '150s限时' + return this.playing ? `${this.timeLeft}s` : '300s限时' }, timerPercent() { return Math.max(0, Math.min(100, Math.round(this.timeLeft * 100 / this.timeLimit))) @@ -240,7 +243,7 @@ if (!this.playing) return '开始后进入倒计时' if (this.timeLeft <= 30) return '最后冲刺,槽位别满' if (this.timeLeft <= 60) return '时间紧张,加快决策' - return '150秒内完成助推' + return '300秒内完成助推' }, riskText() { if (this.slots.length >= this.slotLimit - 1) return '危险:差1格就满' @@ -274,7 +277,7 @@ const elapsed = Math.floor((Date.now() - this.startTs) / 1000) this.timeLeft = Math.max(0, this.timeLimit - elapsed) if (this.timeLeft <= 0) { - this.failLevel('时间到了', '超过150秒,本关失败。不消耗次数,调整顺序再来一次。') + this.failLevel('时间到了', '超过300秒,本关失败。不消耗次数,调整顺序再来一次。') } else { this.startTimer(false) } @@ -356,6 +359,9 @@ if (this.shouldBuildTrapLayout()) { this.applyTrapLayout(cards) } + if (!this.validateTileCounts(cards)) { + this.repairTileCounts(cards, icons, garbageIcons) + } this.cards = cards this.slots = [] this.moveCount = 0 @@ -481,6 +487,7 @@ assigned[card.id] = true }) } + this.cards = cards this.repositionKeyCovers(cards, seed) const freeCards = cards.filter(card => !assigned[card.id]) const garbageCards = this.seededShuffle(freeCards.slice(), seed + '_garbage').slice(0, 6) @@ -511,11 +518,36 @@ }) return positions }, + validateTileCounts(cards) { + const counts = {} + cards.forEach(card => { + if (!card.icon) return + counts[card.icon] = (counts[card.icon] || 0) + 1 + }) + return Object.keys(counts).every(icon => { + const unit = this.isGarbageIcon(icon) ? 2 : 3 + return counts[icon] % unit === 0 + }) + }, + repairTileCounts(cards, icons, garbageIcons) { + const sorted = cards.slice().sort((a, b) => this.cardZIndex(a) - this.cardZIndex(b)) + sorted.forEach((card, index) => { + if (index < 6) { + card.icon = garbageIcons[Math.floor(index / 2) % garbageIcons.length] + card.garbage = true + } else { + card.icon = icons[Math.floor((index - 6) / 3) % icons.length] + card.garbage = false + } + card.displayIcon = this.displayIcon(card.icon) + }) + }, shouldBuildTrapLayout() { const rand = this.seededRandom((this.roundSeed || this.levelSeed()) + '_win_rate') return rand() < 0.5 }, applyTrapLayout(cards) { + this.cards = cards const iconMap = {} cards.forEach(card => { if (this.isGarbageIcon(card.icon)) return @@ -544,6 +576,7 @@ card.x = 180 + Math.floor(rand() * 180) card.y = 140 + Math.floor(rand() * 260) card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${this.cardZIndex(card)};` + this.refreshCardState() }, exposePairEarly(pair, index) { const baseX = 66 + (index % 4) * 100 @@ -555,6 +588,7 @@ card.y = baseY + i * 58 card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${this.cardZIndex(card)};` }) + this.refreshCardState() }, buildCoverChain(key, cards, index, rand) { const covers = cards.filter(card => { @@ -569,6 +603,7 @@ cover.y = key.y + (i < 2 ? 16 : -16) + Math.floor(rand() * 10) cover.style = `left:${cover.x}rpx;top:${cover.y}rpx;z-index:${this.cardZIndex(cover)};` } + this.refreshCardState() }, repositionKeyCovers(cards, seed) { const rand = this.seededRandom(seed + '_cover') @@ -583,6 +618,7 @@ cover.style = `left:${cover.x}rpx;top:${cover.y}rpx;z-index:${this.cardZIndex(cover)};` } }) + this.refreshCardState() }, takeNext(pool, assigned) { while (pool.length) { @@ -627,7 +663,7 @@ }, coverCount(card, cards) { return cards.filter(other => { - if (other.id === card.id || other.layer <= card.layer) return false + if (other.id === card.id || this.getRealZ(other) <= this.getRealZ(card)) return false return this.isOverlap(card, other) }).length }, @@ -639,15 +675,24 @@ }, isBlockedIn(card, list) { return list.some(other => { - if (other.id === card.id || other.layer <= card.layer) return false + if (other.id === card.id || this.getRealZ(other) <= this.getRealZ(card)) return false return this.isOverlap(card, other) }) }, isOverlap(a, b) { - return Math.abs(a.x - b.x) < 82 && Math.abs(a.y - b.y) < 82 + const size = 76 + return !( + a.x + size <= b.x || + b.x + size <= a.x || + a.y + size <= b.y || + b.y + size <= a.y + ) + }, + getRealZ(card) { + return (card.layer || 0) * 1000 + (card.order || 0) }, cardZIndex(card) { - return (card.layer || 0) * 100 + (card.order || 0) + 1 + return this.getRealZ(card) + 1 }, normalizeCards(icons) { this.cards.forEach((card, index) => { @@ -683,8 +728,9 @@ return } this.refreshCardState() - if (card.removed || card.selected || card.locked || this.isLocked(card)) { - this.tui.toast('这张还被压住,先消上面的牌') + const lockedNow = this.isLocked(card) + if (card.removed || card.selected || lockedNow) { + this.tui.toast('这张卡被压住了') return } if (this.slots.length >= this.slotLimit) return @@ -713,7 +759,7 @@ const elapsed = Math.floor((Date.now() - this.startTs) / 1000) this.timeLeft = Math.max(0, this.timeLimit - elapsed) if (this.timeLeft <= 0) { - this.failLevel('时间到了', '超过150秒,本关失败。不消耗次数,调整顺序再来一次。') + this.failLevel('时间到了', '超过300秒,本关失败。不消耗次数,调整顺序再来一次。') } }, 1000) }, @@ -752,8 +798,9 @@ }, isLocked(card) { return this.cards.some(other => { - if (other.removed || other.selected || other.layer <= card.layer) return false - return this.isOverlap(card, other) + if (other.id === card.id) return false + if (other.removed || other.selected) return false + return this.getRealZ(other) > this.getRealZ(card) && this.isOverlap(card, other) }) }, finishClear() { @@ -943,6 +990,7 @@ .board { margin-top: 22rpx; height: 620rpx; border-radius: 36rpx; background: linear-gradient(155deg, rgba(234,248,255,0.9), rgba(255,248,222,0.72)); position: relative; overflow: hidden; z-index: 1; } .tile { position: absolute; width: 76rpx; height: 76rpx; border-radius: 22rpx; background: #fff; display: flex; align-items: center; justify-content: center; box-shadow: 0 10rpx 22rpx rgba(66,99,94,0.12); border: 2rpx solid rgba(255,255,255,0.9); transition: transform .12s ease, opacity .12s ease; } .tile text { font-size: 42rpx; } + .tile .debug-layer { position: absolute; right: 6rpx; bottom: 4rpx; font-size: 16rpx; color: #8AA09C; line-height: 1; } .tile.locked { opacity: .42; pointer-events: none;