refactor(天赋系统): 将天赋和碎片集合改为完整记录并初始化默认值

- 将 `talents` 和 `talent_fragments` 的类型从 `Partial<Record>` 改为 `Record`,确保所有键始终存在
- 初始化集合对象时,为所有天赋类型和碎片类型设置默认值 0,避免后续的空值检查
- 更新数据恢复逻辑,使用 `Object.assign` 合并云端数据,保留本地初始化的完整结构
- 简化相关业务逻辑代码,移除不必要的空值检查和默认值回退
This commit is contained in:
panw
2026-05-09 10:36:30 +08:00
parent 38f4a1f47d
commit dca6ee555c
2 changed files with 47 additions and 24 deletions

View File

@@ -20,10 +20,10 @@ export interface GameDate{
gold:number, gold:number,
timestamp?: number, // 用于比对本地与云端数据的最新状态 timestamp?: number, // 用于比对本地与云端数据的最新状态
collection?: { collection?: {
talents: Partial<Record<TalentType, number>>, talents: Record<TalentType, number>,
player_level: number, player_level: number,
player_exp: number, player_exp: number,
talent_fragments: Partial<Record<TalentFragmentType, number>>, talent_fragments: Record<TalentFragmentType, number>,
talent_points?: number, talent_points?: number,
} }
} }
@@ -64,16 +64,37 @@ export class SingletonModuleComp extends ecs.Comp {
current_guide:number=0 current_guide:number=0
collection: { collection: {
talents: Partial<Record<TalentType, number>>; talents: Record<TalentType, number>;
player_level: number; player_level: number;
player_exp: number; player_exp: number;
talent_fragments: Partial<Record<TalentFragmentType, number>>; talent_fragments: Record<TalentFragmentType, number>;
talent_points?: number; talent_points?: number;
} = { } = {
talents: {}, // 存储各个天赋的等级: { talent_id: level } talents: {
[TalentType.Attack]: 0,
[TalentType.Hp]: 0,
[TalentType.Critical]: 0,
[TalentType.WindFury]: 0,
[TalentType.Freeze]: 0,
[TalentType.Puncture]: 0,
[TalentType.DeadTrigger]: 0,
[TalentType.Summon]: 0,
[TalentType.BuyDiscount]: 0,
[TalentType.RefreshDiscount]: 0,
[TalentType.SellBonus]: 0
}, // 存储各个天赋的等级: { talent_id: level }
player_level: 1, // 玩家等级 player_level: 1, // 玩家等级
player_exp: 0, // 玩家当前经验 player_exp: 0, // 玩家当前经验
talent_fragments: {}, // 当前拥有的天赋碎片库存 talent_fragments: {
[TalentFragmentType.Power]: 0,
[TalentFragmentType.Tempest]: 0,
[TalentFragmentType.Vitality]: 0,
[TalentFragmentType.Frost]: 0,
[TalentFragmentType.Tactics]: 0,
[TalentFragmentType.Spirit]: 0,
[TalentFragmentType.Trade]: 0,
[TalentFragmentType.Fate]: 0
}, // 当前拥有的天赋碎片库存
talent_points: 0, // 兼容旧存档的历史字段 talent_points: 0, // 兼容旧存档的历史字段
}; };
@@ -240,10 +261,14 @@ export class SingletonModuleComp extends ecs.Comp {
// 恢复收集记录 // 恢复收集记录
if (data.collection) { if (data.collection) {
const remoteCol = data.collection; const remoteCol = data.collection;
if (remoteCol.talents) this.collection.talents = remoteCol.talents; if (remoteCol.talents) {
Object.assign(this.collection.talents, remoteCol.talents);
}
if (typeof remoteCol.player_level === 'number') this.collection.player_level = remoteCol.player_level; if (typeof remoteCol.player_level === 'number') this.collection.player_level = remoteCol.player_level;
if (typeof remoteCol.player_exp === 'number') this.collection.player_exp = remoteCol.player_exp; if (typeof remoteCol.player_exp === 'number') this.collection.player_exp = remoteCol.player_exp;
if (remoteCol.talent_fragments) this.collection.talent_fragments = remoteCol.talent_fragments; if (remoteCol.talent_fragments) {
Object.assign(this.collection.talent_fragments, remoteCol.talent_fragments);
}
if (typeof remoteCol.talent_points === 'number') this.collection.talent_points = remoteCol.talent_points; if (typeof remoteCol.talent_points === 'number') this.collection.talent_points = remoteCol.talent_points;
} }
} }

View File

