3 Commits

Author SHA1 Message Date
panw
95ea36651e feat(天赋系统): 实现天赋效果并应用至相关游戏系统
- 在 MissionCardComp 中应用 RefreshDiscount 天赋以减少刷新消耗
- 在 CardComp 中应用 BuyDiscount 天赋以减少英雄购买消耗
- 在 HInfoComp 中应用 SellBonus 天赋以增加英雄出售收益
- 统一 TalentType 枚举类型,增强类型安全性
- 更新 SingletonModuleComp 中 talents 数据结构以支持类型化
- 修改 HeroAttrsComp.getTalentValue 方法参数类型为 TalentType
2026-04-28 15:34:58 +08:00
panw
1a45c87e70 feat(hero): 为英雄系统添加天赋加成支持
- 在 HeroAttrsComp 中添加 getTalentValue 静态方法,用于获取指定天赋的加成数值
- 定义 TalentType 枚举,明确各类天赋类型
- 调整部分天赋配置,如亡语强化和召唤强化的数值与消耗
- 在 Hero 实体初始化时,根据英雄阵营应用攻击、生命、暴击等天赋加成
- 在召唤技能触发逻辑中,增加召唤强化天赋的额外触发次数
2026-04-28 15:26:47 +08:00
panw
738ecf3bf8 feat(天赋配置): 调整天赋配置以支持新技能机制
- 将"护盾强化"天赋替换为"亡语强化",提供死亡触发技能额外次数
- 新增"召唤强化"天赋,提供召唤触发技能额外次数
- 调整采购、刷新、出售天赋为单级天赋,并提高消耗成本
- 重新分配天赋ID以保持连续性
2026-04-28 15:11:28 +08:00
8 changed files with 104 additions and 29 deletions

View File

@@ -7,6 +7,8 @@ import { WxCloudApi } from "../wx_clound_client_api/WxCloudApi";
import { GameEvent } from "./config/GameEvent"; import { GameEvent } from "./config/GameEvent";
import { GameScoreStats } from "./config/HeroAttrs"; import { GameScoreStats } from "./config/HeroAttrs";
import { mLogger } from "./Logger"; import { mLogger } from "./Logger";
import { TalentType } from "./config/TalentSet";
/** /**
* 用远程数据覆盖本地数据(统一方法) * 用远程数据覆盖本地数据(统一方法)
* @param remoteData 远程数据(云端或本地调试) * @param remoteData 远程数据(云端或本地调试)
@@ -16,7 +18,7 @@ interface GameDate{
heros:any, heros:any,
fight_hero:number, fight_hero:number,
collection?: { collection?: {
talents: Record<number, number>, talents: Partial<Record<TalentType, number>>,
player_level: number, player_level: number,
player_exp: number, player_exp: number,
talent_points: number, talent_points: number,
@@ -63,7 +65,7 @@ export class SingletonModuleComp extends ecs.Comp {
heros:any= [5001] heros:any= [5001]
collection: { collection: {
talents: Record<number, number>; talents: Partial<Record<TalentType, number>>;
player_level: number; player_level: number;
player_exp: number; player_exp: number;
talent_points: number; talent_points: number;

View File

@@ -3,6 +3,20 @@
* @description 天赋系统配置数据,包含经验要求、消耗、每个天赋的具体加成数值和描述。 * @description 天赋系统配置数据,包含经验要求、消耗、每个天赋的具体加成数值和描述。
*/ */
export enum TalentType {
Attack = 1, // 攻击强化
Hp = 2, // 生命强化
Critical = 3, // 暴击强化
WindFury = 4, // 风怒强化
Freeze = 5, // 冰冻强化
Puncture = 6, // 穿刺强化
DeadTrigger = 7,// 亡语强化
Summon = 8, // 召唤强化
BuyDiscount = 9,// 采购优惠
RefreshDiscount = 10, // 刷新优惠
SellBonus = 11 // 出售补贴
}
export interface TalentInfo { export interface TalentInfo {
/** 天赋 ID */ /** 天赋 ID */
id: number; id: number;
@@ -28,27 +42,29 @@ export const TalentConfig = {
{ maxLevel: 30, expPerLevel: 200 } { maxLevel: 30, expPerLevel: 200 }
], ],
// 天赋列表 // 所有天赋定义(使用数组维护)
talents: [ talents: [
{ id: 1, name: "攻击强化", icon: "⚔️", desc: "所有英雄 ATK +{value}%", { 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] },
{ id: 2, name: "生命强化", icon: "❤️", desc: "所有英雄 HP +{value}%", { 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] },
{ id: 3, name: "暴击强化", icon: "🔥", desc: "所有英雄暴击率 +{value}%", { 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] },
{ id: 4, name: "风怒强化", icon: "⚡", desc: "所有英雄风怒率 +{value}%", { 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] },
{ id: 5, name: "冰冻强化", icon: "❄️", desc: "所有英雄冰冻率 +{value}%", { 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] },
{ id: 6, name: "穿刺强化", icon: "🗡️", desc: "所有英雄穿刺 +{value}", { 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] },
{ id: 7, name: "护盾强化", icon: "🛡️", desc: "所有护盾效果 +{value}%", { id: TalentType.DeadTrigger, name: "亡语强化", icon: "🛡️", desc: "死亡触发技能额外触发次数+{value}",
maxLevel: 5, values: [5, 10, 15, 20, 25], costs: [1, 1, 2, 2, 3] }, maxLevel: 5, values: [1], costs: [25] },
{ id: 8, name: "采购优惠", icon: "🛒", desc: "购买英雄 -{value}", { id: TalentType.Summon, name: "召唤强化", icon: "🛡️", desc: "召唤触发技能额外触发次数+{value}",
maxLevel: 5, values: [1, 2, 3, 4, 5], costs: [1, 1, 2, 2, 3] }, maxLevel: 1, values: [1], costs: [25] },
{ id: 9, name: "刷新优惠", icon: "🔄", desc: "刷新重抽 -{value}金", { id: TalentType.BuyDiscount, name: "采购优惠", icon: "🛒", desc: "购买英雄 -{value}金",
maxLevel: 5, values: [0.5, 1.0, 1.5, 2.0, 2.5], costs: [1, 1, 2, 2, 3] }, maxLevel: 1, values: [1], costs: [10] },
{ id: 10, name: "出售补贴", icon: "💰", desc: "出售英雄返还 +{value}%金币", { id: TalentType.RefreshDiscount, name: "刷新优惠", icon: "🔄", desc: "刷新重抽 -{value}",
maxLevel: 5, values: [10, 20, 30, 40, 50], costs: [1, 1, 2, 2, 3] } maxLevel: 1, values: [1], costs: [10] },
{ id: TalentType.SellBonus, name: "出售补贴", icon: "💰", desc: "出售英雄返还 +{value}金币",
maxLevel: 1, values: [1], costs: [10] }
] as TalentInfo[] ] as TalentInfo[]
}; };

