import { MapGrid } from './MapGrid'; /** * 拖文字推进区域解锁的结果。 * skip:不是解锁格,走原落子。 * blocked:还没轮到这个区域,或解锁条件无效。 * counted:次数 +1,字不落下。 * unlocked:本区次数已满,整区可放置文字。 */ export type AreaUnlockDrag = 'skip' | 'blocked' | 'counted' | 'unlocked'; /** * 地图解锁区域。 * 区域 ID 从小到大依次解锁;把文字拖到当前区域上,达到解锁条件后整区可放置文字。 */ export class MapAreaUnlock { private _ids: number[] = []; private _needs: number[] = []; private _counts: number[] = []; private _done: boolean[] = []; private _grids: MapGrid[] = []; private _warnedNeed = false; /** * 按当前格子重建区域,并刷新等待解锁的显示。 * 已有进度按区域 ID 保留。 * @param grids 地图格子 */ sync(grids: MapGrid[]): void { const prevIds = this._ids; const prevCounts = this._counts; const prevDone = this._done; this._grids = grids; this._ids = []; this._needs = []; this._counts = []; this._done = []; this._warnedNeed = false; for (let i = 0; i < grids.length; i++) { const grid = grids[i]; if (!grid?.isCanBeDig || grid.unlockAreaId <= 0) continue; this._touchArea(grid, prevIds, prevCounts, prevDone); } this._sortAreas(); this.applyVisual(); } /** 当前正在解锁的区域 ID;没有则 0 */ currentId(): number { for (let i = 0; i < this._ids.length; i++) { if (!this._done[i]) return this._ids[i]; } return 0; } /** * 把文字拖到格子上,推进当前解锁区域。 * @param grid 落点格 */ tryDrag(grid: MapGrid): AreaUnlockDrag { if (!grid?.isAreaUnlock) return 'skip'; const current = this.currentId(); if (current <= 0 || grid.unlockAreaId !== current) return 'blocked'; const index = this._indexOf(current); if (index < 0) return 'blocked'; const need = this._needs[index]; if (!(need > 0)) { console.warn('[MapAreaUnlock] 解锁条件必须是大于 0 的数字', current, need); return 'blocked'; } this._counts[index] += 1; if (this._counts[index] < need) return 'counted'; this._done[index] = true; return 'unlocked'; } /** * 某区域下仍挂着该 ID 的格子。 * @param areaId 解锁区域 ID */ gridsOf(areaId: number): MapGrid[] { const out: MapGrid[] = []; for (let i = 0; i < this._grids.length; i++) { const grid = this._grids[i]; if (grid?.unlockAreaId === areaId) out.push(grid); } return out; } /** 只让当前区域显示 Wait Unlocked */ applyVisual(): void { const current = this.currentId(); for (let i = 0; i < this._grids.length; i++) { const grid = this._grids[i]; if (!grid) continue; const wait = grid.isAreaUnlock && grid.unlockAreaId === current; if (wait && !grid.waitUnlockedSprite && !this._warnedNeed) { console.warn('[MapAreaUnlock] 当前解锁区域缺少 Wait Unlocked', current, grid.Index); this._warnedNeed = true; } grid.waitingUnlock = wait; } } /** * 登记区域。同一 ID 的解锁条件不一致时警告,并采用 Index 最小那格的数字。 * @param grid 格子 * @param prevIds 同步前的区域 ID * @param prevCounts 同步前的拖动次数 * @param prevDone 同步前是否已解锁 */ private _touchArea(grid: MapGrid, prevIds: number[], prevCounts: number[], prevDone: boolean[]): void { const id = grid.unlockAreaId; let index = this._indexOf(id); if (index < 0) { index = this._ids.length; this._ids.push(id); this._needs.push(grid.unlockNeed); const old = this._oldIndex(id, prevIds); this._counts.push(old >= 0 ? prevCounts[old] : 0); this._done.push(old >= 0 ? prevDone[old] : false); if (!(grid.unlockNeed > 0)) { console.warn('[MapAreaUnlock] 解锁条件必须是大于 0 的数字', id, grid.unlockNeed, grid.Index); } return; } if (this._needs[index] !== grid.unlockNeed) { const kept = this._needOwnerIndex(id); const useNew = grid.Index < kept; console.warn( '[MapAreaUnlock] 同一解锁区域的条件不一致,使用 Index 更小的格子', id, useNew ? grid.unlockNeed : this._needs[index], grid.Index, kept, ); if (useNew) this._needs[index] = grid.unlockNeed; } } /** 区域 ID 从小到大 */ private _sortAreas(): void { for (let i = 1; i < this._ids.length; i++) { const id = this._ids[i]; const need = this._needs[i]; const count = this._counts[i]; const done = this._done[i]; let j = i - 1; while (j >= 0 && this._ids[j] > id) { this._ids[j + 1] = this._ids[j]; this._needs[j + 1] = this._needs[j]; this._counts[j + 1] = this._counts[j]; this._done[j + 1] = this._done[j]; j--; } this._ids[j + 1] = id; this._needs[j + 1] = need; this._counts[j + 1] = count; this._done[j + 1] = done; } } /** * 当前记录里该区域条件来自哪一格。 * @param id 区域 ID */ private _needOwnerIndex(id: number): number { let owner = Number.MAX_VALUE; for (let i = 0; i < this._grids.length; i++) { const grid = this._grids[i]; if (!grid || grid.unlockAreaId !== id || !grid.isCanBeDig) continue; if (grid.unlockNeed === this._needs[this._indexOf(id)] && grid.Index < owner) { owner = grid.Index; } } return owner; } /** * @param id 区域 ID */ private _indexOf(id: number): number { for (let i = 0; i < this._ids.length; i++) { if (this._ids[i] === id) return i; } return -1; } /** * @param id 区域 ID * @param ids 旧列表 */ private _oldIndex(id: number, ids: number[]): number { for (let i = 0; i < ids.length; i++) { if (ids[i] === id) return i; } return -1; } }