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.
AdLike2048/assets/script/battle/map/legacy/MapAreaUnlock.ts

201 lines
6.7 KiB

2 weeks ago
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;
}
}