@@ -138,21 +138,20 @@ export class TalentsComp extends CCComp {
if (!this.talents_content || !this.prefab_talent_item) return; if (!this.talents_content || !this.prefab_talent_item) return;
const collection = smc.collection; const collection = smc.collection;
if (!collection.talents) collection.talents = {};
// 如果内容为空,则实例化预制体 // 如果内容为空,则实例化预制体
if (this.talents_content.children.length === 0) { if (this.talents_content.children.length === 0) {
TalentConfig.talents.forEach(talentInfo => { TalentConfig.talents.forEach(talentInfo => {
let itemNode = instantiate(this.prefab_talent_item); let itemNode = instantiate(this.prefab_talent_item);
this.talents_content.addChild(itemNode); this.talents_content.addChild(itemNode);
this.updateTalentItem(itemNode, talentInfo, collection.talents[talentInfo.id as TalentType] || 0); this.updateTalentItem(itemNode, talentInfo, collection.talents[talentInfo.id as TalentType]);
}); });
} else { } else {
// 否则直接更新现有节点 // 否则直接更新现有节点
TalentConfig.talents.forEach((talentInfo, index) => { TalentConfig.talents.forEach((talentInfo, index) => {
let itemNode = this.talents_content.children[index]; let itemNode = this.talents_content.children[index];
if (itemNode) { if (itemNode) {
this.updateTalentItem(itemNode, talentInfo, collection.talents[talentInfo.id as TalentType] || 0); this.updateTalentItem(itemNode, talentInfo, collection.talents[talentInfo.id as TalentType]);
} }
}); });
} }
@@ -244,7 +243,7 @@ export class TalentsComp extends CCComp {
const refundedFragments: Partial<Record<TalentFragmentType, number>> = {}; const refundedFragments: Partial<Record<TalentFragmentType, number>> = {};
for (let id in collection.talents) { for (let id in collection.talents) {
let talentId = Number(id) as TalentType; let talentId = Number(id) as TalentType;
let level = collection.talents[talentId] || 0; let level = collection.talents[talentId];
let talentInfo = TalentConfig.talents.find(t => t.id === talentId); let talentInfo = TalentConfig.talents.find(t => t.id === talentId);
if (talentInfo) { if (talentInfo) {
for (let i = 0; i < level; i++) { for (let i = 0; i < level; i++) {
@@ -256,15 +255,14 @@ export class TalentsComp extends CCComp {
} }
// 重置天赋等级并返还碎片 // 重置天赋等级并返还碎片
collection.talents = {}; for (let k in collection.talents) {
if (!collection.talent_fragments) { collection.talents[k as any as TalentType] = 0;
collection.talent_fragments = {};
} }
for (const fragmentType in refundedFragments) { for (const fragmentType in refundedFragments) {
const key = Number(fragmentType) as TalentFragmentType; const key = Number(fragmentType) as TalentFragmentType;
const amount = refundedFragments[key] || 0; const amount = refundedFragments[key] || 0;
if (amount > 0) { if (amount > 0) {
collection.talent_fragments[key] = (collection.talent_fragments[key] || 0) + amount; collection.talent_fragments[key] += amount;
} }
} }
@@ -293,30 +291,30 @@ export class TalentsComp extends CCComp {
/** 判断当前库存是否满足一次升级 */ /** 判断当前库存是否满足一次升级 */
private hasEnoughFragments(cost: { type: TalentFragmentType; amount: number }): boolean { private hasEnoughFragments(cost: { type: TalentFragmentType; amount: number }): boolean {
const bag = smc.collection.talent_fragments || {}; const bag = smc.collection.talent_fragments;
return (bag[cost.type] || 0) >= cost.amount; return bag[cost.type] >= cost.amount;
} }
/** 执行碎片扣除 */ /** 执行碎片扣除 */
private consumeFragments(cost: { type: TalentFragmentType; amount: number }) { private consumeFragments(cost: { type: TalentFragmentType; amount: number }) {
const bag = smc.collection.talent_fragments || (smc.collection.talent_fragments = {}); const bag = smc.collection.talent_fragments;
bag[cost.type] = Math.max(0, (bag[cost.type] || 0) - cost.amount); bag[cost.type] = Math.max(0, bag[cost.type] - cost.amount);
} }
/** 生成单次升级消耗文案 */ /** 生成单次升级消耗文案 */
private buildFragmentCostText(cost: { type: TalentFragmentType; amount: number }): string { private buildFragmentCostText(cost: { type: TalentFragmentType; amount: number }): string {
const info = this.getFragmentInfo(cost.type); const info = this.getFragmentInfo(cost.type);
const own = smc.collection.talent_fragments?.[cost.type] || 0; const own = smc.collection.talent_fragments[cost.type];
const fragmentName = info ? info.name : `碎片${cost.type}`; const fragmentName = info ? info.name : `碎片${cost.type}`;
return `${fragmentName} ${own}/${cost.amount}`; return `${fragmentName} ${own}/${cost.amount}`;
} }
/** 顶部碎片库存摘要 */ /** 顶部碎片库存摘要 */
private getFragmentSummaryText(): string { private getFragmentSummaryText(): string {
const bag = smc.collection.talent_fragments || {}; const bag = smc.collection.talent_fragments;
const summary = TalentConfig.fragments const summary = TalentConfig.fragments
.filter(fragment => (bag[fragment.id] || 0) > 0) .filter(fragment => bag[fragment.id] > 0)
.map(fragment => `${fragment.name}:${bag[fragment.id] || 0}`) .map(fragment => `${fragment.name}:${bag[fragment.id]}`)
.join(" "); .join(" ");
return summary ? `碎片: ${summary}` : "碎片: 暂无"; return summary ? `碎片: ${summary}` : "碎片: 暂无";
} }