View File

@@ -11,6 +11,7 @@ import { Attrs} from "../common/config/HeroAttrs";
import { MoveComp } from "./MoveComp"; import { MoveComp } from "./MoveComp";
import { mLogger } from "../common/Logger"; import { mLogger } from "../common/Logger";
import { FieldSkillType } from "../common/config/SkillSet"; import { FieldSkillType } from "../common/config/SkillSet";
import { TalentType } from "../common/config/TalentSet";
/** 英雄实体:负责英雄节点创建、属性初始化、入场动画与销毁流程 */ /** 英雄实体:负责英雄节点创建、属性初始化、入场动画与销毁流程 */
@ecs.register(`Hero`) @ecs.register(`Hero`)
@@ -120,8 +121,25 @@ export class Hero extends ecs.Entity {
// 基础属性按等级倍率初始化 // 基础属性按等级倍率初始化
// 使用指数增长公式等级2时为原来的3倍等级3时为原来的9倍 (若需线性增长可改为 hero.ap * (1 + (model.lv - 1) * (FightSet.H_HERO_POW - 1))) // 使用指数增长公式等级2时为原来的3倍等级3时为原来的9倍 (若需线性增长可改为 hero.ap * (1 + (model.lv - 1) * (FightSet.H_HERO_POW - 1)))
model.ap = hero.ap * Math.pow(FightSet.MERGE_NEED, model.lv - 1); let base_ap = hero.ap * Math.pow(FightSet.MERGE_NEED, model.lv - 1);
model.hp = model.hp_max = hero.hp * Math.pow(FightSet.MERGE_NEED, model.lv - 1); let base_hp = hero.hp * Math.pow(FightSet.MERGE_NEED, model.lv - 1);
// 应用天赋加成
if (model.fac === FacSet.HERO) {
let apBonus = HeroAttrsComp.getTalentValue(TalentType.Attack); // 攻击强化
let hpBonus = HeroAttrsComp.getTalentValue(TalentType.Hp); // 生命强化
model.ap = base_ap * (1 + apBonus / 100);
model.hp = model.hp_max = base_hp * (1 + hpBonus / 100);
model.critical = HeroAttrsComp.getTalentValue(TalentType.Critical); // 暴击强化
model.wfuny = HeroAttrsComp.getTalentValue(TalentType.WindFury); // 风怒强化
model.freeze_chance = HeroAttrsComp.getTalentValue(TalentType.Freeze); // 冰冻强化
model.puncture = HeroAttrsComp.getTalentValue(TalentType.Puncture); // 穿刺强化
// 护盾强化 和 亡语强化 在对应逻辑中应用
} else {
model.ap = base_ap;
model.hp = model.hp_max = base_hp;
}
model.speed = hero.speed; model.speed = hero.speed;
// 构建技能表并注入运行时冷却字段 ccd // 构建技能表并注入运行时冷却字段 ccd
@@ -203,6 +221,9 @@ export class Hero extends ecs.Entity {
// 落地后触发 call 技能 // 落地后触发 call 技能
if (model && model.call && model.call.length > 0) { if (model && model.call && model.call.length > 0) {
let triggerCount = 1 + HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SummonCount); let triggerCount = 1 + HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SummonCount);
if (model.fac === FacSet.HERO) {
triggerCount += HeroAttrsComp.getTalentValue(TalentType.Summon); // 召唤强化额外次数
}
triggerCount = Math.max(1, Math.floor(triggerCount)); triggerCount = Math.max(1, Math.floor(triggerCount));
for (let i = 0; i < triggerCount; i++) { for (let i = 0; i < triggerCount; i++) {

View File

@@ -4,6 +4,8 @@ import { mLogger } from "../common/Logger";
import { Timer } from "db://oops-framework/core/common/timer/Timer"; import { Timer } from "db://oops-framework/core/common/timer/Timer";
import { FacSet, FightSet } from "../common/config/GameSet"; import { FacSet, FightSet } from "../common/config/GameSet";
import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet"; import { FieldSkillSet, FieldSkillType } from "../common/config/SkillSet";
import { smc } from "../common/SingletonModuleComp";
import { TalentConfig, TalentType } from "../common/config/TalentSet";
@ecs.register('HeroAttrs') @ecs.register('HeroAttrs')
export class HeroAttrsComp extends ecs.Comp { export class HeroAttrsComp extends ecs.Comp {
public debugMode: boolean = false; public debugMode: boolean = false;
@@ -298,6 +300,16 @@ export class HeroAttrsComp extends ecs.Comp {
}); });
return total; return total;
} }
/** 获取指定天赋的加成数值 */
public static getTalentValue(talentId: TalentType): number {
if (!smc || !smc.collection || !smc.collection.talents) return 0;
let level = smc.collection.talents[talentId] || 0;
if (level <= 0) return 0;
let talentInfo = TalentConfig.talents.find(t => t.id === talentId);
if (!talentInfo || !talentInfo.values || level > talentInfo.values.length) return 0;
return talentInfo.values[level - 1];
}
} }
@ecs.register('HeroBuffSystem') @ecs.register('HeroBuffSystem')

