You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
200 lines
6.7 KiB
200 lines
6.7 KiB
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;
|
|
}
|
|
}
|
|
|