|
|
|
@ -165,6 +165,7 @@ |
|
|
|
timeLimit: 150, |
|
|
|
timeLeft: 150, |
|
|
|
timer: null, |
|
|
|
roundSeed: '', |
|
|
|
moveCount: 0, |
|
|
|
modal: { show: false, title: '', sub: '' } |
|
|
|
} |
|
|
|
@ -328,6 +329,7 @@ |
|
|
|
this.session = res.result |
|
|
|
this.playing = true |
|
|
|
this.startTs = Date.now() |
|
|
|
this.roundSeed = this.newRoundSeed() |
|
|
|
this.buildLevel() |
|
|
|
if (!this.cards.length) this.buildLevel() |
|
|
|
this.startTimer() |
|
|
|
@ -336,19 +338,20 @@ |
|
|
|
buildLevel() { |
|
|
|
const icons = ['🍔', '🥤', '🥟', '🏀', '🎧', '🚲', '🍜', '🔥', '☕', '🍢'] |
|
|
|
const garbageIcons = ['🚀', '🪐', '🛸'] |
|
|
|
if (!this.roundSeed) this.roundSeed = this.newRoundSeed() |
|
|
|
let cards = [] |
|
|
|
for (let attempt = 0; attempt < 100; attempt++) { |
|
|
|
const seed = this.levelSeed() + '_try_' + attempt |
|
|
|
const seed = this.roundSeed + '_try_' + attempt |
|
|
|
const positions = this.buildCardPositions(seed) |
|
|
|
cards = this.assignTrapLayoutIcons(positions, icons, garbageIcons, seed) |
|
|
|
if (cards.length && this.checkDifficultyMetrics(cards)) break |
|
|
|
cards = [] |
|
|
|
} |
|
|
|
if (!cards.length) { |
|
|
|
cards = this.assignTrapLayoutIcons(this.buildCardPositions(this.levelSeed() + '_fallback'), icons, garbageIcons, this.levelSeed() + '_fallback', true) |
|
|
|
cards = this.assignTrapLayoutIcons(this.buildCardPositions(this.roundSeed + '_fallback'), icons, garbageIcons, this.roundSeed + '_fallback', true) |
|
|
|
} |
|
|
|
if (!cards.length) { |
|
|
|
cards = this.assignEmergencyIcons(this.buildCardPositions(this.levelSeed() + '_emergency'), icons, garbageIcons) |
|
|
|
cards = this.assignEmergencyIcons(this.buildCardPositions(this.roundSeed + '_emergency'), icons, garbageIcons) |
|
|
|
} |
|
|
|
if (this.shouldBuildUnwinnableLevel()) { |
|
|
|
this.applyUnwinnableTwist(cards, icons, garbageIcons) |
|
|
|
@ -359,6 +362,9 @@ |
|
|
|
this.normalizeCards(icons) |
|
|
|
this.refreshCardState() |
|
|
|
}, |
|
|
|
newRoundSeed() { |
|
|
|
return `${this.levelSeed()}_${Date.now()}_${Math.floor(Math.random() * 100000)}` |
|
|
|
}, |
|
|
|
buildCardPositions(seed) { |
|
|
|
const cards = [] |
|
|
|
const template = this.levelTemplate('hell') |
|
|
|
@ -505,7 +511,7 @@ |
|
|
|
return positions |
|
|
|
}, |
|
|
|
shouldBuildUnwinnableLevel() { |
|
|
|
const rand = this.seededRandom(this.levelSeed() + '_win_rate') |
|
|
|
const rand = this.seededRandom((this.roundSeed || this.levelSeed()) + '_win_rate') |
|
|
|
return rand() < 0.5 |
|
|
|
}, |
|
|
|
applyUnwinnableTwist(cards, icons, garbageIcons) { |
|
|
|
@ -517,7 +523,7 @@ |
|
|
|
}) |
|
|
|
const candidates = Object.keys(iconMap).filter(icon => iconMap[icon].length >= 3) |
|
|
|
if (!candidates.length) return |
|
|
|
const rand = this.seededRandom(this.levelSeed() + '_unwinnable') |
|
|
|
const rand = this.seededRandom((this.roundSeed || this.levelSeed()) + '_unwinnable') |
|
|
|
this.shuffleWithRandom(candidates, rand) |
|
|
|
const trapIcons = candidates.slice(0, 4) |
|
|
|
trapIcons.forEach((targetIcon, trapIndex) => { |
|
|
|
@ -533,9 +539,16 @@ |
|
|
|
key.displayIcon = this.displayIcon(key.icon) |
|
|
|
key.unwinnableKey = true |
|
|
|
key.trap = true |
|
|
|
this.exposeBadCardEarly(key, trapIndex) |
|
|
|
this.exposePairEarly(iconMap[targetIcon].filter(card => card.id !== key.id).slice(0, 2), trapIndex) |
|
|
|
}) |
|
|
|
}, |
|
|
|
exposeBadCardEarly(card, index) { |
|
|
|
card.layer = 2 |
|
|
|
card.x = 116 + index * 74 |
|
|
|
card.y = 178 + (index % 2) * 70 |
|
|
|
card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${this.cardZIndex(card)};` |
|
|
|
}, |
|
|
|
exposePairEarly(pair, index) { |
|
|
|
const baseX = 74 + index * 88 |
|
|
|
pair.forEach((card, i) => { |
|
|
|
@ -773,6 +786,7 @@ |
|
|
|
this.playing = false |
|
|
|
this.clearTimer() |
|
|
|
this.timeLeft = this.timeLimit |
|
|
|
this.roundSeed = this.newRoundSeed() |
|
|
|
this.buildLevel() |
|
|
|
if (!this.cards.length) this.buildLevel() |
|
|
|
}, |
|
|
|
|