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.
245 lines
8.2 KiB
245 lines
8.2 KiB
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;
|
|
}
|
|
}
|
|
|