diff --git a/assets/script/game/common/SingletonModuleComp.ts b/assets/script/game/common/SingletonModuleComp.ts index 69f107dc..e96dbdb9 100644 --- a/assets/script/game/common/SingletonModuleComp.ts +++ b/assets/script/game/common/SingletonModuleComp.ts @@ -8,7 +8,7 @@ import { WxCloudApi } from "../wx_clound_client_api/WxCloudApi"; import { GameEvent } from "./config/GameEvent"; import { GameScoreStats } from "./config/HeroAttrs"; import { mLogger } from "./Logger"; -import { TalentType } from "./config/TalentSet"; +import { TalentFragmentType, TalentType } from "./config/TalentSet"; import { gameDataSync } from "./GameDataSync"; import { FightSet } from "./config/GameSet"; @@ -23,7 +23,8 @@ export interface GameDate{ talents: Partial>, player_level: number, player_exp: number, - talent_points: number, + talent_fragments: Partial>, + talent_points?: number, } } export interface CloudData { @@ -66,12 +67,14 @@ export class SingletonModuleComp extends ecs.Comp { talents: Partial>; player_level: number; player_exp: number; - talent_points: number; + talent_fragments: Partial>; + talent_points?: number; } = { talents: {}, // 存储各个天赋的等级: { talent_id: level } player_level: 1, // 玩家等级 player_exp: 0, // 玩家当前经验 - talent_points: 0, // 当前可用天赋点 + talent_fragments: {}, // 当前拥有的天赋碎片库存 + talent_points: 0, // 兼容旧存档的历史字段 }; vmdata: any = { @@ -240,6 +243,7 @@ export class SingletonModuleComp extends ecs.Comp { if (remoteCol.talents) this.collection.talents = remoteCol.talents; 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 (remoteCol.talent_fragments) this.collection.talent_fragments = remoteCol.talent_fragments; if (typeof remoteCol.talent_points === 'number') this.collection.talent_points = remoteCol.talent_points; } } diff --git a/assets/script/game/common/config/TalentSet.ts b/assets/script/game/common/config/TalentSet.ts index 959f9785..93298b60 100644 --- a/assets/script/game/common/config/TalentSet.ts +++ b/assets/script/game/common/config/TalentSet.ts @@ -17,6 +17,28 @@ export enum TalentType { SellBonus = 11 // 出售补贴 } +export enum TalentFragmentType { + Power = 1, // 力量碎片 + Tempest = 2, // 风暴碎片 + Vitality = 3, // 生机碎片 + Frost = 4, // 寒霜碎片 + Tactics = 5, // 谋略碎片 + Spirit = 6, // 灵契碎片 + Trade = 7, // 商贸碎片 + Fate = 8 // 命运碎片 +} + +export interface TalentFragmentInfo { + /** 碎片 ID */ + id: TalentFragmentType; + /** 碎片名称 */ + name: string; + /** 碎片图标或标识(可选) */ + icon?: string; + /** 该碎片可用于哪些天赋,单个碎片最多映射 3 个天赋 */ + talentIds: TalentType[]; +} + export interface TalentInfo { /** 天赋 ID */ id: number; @@ -30,8 +52,10 @@ export interface TalentInfo { maxLevel: number; /** 每一级的加成数值,从第1级到最大级 */ values: number[]; - /** 升到每一级所需的消耗点数,从第1级到最大级 */ + /** 每一级的消耗数量,下标 0 表示升到 1 级 */ costs: number[]; + /** 升级所需的单碎片类型 */ + fragmentType: TalentFragmentType; } export const TalentConfig = { @@ -41,30 +65,42 @@ export const TalentConfig = { { maxLevel: 20, expPerLevel: 150 }, { maxLevel: 30, expPerLevel: 200 } ], + + // 天赋碎片配置:不同天赋可共用同一种碎片,单个碎片最多映射 3 个天赋 + fragments: [ + { id: TalentFragmentType.Power, name: "力量碎片", icon: "◆", talentIds: [TalentType.Attack, TalentType.Critical, TalentType.Puncture] }, + { id: TalentFragmentType.Tempest, name: "风暴碎片", icon: "◇", talentIds: [TalentType.WindFury, TalentType.RefreshDiscount] }, + { id: TalentFragmentType.Vitality, name: "生机碎片", icon: "●", talentIds: [TalentType.Hp, TalentType.DeadTrigger, TalentType.Summon] }, + { id: TalentFragmentType.Frost, name: "寒霜碎片", icon: "○", talentIds: [TalentType.Freeze] }, + { id: TalentFragmentType.Tactics, name: "谋略碎片", icon: "■", talentIds: [TalentType.BuyDiscount] }, + { id: TalentFragmentType.Spirit, name: "灵契碎片", icon: "□", talentIds: [TalentType.SellBonus] }, + { id: TalentFragmentType.Trade, name: "商贸碎片", icon: "▲", talentIds: [] }, + { id: TalentFragmentType.Fate, name: "命运碎片", icon: "△", talentIds: [] } + ] as TalentFragmentInfo[], // 所有天赋定义(使用数组维护) talents: [ { id: TalentType.Attack, name: "攻击强化", icon: "⚔️", desc: "所有英雄 ATK +{value}%", - maxLevel: 5, values: [3, 6, 9, 12, 15], costs: [1, 1, 2, 2, 3] }, + maxLevel: 5, values: [3, 6, 9, 12, 15], costs: [1, 1, 2, 2, 3], fragmentType: TalentFragmentType.Power }, { id: TalentType.Hp, name: "生命强化", icon: "❤️", desc: "所有英雄 HP +{value}%", - maxLevel: 5, values: [5, 10, 15, 20, 25], costs: [1, 1, 2, 2, 3] }, + maxLevel: 5, values: [5, 10, 15, 20, 25], costs: [1, 1, 2, 2, 3], fragmentType: TalentFragmentType.Vitality }, { id: TalentType.Critical, name: "暴击强化", icon: "🔥", desc: "所有英雄暴击率 +{value}%", - maxLevel: 5, values: [2, 4, 6, 8, 10], costs: [1, 1, 2, 2, 3] }, + maxLevel: 5, values: [2, 4, 6, 8, 10], costs: [1, 1, 2, 2, 3], fragmentType: TalentFragmentType.Power }, { id: TalentType.WindFury, name: "风怒强化", icon: "⚡", desc: "所有英雄风怒率 +{value}%", - maxLevel: 5, values: [2, 4, 6, 8, 10], costs: [1, 1, 2, 2, 3] }, + maxLevel: 5, values: [2, 4, 6, 8, 10], costs: [1, 1, 2, 2, 3], fragmentType: TalentFragmentType.Tempest }, { id: TalentType.Freeze, name: "冰冻强化", icon: "❄️", desc: "所有英雄冰冻率 +{value}%", - maxLevel: 5, values: [2, 4, 6, 8, 10], costs: [1, 1, 2, 2, 3] }, + maxLevel: 5, values: [2, 4, 6, 8, 10], costs: [1, 1, 2, 2, 3], fragmentType: TalentFragmentType.Frost }, { id: TalentType.Puncture, name: "穿刺强化", icon: "🗡️", desc: "所有英雄穿刺 +{value}", - maxLevel: 5, values: [0.2, 0.4, 0.6, 0.8, 1.0], costs: [1, 1, 2, 2, 3] }, + maxLevel: 5, values: [0.2, 0.4, 0.6, 0.8, 1.0], costs: [1, 1, 2, 2, 3], fragmentType: TalentFragmentType.Power }, { id: TalentType.DeadTrigger, name: "亡语强化", icon: "🛡️", desc: "死亡触发技能额外触发次数+{value}次", - maxLevel: 5, values: [1], costs: [25] }, + maxLevel: 1, values: [1], costs: [25], fragmentType: TalentFragmentType.Vitality }, { id: TalentType.Summon, name: "召唤强化", icon: "🛡️", desc: "召唤触发技能额外触发次数+{value}次", - maxLevel: 1, values: [1], costs: [25] }, + maxLevel: 1, values: [1], costs: [25], fragmentType: TalentFragmentType.Vitality }, { id: TalentType.BuyDiscount, name: "采购优惠", icon: "🛒", desc: "购买英雄 -{value}金", - maxLevel: 1, values: [1], costs: [10] }, + maxLevel: 1, values: [1], costs: [10], fragmentType: TalentFragmentType.Tactics }, { id: TalentType.RefreshDiscount, name: "刷新优惠", icon: "🔄", desc: "刷新重抽 -{value}金", - maxLevel: 1, values: [1], costs: [10] }, + maxLevel: 1, values: [1], costs: [10], fragmentType: TalentFragmentType.Tempest }, { id: TalentType.SellBonus, name: "出售补贴", icon: "💰", desc: "出售英雄返还 +{value}金币", - maxLevel: 1, values: [1], costs: [10] } + maxLevel: 1, values: [1], costs: [10], fragmentType: TalentFragmentType.Spirit } ] as TalentInfo[] }; diff --git a/assets/script/game/map/TalentsComp.ts b/assets/script/game/map/TalentsComp.ts index 0d7b79d7..34ec99b9 100644 --- a/assets/script/game/map/TalentsComp.ts +++ b/assets/script/game/map/TalentsComp.ts @@ -24,7 +24,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu import { mLogger } from "../common/Logger"; import { smc } from "../common/SingletonModuleComp"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; -import { TalentConfig, TalentInfo, TalentType } from "../common/config/TalentSet"; +import { TalentConfig, TalentFragmentInfo, TalentFragmentType, TalentInfo, TalentType } from "../common/config/TalentSet"; const { ccclass, property } = _decorator; @@ -32,9 +32,9 @@ const { ccclass, property } = _decorator; * TalentsComp —— 天赋系统界面组件 * * 职责: - * 1. 展示玩家等级、当前经验、进度条、可用天赋点。 + * 1. 展示玩家等级、当前经验、进度条、碎片库存。 * 2. 展示天赋列表及每个天赋的当前等级。 - * 3. 处理天赋升级点击事件,扣除天赋点并保存。 + * 3. 处理天赋升级点击事件,扣除碎片并保存。 * 4. 处理重置天赋(看广告)功能。 */ @ccclass('TalentsComp') @@ -53,7 +53,7 @@ export class TalentsComp extends CCComp { @property({ type: ProgressBar, tooltip: "经验进度条" }) pb_exp: ProgressBar = null!; - @property({ type: Label, tooltip: "当前可用天赋点数文本,例如 '4/30'" }) + @property({ type: Label, tooltip: "当前碎片库存摘要文本" }) lbl_points: Label = null!; @property({ type: Node, tooltip: "天赋列表容器,用于动态添加天赋项" }) @@ -96,12 +96,12 @@ export class TalentsComp extends CCComp { this.updateTalentList(); } - /** 更新玩家等级、经验、天赋点信息 */ + /** 更新玩家等级、经验、碎片信息 */ private updatePlayerInfo() { const collection = smc.collection; let level = collection.player_level || 1; let exp = collection.player_exp || 0; - let points = collection.talent_points || 0; + const fragmentSummary = this.getFragmentSummaryText(); // 限制最大等级 if (level > this.MAX_PLAYER_LEVEL) { @@ -109,7 +109,7 @@ export class TalentsComp extends CCComp { } if (this.lbl_level) this.lbl_level.string = `Lv.${level}`; - if (this.lbl_points) this.lbl_points.string = `天赋点: ${points}/${this.MAX_PLAYER_LEVEL}`; + if (this.lbl_points) this.lbl_points.string = fragmentSummary; // 计算当前等级升级所需经验 let expRequired = this.getExpRequirement(level); @@ -179,31 +179,46 @@ export class TalentsComp extends CCComp { if (lblLevel) lblLevel.string = `Lv.${currentLevel}`; let isMax = currentLevel >= talentInfo.maxLevel; - let cost = isMax ? 0 : talentInfo.costs[currentLevel]; - let points = smc.collection.talent_points || 0; + const levelCost = isMax ? null : this.getLevelFragmentCost(talentInfo, currentLevel); + const canUpgrade = !!levelCost && this.hasEnoughFragments(levelCost); if (lblCost) { - lblCost.string = isMax ? "已满级" : `消耗: ${cost}点`; + lblCost.string = isMax ? "已满级" : `消耗: ${this.buildFragmentCostText(levelCost!)}`; } if (btnUpgrade) { - btnUpgrade.interactable = !isMax && points >= cost; + btnUpgrade.interactable = !isMax && canUpgrade; // 清除旧的监听,避免重复绑定 btnUpgradeNode?.off(Button.EventType.CLICK); btnUpgradeNode?.on(Button.EventType.CLICK, () => { - this.onUpgradeClicked(talentInfo.id, currentLevel, cost); + this.onUpgradeClicked(talentInfo.id, currentLevel); }, this); } } /** 点击升级按钮 */ - private onUpgradeClicked(talentId: TalentType, currentLevel: number, cost: number) { + private onUpgradeClicked(talentId: TalentType, currentLevel: number) { const collection = smc.collection; - let points = collection.talent_points || 0; + const talentInfo = TalentConfig.talents.find(t => t.id === talentId); + if (!talentInfo) { + oops.gui.toast("天赋配置不存在"); + return; + } - if (points >= cost && currentLevel < 5) { - // 1. 扣除消耗 - collection.talent_points -= cost; + if (currentLevel >= talentInfo.maxLevel) { + oops.gui.toast("该天赋已满级"); + return; + } + + const levelCost = this.getLevelFragmentCost(talentInfo, currentLevel); + if (!levelCost) { + oops.gui.toast("天赋消耗配置异常"); + return; + } + + if (this.hasEnoughFragments(levelCost)) { + // 1. 扣除单碎片消耗 + this.consumeFragments(levelCost); // 2. 更新等级 collection.talents[talentId] = currentLevel + 1; @@ -215,7 +230,7 @@ export class TalentsComp extends CCComp { oops.gui.toast("天赋升级成功"); } else { - oops.gui.toast("天赋点不足或已满级"); + oops.gui.toast("碎片不足或已满级"); } } @@ -225,26 +240,32 @@ export class TalentsComp extends CCComp { this.watch_ad().then(success => { if (success) { const collection = smc.collection; - // 计算已消耗的天赋点总和 - let refundedPoints = 0; + // 计算已消耗碎片并返还 + const refundedFragments: Partial> = {}; for (let id in collection.talents) { let talentId = Number(id) as TalentType; let level = collection.talents[talentId] || 0; let talentInfo = TalentConfig.talents.find(t => t.id === talentId); if (talentInfo) { for (let i = 0; i < level; i++) { - refundedPoints += talentInfo.costs[i]; + const levelCost = this.getLevelFragmentCost(talentInfo, i); + if (!levelCost) continue; + refundedFragments[levelCost.type] = (refundedFragments[levelCost.type] || 0) + levelCost.amount; } } } - // 重置天赋等级并返还点数 + // 重置天赋等级并返还碎片 collection.talents = {}; - collection.talent_points += refundedPoints; - - // 限制不超过最大点数(30) - if (collection.talent_points > this.MAX_PLAYER_LEVEL) { - collection.talent_points = this.MAX_PLAYER_LEVEL; + if (!collection.talent_fragments) { + collection.talent_fragments = {}; + } + for (const fragmentType in refundedFragments) { + const key = Number(fragmentType) as TalentFragmentType; + const amount = refundedFragments[key] || 0; + if (amount > 0) { + collection.talent_fragments[key] = (collection.talent_fragments[key] || 0) + amount; + } } // 同步到云端 @@ -252,13 +273,59 @@ export class TalentsComp extends CCComp { // 刷新界面 this.refreshUI(); - oops.gui.toast("天赋已重置,点数已返还"); + oops.gui.toast("天赋已重置,碎片已返还"); } else { oops.gui.toast("广告观看失败,无法重置"); } }); } + /** 获取升级到下一等级所需的单碎片消耗 */ + private getLevelFragmentCost(talentInfo: TalentInfo, currentLevel: number): { type: TalentFragmentType; amount: number } | null { + if (talentInfo.fragmentType === undefined) { + return null; + } + return { + type: talentInfo.fragmentType, + amount: talentInfo.costs[currentLevel] ?? 0 + }; + } + + /** 判断当前库存是否满足一次升级 */ + private hasEnoughFragments(cost: { type: TalentFragmentType; amount: number }): boolean { + const bag = smc.collection.talent_fragments || {}; + return (bag[cost.type] || 0) >= cost.amount; + } + + /** 执行碎片扣除 */ + private consumeFragments(cost: { type: TalentFragmentType; amount: number }) { + const bag = smc.collection.talent_fragments || (smc.collection.talent_fragments = {}); + bag[cost.type] = Math.max(0, (bag[cost.type] || 0) - cost.amount); + } + + /** 生成单次升级消耗文案 */ + private buildFragmentCostText(cost: { type: TalentFragmentType; amount: number }): string { + const info = this.getFragmentInfo(cost.type); + const own = smc.collection.talent_fragments?.[cost.type] || 0; + const fragmentName = info ? info.name : `碎片${cost.type}`; + return `${fragmentName} ${own}/${cost.amount}`; + } + + /** 顶部碎片库存摘要 */ + private getFragmentSummaryText(): string { + const bag = smc.collection.talent_fragments || {}; + const summary = TalentConfig.fragments + .filter(fragment => (bag[fragment.id] || 0) > 0) + .map(fragment => `${fragment.name}:${bag[fragment.id] || 0}`) + .join(" "); + return summary ? `碎片: ${summary}` : "碎片: 暂无"; + } + + /** 按碎片类型查找碎片配置 */ + private getFragmentInfo(type: TalentFragmentType): TalentFragmentInfo | undefined { + return TalentConfig.fragments.find(fragment => fragment.id === type); + } + /** 模拟看广告回调,实际项目中需要替换为真实的广告SDK调用 */ private watch_ad(): Promise { return new Promise((resolve) => { @@ -282,4 +349,4 @@ export class TalentsComp extends CCComp { reset() { this.node.destroy(); } -} \ No newline at end of file +}