Files
pixelheros/assets/script/game/map/MissionComp.ts
panw 940211d465 feat: 实现角色经验获取与升级系统
- 在SingletonModuleComp中新增addExp方法,处理经验累积和升级逻辑
- 修改英雄初始等级从0调整为1,确保等级系统正常运作
- 在MissionComp中为不同怪物类型添加经验奖励计算
- 使用getLevelExp函数动态计算各级所需经验值
- 升级时触发GameEvent.CanUpdateLv事件通知其他模块
2026-01-28 15:51:59 +08:00

312 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { _decorator, Vec3,Animation, instantiate, Prefab, Node, ProgressBar, Label } from "cc";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { CCComp } from "../../../../extensions/oops-plugin-framework/assets/module/common/CCComp";
import { smc } from "../common/SingletonModuleComp";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { MonsterCost, MonType, calculateMonsterGold, getLevelExp } from "./RogueConfig";
import { GameEvent } from "../common/config/GameEvent";
import { HeroViewComp } from "../hero/HeroViewComp";
import { UIID } from "../common/config/GameUIConfig";
import { SkillView } from "../skill/SkillView";
import { FightSet, getLevelRewardType, CardType, FacSet } from "../common/config/GameSet";
const { ccclass, property } = _decorator;
//@todo 需要关注 当boss死亡的时候的动画播放完成后需要触发事件通知 MissionComp 进行奖励处理
/** 视图层对象 */
@ccclass('MissionComp')
@ecs.register('MissionComp', false)
export class MissionComp extends CCComp {
// VictoryComp:any = null;
// reward:number = 0;
// reward_num:number = 0;
@property(Node)
coins_node:Node = null!
@property(Node)
lv_node:Node = null!
@property(Node)
chou_node:Node = null!
@property(Node)
time_node:Node = null!
/** 剩余复活次数 */
revive_times: number = 1;
rewards:any[]=[]
game_data:any={
exp:0,
gold:0,
diamond:0
}
onLoad(){
this.on(GameEvent.MissionStart,this.mission_start,this)
this.on(GameEvent.MonDead,this.do_mon_dead,this)
this.on(GameEvent.HeroDead,this.do_hero_dead,this)
// this.on(GameEvent.FightEnd,this.fight_end,this)
this.on(GameEvent.MissionEnd,this.mission_end,this)
this.on(GameEvent.DO_AD_BACK,this.do_ad,this)
this.on(GameEvent.CanUpdateLv,this.onLevelUp,this)
this.on(GameEvent.ReviveSuccess, this.onReviveSuccess, this)
}
protected update(dt: number): void {
if(!smc.mission.play) return
if(smc.mission.pause) return
if(smc.mission.in_fight){
if(smc.mission.stop_mon_action) return
smc.vmdata.mission_data.fight_time+=dt
smc.vmdata.mission_data.time-=dt
}
}
// 升级奖励触发
onLevelUp(event: string, args: any) {
console.log(`[MissionComp] 英雄升级到 ${args.lv} 级!`);
this.call_cards(args.lv)
// 获取当前等级对应的奖励类型
// 触发奖励选择界面 (暂时留空)
// this.showLevelUpReward();
}
call_cards(lv:number){
const rewardType = getLevelRewardType(lv);
console.log(`[MissionComp] 触发奖励选择, 类型: ${rewardType}`);
// 根据类型发送对应的事件
switch (rewardType) {
case CardType.Talent:
oops.message.dispatchEvent(GameEvent.TalentSelect);
break;
case CardType.Skill:
oops.message.dispatchEvent(GameEvent.HeroSkillSelect);
break;
case CardType.Partner:
oops.message.dispatchEvent(GameEvent.ToCallFriend);
break;
case CardType.Potion:
oops.message.dispatchEvent(GameEvent.ShopOpen);
break;
default:
console.warn(`[MissionComp] 未知的奖励类型: ${rewardType}`);
oops.message.dispatchEvent(GameEvent.TalentSelect); // 默认回退到天赋选择
break;
}
}
showLevelUpReward() {
// TODO: 显示三选一技能/属性奖励界面
console.log("[MissionComp] 显示升级奖励界面 (TODO)");
}
//奖励发放
do_reward(){
// 奖励发放
}
do_mon_dead(event:any,data:any){
// console.log("[MissionComp] do_mon_dead",event,data)
smc.vmdata.mission_data.mon_num--
// 计算并增加经验
// data 应该是怪物组件或包含怪物信息的对象
if (data && data.uuid) {
// 默认值处理
const level = data.lv || 1;
// 类型推断
let type = MonType.NORMAL;
if (data.is_boss) {
type = MonType.BOSS;
} else if (data.is_elite) {
type = MonType.ELITE;
} else {
// 兜底策略根据Cost判断是否为精英怪
const cost = MonsterCost[data.uuid] || 1;
if (cost >= 10) {
type = MonType.ELITE;
}
}
// 计算经验
let exp = 10; // 基础经验
switch (type) {
case MonType.BOSS:
exp = 200;
break;
case MonType.ELITE:
exp = 50;
break;
default:
exp = 10;
break;
}
smc.addExp(exp);
this.cal_gold_reward(data, type);
}
}
cal_gold_reward(data: any, type: MonType) {
const cost = MonsterCost[data.uuid] || 1;
const level = data.lv || 1;
let add_gold = calculateMonsterGold(data.uuid, level, type);
smc.updateGold(add_gold, false);
}
do_hero_dead(event:any,data:any){
// console.log("[MissionComp] do_hero_dead",event,data)
// 收到 HeroDead 说明已经没有复活次数了,打开失败界面,等待玩家选择(复活或结束)
// oops.message.dispatchEvent(GameEvent.FightEnd,{victory:false}) // 暂时不分发结束事件
this.open_Victory(null,true)
}
do_ad(){
if(this.ad_back()){
oops.message.dispatchEvent(GameEvent.AD_BACK_TRUE)
smc.vmdata.mission_data.refresh_count+=FightSet.MORE_RC
}else{
oops.message.dispatchEvent(GameEvent.AD_BACK_FALSE)
}
}
ad_back(){
return true
}
async mission_start(){
// 防止上一局的 fight_end 延迟回调干扰新局
this.unscheduleAllCallbacks();
// 确保清理上一局的残留实体
this.cleanComponents();
// console.log("[MissionComp] ** 1 ** mission_start")
oops.message.dispatchEvent(GameEvent.FightReady)
this.node.active=true
this.data_init()
let loading=this.node.parent.getChildByName("loading")
loading.active=true
this.scheduleOnce(()=>{
loading.active=false
},0.5)
this.scheduleOnce(()=>{
this.to_fight()
},0.1)
}
to_fight(){
smc.mission.in_fight=true
oops.message.dispatchEvent(GameEvent.FightStart) //GameSetMonComp 监听刷怪
}
open_Victory(e:any,is_hero_dead: boolean = false){
// 暂停游戏循环和怪物行为
// smc.mission.play = false;
smc.mission.pause = true;
// oops.message.dispatchEvent(GameEvent.FightEnd,{victory:false})
console.log("[MissionComp] open_Victory",is_hero_dead,this.revive_times)
oops.gui.open(UIID.Victory,{
victory:false,
rewards:this.rewards,
game_data:this.game_data,
can_revive: is_hero_dead && this.revive_times > 0
})
}
/** 复活成功回调,扣除次数 */
onReviveSuccess() {
if (this.revive_times > 0) {
this.revive_times--;
console.log(`[MissionComp] 玩家复活,剩余次数: ${this.revive_times}`);
}
}
fight_end(){
// console.log("任务结束")
// 延迟0.5秒后执行任务结束逻辑
this.scheduleOnce(() => {
smc.mission.play=false
this.cleanComponents()
}, 0.5)
}
mission_end(){
// console.log("[MissionComp] mission_end")
// 合并 FightEnd 逻辑:清理组件、停止游戏循环
smc.mission.play=false
this.cleanComponents()
this.node.active=false
}
data_init(){
// 重置金币为初始值 (如果需要保留金币,请注释掉此行)
smc.vmdata.gold = 0;
smc.vmdata.chou_gold = FightSet.CHOU_GOLD;
smc.vmdata.lvup_gold = FightSet.LVUP_GOLD_UP;
//局内数据初始化 smc 数据初始化
smc.mission.play = true;
smc.mission.pause = false;
smc.mission.stop_mon_action = false;
smc.vmdata.mission_data.in_fight=false
smc.vmdata.mission_data.fight_time=0
smc.vmdata.mission_data.level=0
smc.vmdata.mission_data.time=15*60
this.rewards=[] // 改为数组,用于存储掉落物品列表
this.revive_times = 1; // 每次任务开始重置复活次数
// 重置英雄数据,确保新一局是初始状态
smc.vmdata.hero = {
name:'',
path:'',
as:0,
type:0,
lv:1,
exp:0,
exp_max: getLevelExp(1),
exp_pre:0,
hp:50,
hp_max:100,
mp:50,
mp_max:100,
def:0,
ap:0,
dis:0,
crt:0,
speed:0,
skills:[],
buff:[],
tal:[],
info:'',
};
// console.log("[MissionComp]局内数据初始化",smc.vmdata.mission_data)
}
private cleanComponents() {
// 优化销毁顺序直接销毁实体让ECS系统自动处理组件清理
// 这样可以避免在组件reset方法中访问已经被销毁的实体引用
ecs.query(ecs.allOf(HeroViewComp)).forEach(entity => {
entity.destroy();
});
ecs.query(ecs.allOf(SkillView)).forEach(entity => {
entity.destroy();
});
}
/** 视图层逻辑代码分离演示 */
/** 视图对象通过 ecs.Entity.remove(ModuleViewComp) 删除组件是触发组件处理自定义释放逻辑 */
reset() {
this.node.destroy();
}
}