|
|
|
@ -353,8 +353,8 @@ |
|
|
|
if (!cards.length) { |
|
|
|
cards = this.assignEmergencyIcons(this.buildCardPositions(this.roundSeed + '_emergency'), icons, garbageIcons) |
|
|
|
} |
|
|
|
if (this.shouldBuildUnwinnableLevel()) { |
|
|
|
this.applyUnwinnableTwist(cards, icons, garbageIcons) |
|
|
|
if (this.shouldBuildTrapLayout()) { |
|
|
|
this.applyTrapLayout(cards) |
|
|
|
} |
|
|
|
this.cards = cards |
|
|
|
this.slots = [] |
|
|
|
@ -438,7 +438,6 @@ |
|
|
|
return templates[type] || templates.hell |
|
|
|
}, |
|
|
|
assignTrapLayoutIcons(positions, icons, garbageIcons, seed, forceBuild) { |
|
|
|
const rand = this.seededRandom(seed + '_layout') |
|
|
|
const cards = positions.slice() |
|
|
|
cards.forEach(card => { |
|
|
|
card.icon = '' |
|
|
|
@ -490,9 +489,11 @@ |
|
|
|
card.garbage = true |
|
|
|
assigned[card.id] = true |
|
|
|
}) |
|
|
|
let fillIndex = 0 |
|
|
|
cards.forEach((card, index) => { |
|
|
|
if (!card.icon) { |
|
|
|
card.icon = icons[(index + Math.floor(rand() * icons.length)) % icons.length] |
|
|
|
card.icon = icons[Math.floor(fillIndex / 3) % icons.length] |
|
|
|
fillIndex++ |
|
|
|
} |
|
|
|
card.displayIcon = this.displayIcon(card.icon) |
|
|
|
card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${this.cardZIndex(card)};` |
|
|
|
@ -510,11 +511,11 @@ |
|
|
|
}) |
|
|
|
return positions |
|
|
|
}, |
|
|
|
shouldBuildUnwinnableLevel() { |
|
|
|
shouldBuildTrapLayout() { |
|
|
|
const rand = this.seededRandom((this.roundSeed || this.levelSeed()) + '_win_rate') |
|
|
|
return rand() < 0.5 |
|
|
|
}, |
|
|
|
applyUnwinnableTwist(cards, icons, garbageIcons) { |
|
|
|
applyTrapLayout(cards) { |
|
|
|
const iconMap = {} |
|
|
|
cards.forEach(card => { |
|
|
|
if (this.isGarbageIcon(card.icon)) return |
|
|
|
@ -523,41 +524,52 @@ |
|
|
|
}) |
|
|
|
const candidates = Object.keys(iconMap).filter(icon => iconMap[icon].length >= 3) |
|
|
|
if (!candidates.length) return |
|
|
|
const rand = this.seededRandom((this.roundSeed || this.levelSeed()) + '_unwinnable') |
|
|
|
const rand = this.seededRandom((this.roundSeed || this.levelSeed()) + '_trap_layout') |
|
|
|
this.shuffleWithRandom(candidates, rand) |
|
|
|
const trapIcons = candidates.slice(0, 4) |
|
|
|
const trapIcons = candidates.slice(0, 8 + Math.floor(rand() * 5)) |
|
|
|
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 group = iconMap[targetIcon].slice(0, 3) |
|
|
|
if (group.length < 3) return |
|
|
|
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.exposeBadCardEarly(key, trapIndex) |
|
|
|
this.exposePairEarly(iconMap[targetIcon].filter(card => card.id !== key.id).slice(0, 2), trapIndex) |
|
|
|
const pair = group.slice(1, 3) |
|
|
|
this.placeDeepKeyCard(key, trapIndex, rand) |
|
|
|
this.exposePairEarly(pair, trapIndex) |
|
|
|
this.buildCoverChain(key, cards, trapIndex, rand) |
|
|
|
}) |
|
|
|
}, |
|
|
|
exposeBadCardEarly(card, index) { |
|
|
|
card.layer = 2 |
|
|
|
card.x = 116 + index * 74 |
|
|
|
card.y = 178 + (index % 2) * 70 |
|
|
|
placeDeepKeyCard(card, index, rand) { |
|
|
|
card.layer = 0 |
|
|
|
card.keyCard = true |
|
|
|
card.trap = true |
|
|
|
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)};` |
|
|
|
}, |
|
|
|
exposePairEarly(pair, index) { |
|
|
|
const baseX = 74 + index * 88 |
|
|
|
const baseX = 66 + (index % 4) * 100 |
|
|
|
const baseY = 24 + Math.floor(index / 4) * 88 |
|
|
|
pair.forEach((card, i) => { |
|
|
|
card.layer = 2 |
|
|
|
card.trap = true |
|
|
|
card.x = baseX + i * 8 |
|
|
|
card.y = 26 + i * 78 |
|
|
|
card.y = baseY + i * 58 |
|
|
|
card.style = `left:${card.x}rpx;top:${card.y}rpx;z-index:${this.cardZIndex(card)};` |
|
|
|
}) |
|
|
|
}, |
|
|
|
buildCoverChain(key, cards, index, rand) { |
|
|
|
const covers = cards.filter(card => { |
|
|
|
return card.id !== key.id && !card.garbage && !card.keyCard && !card.trap && card.layer > 0 |
|
|
|
}) |
|
|
|
this.shuffleWithRandom(covers, rand) |
|
|
|
const count = 4 + (index % 2) |
|
|
|
for (let i = 0; i < count && i < covers.length; i++) { |
|
|
|
const cover = covers[i] |
|
|
|
cover.layer = i % 2 === 0 ? 1 : 2 |
|
|
|
cover.x = key.x + (i % 2 === 0 ? 16 : -16) + Math.floor(rand() * 10) |
|
|
|
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)};` |
|
|
|
} |
|
|
|
}, |
|
|
|
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) |
|
|
|
@ -685,7 +697,7 @@ |
|
|
|
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() |
|
|
|
this.failLevel('没有可消组合', '剩余卡牌无法凑成消除,本关失败。不消耗次数,换个顺序再来一次。') |
|
|
|
} else if (this.slots.length >= this.slotLimit) { |
|
|
|
this.failLevel() |
|
|
|
} |
|
|
|
@ -713,11 +725,11 @@ |
|
|
|
}, |
|
|
|
tryClear(icon) { |
|
|
|
if (this.isGarbageIcon(icon)) { |
|
|
|
const garbage = this.slots.filter(item => item.icon === icon && !item.unwinnableKey) |
|
|
|
const garbage = this.slots.filter(item => item.icon === icon) |
|
|
|
if (garbage.length < 2) return |
|
|
|
let removedGarbage = 0 |
|
|
|
this.slots = this.slots.filter(item => { |
|
|
|
if (item.icon === icon && !item.unwinnableKey && removedGarbage < 2) { |
|
|
|
if (item.icon === icon && removedGarbage < 2) { |
|
|
|
item.removed = true |
|
|
|
removedGarbage++ |
|
|
|
return false |
|
|
|
|