View File

@@ -30,6 +30,8 @@ import { GameEvent } from "../common/config/GameEvent";
import { oops } from "db://oops-framework/core/Oops"; import { oops } from "db://oops-framework/core/Oops";
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
import { UIID } from "../common/config/GameUIConfig"; import { UIID } from "../common/config/GameUIConfig";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { TalentType } from "../common/config/TalentSet";
@@ -272,7 +274,14 @@ export class CardComp extends CCComp {
this.cardData = data; this.cardData = data;
this.card_uuid = data.uuid; this.card_uuid = data.uuid;
this.card_type = data.type; this.card_type = data.type;
this.card_cost = data.cost;
let baseCost = data.cost ?? 0;
if (this.card_type === CardType.Hero) {
const discount = HeroAttrsComp.getTalentValue(TalentType.BuyDiscount);
baseCost = Math.max(0, baseCost - discount);
}
this.card_cost = Math.floor(baseCost);
this.node.active = true; this.node.active = true;
this.isEnlarged = false; this.isEnlarged = false;
this.applyCardUI(); this.applyCardUI();
@@ -300,7 +309,7 @@ export class CardComp extends CCComp {
*/ */
useCard(): CardConfig | null { useCard(): CardConfig | null {
if (!this.cardData || this.isUsing) return null; if (!this.cardData || this.isUsing) return null;
const cardCost = Math.max(0, Math.floor(this.cardData.cost ?? 0)); const cardCost = this.card_cost;
const currentCoin = this.getMissionCoin(); const currentCoin = this.getMissionCoin();
// 金币不足 → 提示并回弹 // 金币不足 → 提示并回弹
if (currentCoin < cardCost) { if (currentCoin < cardCost) {

View File

@@ -24,6 +24,8 @@ import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ec
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp"; import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { HeroInfo } from "../common/config/heroSet"; import { HeroInfo } from "../common/config/heroSet";
import { HeroAttrsComp } from "../hero/HeroAttrsComp"; import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { smc } from "../common/SingletonModuleComp";
import { TalentType } from "../common/config/TalentSet";
import { Hero } from "../hero/Hero"; import { Hero } from "../hero/Hero";
import { FieldSkillType } from "../common/config/SkillSet"; import { FieldSkillType } from "../common/config/SkillSet";
import { GameEvent } from "../common/config/GameEvent"; import { GameEvent } from "../common/config/GameEvent";
@@ -267,7 +269,15 @@ export class HInfoComp extends CCComp {
// 卖出英雄金币收益 // 卖出英雄金币收益
const baseSellGold = 1; // 基础卖出金币 const baseSellGold = 1; // 基础卖出金币
const goldBoost = HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SellGold); const goldBoost = HeroAttrsComp.getFieldSkillTotalValue(FieldSkillType.SellGold);
const totalSellGold = baseSellGold + goldBoost; let totalSellGold = baseSellGold + goldBoost;
// 应用天赋 SellBonus (增加数值)
const bonusGold = HeroAttrsComp.getTalentValue(TalentType.SellBonus);
if (bonusGold > 0) {
totalSellGold += bonusGold;
}
totalSellGold = Math.floor(totalSellGold);
oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: totalSellGold }); oops.message.dispatchEvent(GameEvent.CoinAdd, { delta: totalSellGold });
oops.gui.remove(UIID.IBox); oops.gui.remove(UIID.IBox);

View File

@@ -48,6 +48,7 @@ import { HeroViewComp } from "../hero/HeroViewComp";
import { FacSet, FightSet } from "../common/config/GameSet"; import { FacSet, FightSet } from "../common/config/GameSet";
import { MoveComp } from "../hero/MoveComp"; import { MoveComp } from "../hero/MoveComp";
import { MissionHeroCompComp } from "./MissionHeroComp"; import { MissionHeroCompComp } from "./MissionHeroComp";
import { TalentType } from "../common/config/TalentSet";
const { ccclass, property } = _decorator; const { ccclass, property } = _decorator;
@@ -840,7 +841,10 @@ export class MissionCardComp extends CCComp {
} }
private getRefreshCost(): number { private getRefreshCost(): number {
return Math.max(0, Math.floor(this.refreshCost)); let cost = this.refreshCost;
const discount = HeroAttrsComp.getTalentValue(TalentType.RefreshDiscount);
cost = Math.max(0, cost - discount);
return Math.floor(cost);
} }
private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) { private ensureHeroInfoPanel(eid: number, model: HeroAttrsComp) {

View File

@@ -24,7 +24,7 @@ import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/modu
import { mLogger } from "../common/Logger"; import { mLogger } from "../common/Logger";
import { smc } from "../common/SingletonModuleComp"; import { smc } from "../common/SingletonModuleComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops"; import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { TalentConfig, TalentInfo } from "../common/config/TalentSet"; import { TalentConfig, TalentInfo, TalentType } from "../common/config/TalentSet";
const { ccclass, property } = _decorator; const { ccclass, property } = _decorator;
@@ -145,14 +145,14 @@ export class TalentsComp extends CCComp {
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] || 0); this.updateTalentItem(itemNode, talentInfo, collection.talents[talentInfo.id as TalentType] || 0);
}); });
} 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] || 0); this.updateTalentItem(itemNode, talentInfo, collection.talents[talentInfo.id as TalentType] || 0);
} }
}); });
} }
@@ -197,7 +197,7 @@ export class TalentsComp extends CCComp {
} }
/** 点击升级按钮 */ /** 点击升级按钮 */
private onUpgradeClicked(talentId: number, currentLevel: number, cost: number) { private onUpgradeClicked(talentId: TalentType, currentLevel: number, cost: number) {
const collection = smc.collection; const collection = smc.collection;
let points = collection.talent_points || 0; let points = collection.talent_points || 0;
@@ -228,8 +228,9 @@ export class TalentsComp extends CCComp {
// 计算已消耗的天赋点总和 // 计算已消耗的天赋点总和
let refundedPoints = 0; let refundedPoints = 0;
for (let id in collection.talents) { for (let id in collection.talents) {
let level = collection.talents[id]; let talentId = Number(id) as TalentType;
let talentInfo = TalentConfig.talents.find(t => t.id === Number(id)); let level = collection.talents[talentId] || 0;
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++) {
refundedPoints += talentInfo.costs[i]; refundedPoints += talentInfo.costs[i];