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/view/TextListLandHost.ts

246 lines
8.2 KiB

2 weeks ago
import { Node } from 'cc';
import { TableNames } from '../../game/ConfigTableData';
/** ui_textList 格子状态 */
export enum TextListCellState {
/** 开启,可按背包规则拖动 */
Open = 1,
/** 开启但不能拖;其他字拖入后解锁下标 +1 */
OpenNoDrag = 2,
/** 未解锁 */
Locked = 3,
}
/** 格子节点上的开启底 */
const OPEN_NODE = '背包格开启';
/** 格子节点上的未开启底 */
const LOCKED_NODE = '背包格未开启';
/** 模板节点,不参与状态 */
const TEMP_NODE = 'temp';
/**
* 按关卡 normalland / tolockland 显示 ui_textList 格子,并处理待解锁格的拖入解锁。
* normalland 为开启;tolockland 为开启但不能拖;其余为未解锁。名为 temp 的节点跳过。
*/
export class TextListLandHost {
/** ui_textList */
private _list: Node | null = null;
/** 已按关卡刷过;未配置时全部视为开启 */
private _useTable = false;
/** 开启下标(含拖入后新解锁) */
private readonly _open: number[] = [];
/** 待解锁下标(开启但不能拖) */
private readonly _gate: number[] = [];
/**
* 读取关卡并刷新格子底图。
* @param levelId Level.id
* @param list ui_textList
*/
bind(levelId: number, list: Node | null): void {
this.clear();
if (!list?.isValid) {
console.log('[TextListLand] ui_textList 缺失');
return;
}
this._list = list;
const row = gg.data.table.getTableData<ITableLevel>(TableNames.Level, levelId);
if (!row) {
console.log('[TextListLand] Level 缺失', levelId);
this._applyAll(TextListCellState.Open);
return;
}
const normal = row.normalland;
const gate = row.tolockland;
if (!normal?.length && !gate?.length) {
console.log('[TextListLand] 关卡未配置地块,全部开启', levelId);
this._applyAll(TextListCellState.Open);
return;
}
this._useTable = true;
this._fill(this._open, normal, 'normalland');
this._fill(this._gate, gate, 'tolockland');
for (let i = this._gate.length - 1; i >= 0; i--) {
if (!this._has(this._open, this._gate[i])) continue;
console.log('[TextListLand] 下标同时在开启与待解锁,按开启', this._gate[i]);
this._gate.splice(i, 1);
}
this._applyFromTable();
}
/** 解除引用 */
clear(): void {
this._list = null;
this._useTable = false;
this._open.length = 0;
this._gate.length = 0;
}
/**
* 节点对应的格子下标。temp 与无法解析的名字返回 null。
* @param node ui_textList 子节点
*/
static indexOfNode(node: Node | null | undefined): number | null {
if (!node?.isValid) return null;
if (node.name === TEMP_NODE) return null;
if (node.name === 'uigrid') return 0;
const matched = /^uigrid-(\d+)$/.exec(node.name);
if (!matched) {
console.log('[TextListLand] 无法解析格子下标', node.name);
return null;
}
const index = parseInt(matched[1], 10);
if (!Number.isFinite(index) || index < 0) {
console.log('[TextListLand] 格子下标非法', node.name);
return null;
}
return index;
}
/**
* 当前下标状态。未走配表时一律开启。
* @param index 格子下标
*/
stateOf(index: number): TextListCellState {
if (!this._useTable) return TextListCellState.Open;
if (this._has(this._open, index)) return TextListCellState.Open;
if (this._has(this._gate, index)) return TextListCellState.OpenNoDrag;
return TextListCellState.Locked;
}
/**
* 待解锁格禁止把字拖走。
* @param index 格子下标
*/
isNoDrag(index: number): boolean {
return this.stateOf(index) === TextListCellState.OpenNoDrag;
}
/**
* 其他字拖到待解锁格:把下标 +1 的未解锁格改为开启。
* @param gateIndex 待解锁格下标
*/
unlockNext(gateIndex: number): void {
if (!this.isNoDrag(gateIndex)) {
console.log('[TextListLand] 非待解锁格', gateIndex);
return;
}
const next = gateIndex + 1;
const node = this._nodeAt(next);
if (!node) {
console.log('[TextListLand] 下标+1 没有可解锁格子', gateIndex, next);
return;
}
if (this.stateOf(next) === TextListCellState.Open) return;
if (this.stateOf(next) !== TextListCellState.Locked) {
console.log('[TextListLand] 下标+1 已不是未解锁', next, this.stateOf(next));
return;
}
this._open.push(next);
this._applyNode(node, TextListCellState.Open);
}
/**
* 写入一组下标,非法项打日志。
* @param out 目标数组
* @param raw 配表数组
* @param label 字段名
*/
private _fill(out: number[], raw: readonly number[] | undefined, label: string): void {
if (!raw?.length) return;
for (let i = 0; i < raw.length; i++) {
const index = Math.floor(Number(raw[i]));
if (!Number.isFinite(index) || index < 0) {
console.log('[TextListLand] 非法下标', label, raw[i]);
continue;
}
if (this._has(out, index)) continue;
out.push(index);
}
}
/** 按当前开启/待解锁刷新全部格子 */
private _applyFromTable(): void {
const list = this._list;
if (!list?.isValid) return;
const children = list.children;
for (let i = 0; i < children.length; i++) {
const node = children[i];
const index = TextListLandHost.indexOfNode(node);
if (index == null) continue;
this._applyNode(node, this.stateOf(index));
}
this._warnMissing(this._open);
this._warnMissing(this._gate);
}
/**
* 配表下标在列表里没有对应格子时打日志。
* @param indices 下标
*/
private _warnMissing(indices: readonly number[]): void {
for (let i = 0; i < indices.length; i++) {
if (this._nodeAt(indices[i])) continue;
console.log('[TextListLand] 下标没有可显示格子', indices[i]);
}
}
/**
* 未配置地块时统一刷一种状态。
* @param state 状态
*/
private _applyAll(state: TextListCellState): void {
const list = this._list;
if (!list?.isValid) return;
const children = list.children;
for (let i = 0; i < children.length; i++) {
const node = children[i];
if (TextListLandHost.indexOfNode(node) == null) continue;
this._applyNode(node, state);
}
}
/**
* 按下标找格子节点。
* @param index 格子下标
*/
private _nodeAt(index: number): Node | null {
const list = this._list;
if (!list?.isValid) return null;
const children = list.children;
for (let i = 0; i < children.length; i++) {
if (TextListLandHost.indexOfNode(children[i]) === index) return children[i];
}
return null;
}
/**
* 开启显示背包格开启,未解锁显示背包格未开启。
* @param node 格子节点
* @param state 状态
*/
private _applyNode(node: Node, state: TextListCellState): void {
const openNode = node.getChildByName(OPEN_NODE);
const lockedNode = node.getChildByName(LOCKED_NODE);
if (!openNode?.isValid || !lockedNode?.isValid) {
console.log('[TextListLand] 缺少开关节点', node.name);
return;
}
const opened = state !== TextListCellState.Locked;
openNode.active = opened;
lockedNode.active = !opened;
}
/**
* 数组是否包含下标。
* @param list 下标列表
* @param index 下标
*/
private _has(list: readonly number[], index: number): boolean {
for (let i = 0; i < list.length; i++) {
if (list[i] === index) return true;
}
return false;
}
}