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

392 lines
12 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 { VM } from "../../../../extensions/oops-plugin-framework/assets/libs/model-view/ViewModel";
import { ecs } from "../../../../extensions/oops-plugin-framework/assets/libs/ecs/ECS";
import { Initialize } from "../initialize/Initialize";
import { GameMap } from "../map/GameMap";
import { oops } from "../../../../extensions/oops-plugin-framework/assets/core/Oops";
import { WxCloudApi } from "../wx_clound_client_api/WxCloudApi";
import { GameEvent } from "./config/GameEvent";
import * as exp from "constants";
import { HeroAttrsComp } from "../hero/HeroAttrsComp";
import { Attrs, GameScoreStats } from "./config/HeroAttrs";
import { count, time } from "console";
import { getLevelExp } from "../map/RogueConfig";
import { FightSet } from "./config/GameSet";
/**
* 用远程数据覆盖本地数据(统一方法)
* @param remoteData 远程数据(云端或本地调试)
*/
interface GameDate{
gold:number,
heros:any,
fight_hero:number,
collection?: {
talents: Record<number, number>,
skills: {uuid:0,count:0},
friend:{uuid:0,count:0},
}
}
interface CloudData {
openid: string;
data: GameDate;
}
/** 游戏模块 */
@ecs.register('SingletonModule')
export class SingletonModuleComp extends ecs.Comp {
/** 游戏初始化模块 */
initialize: Initialize = null!;
/** 游戏地图 */
map: GameMap = null!;
openid:string=''
mission:any={
status:0, //0:未开始 1:进行中 2:胜利 3:失败
play:false,
pause:false,
in_select:false,
in_fight:false,
stop_mon_action:false,
};
data:any={
score:0,
mission:1,
diamond:100, //商店购买 及 双倍奖励资源
meat:0,
exp:0,
task:0,
noStop:false,
showInfo:true,
}
guides:any=[0,0,0,0,0]
current_guide:number=0
fight_hero: number = 5001; // 单个出战英雄
heros:any= [5001]
monsters:any = [];
vmdata: any = {
game_over:false,
game_pause:false,
mission_data:{
mon_num:0,//怪物数量
hero_num:0,//英雄数量
wave_time_num:0,//波次时间
in_fight:false,
fight_time:0,//战斗时间
level:1,//关卡等级
max_mission:4,//最大关卡
coin:0,
time:15*60,//游戏时间
unlockCoin: 20,
},
scores: {
score: 0, // 基础得分
// 战斗统计
crt_count: 0, // 暴击次数
wf_count: 0, // 风怒次数
dod_count: 0, // 闪避次数
back_count: 0, // 击退次数
stun_count: 0, // 击晕次数
freeze_count: 0, // 冰冻次数
// 伤害统计
total_dmg: 0, // 总伤害
atk_count: 0, // 攻击次数
avg_dmg: 0, // 平均伤害
thorns_dmg: 0, // 反伤伤害
crit_dmg_total: 0, // 暴击伤害总额
// 生存统计
heal_total: 0, // 治疗总量
lifesteal_total: 0, // 吸血总量
// 资源统计
exp_total: 0, // 经验总数
gold_total: 0, // 金币总数
// 击杀统计
melee_kill_count: 0, // 近战怪击杀数量
remote_kill_count: 0, // 远程怪击杀数量
elite_kill_count: 0, // 精英怪击杀数量
boss_kill_count: 0, // Boss击杀数
} as GameScoreStats,
hero:{
name:'',
path:'',
as:0,
type:0,
lv:1,
exp:0,
exp_max:100,
exp_pre:0,
hp:50,
hp_max:100,
mp:50,
mp_max:100,
def:0,
ap:0,
crt:0,
speed:0,
skills:[],
buff:[],
tal:[],
info:'',
},
// 收集记录
collection: {
talents: {} as Record<number, number>,
skill: {uuid:0,count:0},
friend:{uuid:0,count:0},
},
gold: 0, // 金币数据MVVM绑定字段
chou_gold: FightSet.CHOU_GOLD,
lvup_gold: FightSet.LVUP_GOLD_UP,
};
/**
* 记录天赋获取
* @param id 天赋ID
*/
addTalentRecord(id: number) {
if (!this.vmdata.collection.talents[id]) {
this.vmdata.collection.talents[id] = 0;
}
this.vmdata.collection.talents[id]++;
console.log(`[SMC] 记录天赋获取: ID=${id}, 次数=${this.vmdata.collection.talents[id]}`);
oops.message.dispatchEvent(GameEvent.UpdateCollection);
}
/**
* 记录技能获取
* @param id 技能ID
*/
addSkillRecord(id: number) {
if (!this.vmdata.collection.skill.uuid) {
this.vmdata.collection.skill.uuid = id;
}
this.vmdata.collection.skill.count++;
console.log(`[SMC] 记录技能获取: ID=${id}, 次数=${this.vmdata.collection.skill.count}`);
oops.message.dispatchEvent(GameEvent.UpdateCollection);
}
/**
* 记录好友获取
* @param id 好友ID
*/
addFriendHero(id: number) {
if (!this.vmdata.collection.friend.uuid) {
this.vmdata.collection.friend.uuid = id;
}
this.vmdata.collection.friend.count++;
console.log(`[SMC] 记录好友获取: ID=${id}, 次数=${this.vmdata.collection.friend.count}`);
oops.message.dispatchEvent(GameEvent.UpdateCollection);
}
/**
* 增加经验并处理升级逻辑
* @param exp 获得的经验值
*/
addExp(exp: number) {
if (exp <= 0) return;
const h = this.vmdata.hero;
// 确保等级至少为1
if (h.lv < 1) h.lv = 1;
// 确保经验上限正确
if (h.exp_max <= 0) h.exp_max = getLevelExp(h.lv);
h.exp += exp;
// 检查升级
let isLevelUp = false;
while (h.exp >= h.exp_max) {
h.exp -= h.exp_max;
h.lv++;
isLevelUp = true;
// 更新下一级所需经验
h.exp_max = getLevelExp(h.lv);
console.log(`[SMC] 升级! Lv.${h.lv - 1} -> Lv.${h.lv}, 下级所需: ${h.exp_max}`);
}
if (isLevelUp) {
// 发送升级事件
oops.message.dispatchEvent(GameEvent.CanUpdateLv, { lv: h.lv });
}
}
vmAdd() {
VM.add(this.vmdata, "data");
}
reset() {
for (var key in this.vmdata) {
delete this.vmdata[key];
}
}
// ==================== 数据管理方法 ====================
/**
* 判断是否为微信客户端
*/
private isWxClient(): boolean {
// 检查是否存在微信API
return typeof wx !== 'undefined' && typeof (wx as any).getSystemInfoSync === 'function';
}
finishGuide(index:number){
smc.guides[index]=1
//存储到远程服务器 后续再添加
}
updateCloudData(){
let gemeDate=this.getGameDate()
WxCloudApi.save(gemeDate).then((result) => {
console.log('云端保存')
if(result.result.code === 200) {
console.log("保存成功",result.result)
return true
} else {
console.warn(`[SMC]: 游戏数据增加失败: ${result.result.msg}`);
return false
}
}).catch((error) => {
console.error(`[SMC]: 增加游戏数据异常:`, error);
return false
});
return true
}
getCloudData(){
WxCloudApi.get().then(async (result) => {
if(result.result.code === 200) {
let data=result.result.data
console.log(`[SMC]: 获取游戏数据成功:`, result.result);
this.overrideLocalDataWithRemote(data)
return true
} else {
console.warn(`[SMC]: 游戏数据增加失败`);
return false
}
}).catch((error) => {
console.error(`[SMC]: 获取游戏数据异常:`, error);
});
}
public async overrideLocalDataWithRemote(CloudData) {
try {
// 直接覆盖基础游戏数据
if (CloudData.openid) {
this.openid=CloudData.openid
}
// 直接覆盖出战英雄配置
if (CloudData.data) {
if(CloudData.data.gold) this.vmdata.gold=CloudData.data.gold
if(CloudData.data.heros) this.heros=CloudData.data.heros
if(CloudData.data.fight_hero) this.fight_hero=CloudData.data.fight_hero
// 恢复收集记录
if(CloudData.data.collection) {
this.vmdata.collection = CloudData.data.collection;
}
}
} catch (error) {
console.error(`[SMC]: 数据覆盖失败:`, error);
}
}
getGameDate(){
return {
gold:this.vmdata.gold,
heros:this.heros,
fight_hero:this.fight_hero,
collection: this.vmdata.collection
}
}
addHero(hero_uuid:number){
if(this.heros.indexOf(hero_uuid)==-1){
this.heros.push(hero_uuid)
}
if(this.isWxClient()){
let res = this.updateCloudData()
if (res){
return true
}else{
// 同步不成功删除uuid
this.heros.splice(this.heros.indexOf(hero_uuid), 1);
oops.gui.toast("数据同步失败,已回滚操作");
return false
}
}
return true
}
// 设置单个出战英雄
updateFihgtHero(heroId: number) {
this.fight_hero = heroId;
if(this.isWxClient()){
let res = this.updateCloudData()
if (res){
return true
}else{
return false
}
}
return true
}
updateGold(gold:number, is_sync: boolean = true){
this.vmdata.gold += gold;
if(this.isWxClient() && is_sync){
let res = this.updateCloudData()
if (res){
oops.message.dispatchEvent(GameEvent.GOLD_UPDATE)
return true
}else{
this.vmdata.gold -= gold
return false
}
}
oops.message.dispatchEvent(GameEvent.GOLD_UPDATE)
return true
}
/**
* 更新主角英雄数据到 VM
* @param heroAttrs 英雄属性组件
*/
updateHeroInfo(heroAttrs: HeroAttrsComp) {
if (!heroAttrs || !heroAttrs.is_master) return;
const h = this.vmdata.hero;
// 基础信息
h.name = heroAttrs.hero_name;
h.type = heroAttrs.type;
// 防止 ECS 旧数据覆盖 VM 新数据 (如果 VM 里的等级更高,说明刚升级还没同步到 ECS)
if (heroAttrs.lv > h.lv) {
h.lv = heroAttrs.lv;
} else if (h.lv > heroAttrs.lv) {
// 此时应该反向同步?或者等待 CanUpdateLv 事件处理
// 这里暂时保持 VM 的高等级,不被 ECS 覆盖
} else {
h.lv = heroAttrs.lv;
}
// 动态属性
h.hp = Math.floor(heroAttrs.hp);
h.mp = Math.floor(heroAttrs.mp);
// 计算属性
h.hp_max = Math.floor(heroAttrs.Attrs[Attrs.HP_MAX] || 0);
h.mp_max = Math.floor(heroAttrs.Attrs[Attrs.MP_MAX] || 0);
h.def = Math.floor(heroAttrs.Attrs[Attrs.DEF] || 0);
h.ap = Math.floor(heroAttrs.Attrs[Attrs.AP] || 0);
h.speed = Math.floor(heroAttrs.Attrs[Attrs.SPEED] || 0);
h.crt = Math.floor(heroAttrs.Attrs[Attrs.CRITICAL] || 0);
h.as = Math.floor(heroAttrs.Attrs[Attrs.AS] || 0);
}
error(){
oops.gui.toast("数据处理异常,请重试或重新登录")
}
}
export var smc: SingletonModuleComp = ecs.getSingleton(SingletonModuleComp);