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.
297 lines
10 KiB
297 lines
10 KiB
import { TableNames } from '../../../game/ConfigTableData';
|
|
import { MapGrid } from './MapGrid';
|
|
|
|
/**
|
|
* 拖英雄推进区域解锁的结果。
|
|
* skip:不是解锁格,走原落子。
|
|
* blocked:还没轮到、条件没随出来,或英雄类型/等级不符。
|
|
* unlocked:英雄符合当前条件,整区可放置文字。
|
|
*/
|
|
export type AreaUnlockDrag = 'skip' | 'blocked' | 'unlocked';
|
|
|
|
/** 拖上格子的英雄:类型与表等级 */
|
|
export interface AreaUnlockHero {
|
|
/** Hero.herotype */
|
|
heroType: number;
|
|
/** Hero.level */
|
|
level: number;
|
|
}
|
|
|
|
/**
|
|
* 地图解锁区域。
|
|
* 区域 ID 从小到大依次解锁。当前区域从关卡 unlockland1-6 按权重随机一条条件,
|
|
* 等级加上段位 levelvalue。把符合类型和等级的英雄拖上去后,整区可放置文字。
|
|
*/
|
|
export class MapAreaUnlock {
|
|
private _ids: number[] = [];
|
|
private _done: boolean[] = [];
|
|
private _rolled: boolean[] = [];
|
|
private _heroTypes: number[] = [];
|
|
private _needLevels: number[] = [];
|
|
private _grids: MapGrid[] = [];
|
|
private _levelId = 0;
|
|
private _levelValue = 0;
|
|
private _levelReady = false;
|
|
private _warnedSprite = false;
|
|
|
|
/**
|
|
* 本局关卡与段位等级加值。加值来自 Rank.levelvalue。
|
|
* @param levelId 关卡表 id
|
|
* @param levelValue 段位等级加值
|
|
*/
|
|
setLevel(levelId: number, levelValue: number): void {
|
|
this._levelId = levelId;
|
|
this._levelValue = levelValue;
|
|
this._levelReady = true;
|
|
this._rollCurrent();
|
|
}
|
|
|
|
/**
|
|
* 按当前格子重建区域,并刷新等待解锁的显示。已解锁和已随机的条件按区域 ID 保留。
|
|
* @param grids 地图格子
|
|
*/
|
|
sync(grids: MapGrid[]): void {
|
|
const prevIds = this._ids;
|
|
const prevDone = this._done;
|
|
const prevRolled = this._rolled;
|
|
const prevTypes = this._heroTypes;
|
|
const prevLevels = this._needLevels;
|
|
this._grids = grids;
|
|
this._ids = [];
|
|
this._done = [];
|
|
this._rolled = [];
|
|
this._heroTypes = [];
|
|
this._needLevels = [];
|
|
this._warnedSprite = false;
|
|
|
|
for (let i = 0; i < grids.length; i++) {
|
|
const grid = grids[i];
|
|
if (!grid?.isCanBeDig || grid.unlockAreaId <= 0) continue;
|
|
this._touchArea(grid.unlockAreaId, prevIds, prevDone, prevRolled, prevTypes, prevLevels);
|
|
}
|
|
this._sortAreas();
|
|
this._rollCurrent();
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* 把英雄拖到当前解锁区域。类型 0 表示任意 herotype,等级须达到配置等级加段位加值。
|
|
* @param grid 落点格
|
|
* @param hero 拖动的英雄;没有对应英雄时传 null
|
|
*/
|
|
tryDrag(grid: MapGrid, hero: AreaUnlockHero | null): 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 || !this._rolled[index]) return 'blocked';
|
|
if (!hero) return 'blocked';
|
|
const typeOk = this._heroTypes[index] === 0 || hero.heroType === this._heroTypes[index];
|
|
const levelOk = hero.level >= this._needLevels[index];
|
|
if (!typeOk || !levelOk) return 'blocked';
|
|
this._done[index] = true;
|
|
this._rollCurrent();
|
|
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,未轮到 Put,已解锁 Dig(由 MapGrid 按状态选图)。
|
|
*/
|
|
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._warnedSprite) {
|
|
console.warn('[MapAreaUnlock] 当前解锁区域缺少 Wait Unlocked', current, grid.Index);
|
|
this._warnedSprite = true;
|
|
}
|
|
grid.waitingUnlock = wait;
|
|
if (!wait && grid.isAreaUnlock) grid.updateGrid();
|
|
}
|
|
}
|
|
|
|
/** 给当前区域随机一条 unlockland 条件 */
|
|
private _rollCurrent(): void {
|
|
if (!this._levelReady) return;
|
|
const id = this.currentId();
|
|
if (id <= 0) return;
|
|
const index = this._indexOf(id);
|
|
if (index < 0 || this._rolled[index]) return;
|
|
const level = gg.data.table.getTableData<ITableLevel>(TableNames.Level, this._levelId);
|
|
if (!level) {
|
|
console.warn('[MapAreaUnlock] 找不到关卡', this._levelId);
|
|
return;
|
|
}
|
|
const raw = this._landRaw(level, id);
|
|
if (raw == null) return;
|
|
const picked = this._pickLand(raw, id);
|
|
if (!picked) return;
|
|
this._heroTypes[index] = picked.heroType;
|
|
this._needLevels[index] = picked.level + this._levelValue;
|
|
this._rolled[index] = true;
|
|
}
|
|
|
|
/**
|
|
* 按区域 ID 取 unlockland1-6。超出 1-6 时警告。
|
|
* @param level 关卡行
|
|
* @param areaId 解锁区域 ID
|
|
*/
|
|
private _landRaw(level: ITableLevel, areaId: number): string | null {
|
|
switch (areaId) {
|
|
case 1: return level.unlockland1;
|
|
case 2: return level.unlockland2;
|
|
case 3: return level.unlockland3;
|
|
case 4: return level.unlockland4;
|
|
case 5: return level.unlockland5;
|
|
case 6: return level.unlockland6;
|
|
default:
|
|
console.warn('[MapAreaUnlock] 解锁区域ID只支持 1-6', areaId);
|
|
return null;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 按权重随机一条。格式:属性类型,等级,权重|属性类型,等级,权重。
|
|
* @param raw 配表原文
|
|
* @param areaId 解锁区域 ID
|
|
*/
|
|
private _pickLand(raw: string, areaId: number): { heroType: number; level: number } | null {
|
|
const text = String(raw ?? '').trim();
|
|
if (!text) {
|
|
console.warn('[MapAreaUnlock] 关卡未配置解锁条件', this._levelId, areaId);
|
|
return null;
|
|
}
|
|
const types: number[] = [];
|
|
const levels: number[] = [];
|
|
const weights: number[] = [];
|
|
const parts = text.split('|');
|
|
for (let i = 0; i < parts.length; i++) {
|
|
const seg = parts[i].trim().split(',');
|
|
if (seg.length !== 3) {
|
|
console.warn('[MapAreaUnlock] 解锁条件格式应为 类型,等级,权重', areaId, parts[i]);
|
|
continue;
|
|
}
|
|
const heroType = Number(seg[0]);
|
|
const level = Number(seg[1]);
|
|
const weight = Number(seg[2]);
|
|
if (!Number.isFinite(heroType) || !Number.isFinite(level) || !Number.isFinite(weight)) {
|
|
console.warn('[MapAreaUnlock] 解锁条件含非数字', areaId, parts[i]);
|
|
continue;
|
|
}
|
|
if (weight <= 0) continue;
|
|
types.push(heroType);
|
|
levels.push(level);
|
|
weights.push(weight);
|
|
}
|
|
if (types.length === 0) {
|
|
console.warn('[MapAreaUnlock] 解锁条件没有有效权重', this._levelId, areaId, text);
|
|
return null;
|
|
}
|
|
let total = 0;
|
|
for (let i = 0; i < weights.length; i++) total += weights[i];
|
|
let roll = Math.random() * total;
|
|
let pick = types.length - 1;
|
|
for (let i = 0; i < weights.length; i++) {
|
|
roll -= weights[i];
|
|
if (roll < 0) {
|
|
pick = i;
|
|
break;
|
|
}
|
|
}
|
|
return { heroType: types[pick], level: levels[pick] };
|
|
}
|
|
|
|
/**
|
|
* @param id 区域 ID
|
|
* @param prevIds 同步前的区域 ID
|
|
* @param prevDone 同步前是否已解锁
|
|
* @param prevRolled 同步前是否已随机条件
|
|
* @param prevTypes 同步前的英雄类型
|
|
* @param prevLevels 同步前的需求等级
|
|
*/
|
|
private _touchArea(
|
|
id: number,
|
|
prevIds: number[],
|
|
prevDone: boolean[],
|
|
prevRolled: boolean[],
|
|
prevTypes: number[],
|
|
prevLevels: number[],
|
|
): void {
|
|
if (this._indexOf(id) >= 0) return;
|
|
const old = this._oldIndex(id, prevIds);
|
|
this._ids.push(id);
|
|
this._done.push(old >= 0 ? prevDone[old] : false);
|
|
this._rolled.push(old >= 0 ? prevRolled[old] : false);
|
|
this._heroTypes.push(old >= 0 ? prevTypes[old] : 0);
|
|
this._needLevels.push(old >= 0 ? prevLevels[old] : 0);
|
|
}
|
|
|
|
/** 区域 ID 从小到大 */
|
|
private _sortAreas(): void {
|
|
for (let i = 1; i < this._ids.length; i++) {
|
|
const id = this._ids[i];
|
|
const done = this._done[i];
|
|
const rolled = this._rolled[i];
|
|
const heroType = this._heroTypes[i];
|
|
const needLevel = this._needLevels[i];
|
|
let j = i - 1;
|
|
while (j >= 0 && this._ids[j] > id) {
|
|
this._ids[j + 1] = this._ids[j];
|
|
this._done[j + 1] = this._done[j];
|
|
this._rolled[j + 1] = this._rolled[j];
|
|
this._heroTypes[j + 1] = this._heroTypes[j];
|
|
this._needLevels[j + 1] = this._needLevels[j];
|
|
j--;
|
|
}
|
|
this._ids[j + 1] = id;
|
|
this._done[j + 1] = done;
|
|
this._rolled[j + 1] = rolled;
|
|
this._heroTypes[j + 1] = heroType;
|
|
this._needLevels[j + 1] = needLevel;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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;
|
|
}
|
|
}
|
|
|