package player import ( "f1-game/internal/code" "f1-game/internal/constant" "f1-game/internal/data" "f1-game/internal/enum" "f1-game/internal/event" nameEvent "f1-game/internal/name/event" nameLocal "f1-game/internal/name/local" nameRemote "f1-game/internal/name/remote" "f1-game/internal/pb" "f1-game/internal/types" "f1-game/nodes/game/internal/db" dbEquip "f1-game/nodes/game/internal/db/data/equip" dbHero "f1-game/nodes/game/internal/db/data/hero" gameFacade "f1-game/nodes/game/internal/facade" "slices" "f1-game/nodes/game/player/asset" ao "f1-game/nodes/game/player/asset/origin" "f1-game/nodes/game/player/equip" "f1-game/nodes/game/player/hero" cfacade "github.com/cherry-game/cherry/facade" cproto "github.com/cherry-game/cherry/net/proto" ) func (p *actorPlayer) initHero() { p.Local().Register(nameLocal.Player_HeroStar, p.heroStar) p.Local().Register(nameLocal.Player_HeroSkillUp, p.heroSkillUp) p.Local().Register(nameLocal.Player_HeroWearEquip, p.heroWearEquip) p.Local().Register(nameLocal.Player_HeroTakeOffEquip, p.heroTakeOffEquip) p.EventRegister(nameEvent.Lord_LevelUp, p.heroLordLevelUp) } // heroStar 英雄升星 func (p *actorPlayer) heroStar(session *cproto.Session, req *pb.HeroStarRequest) { if len(req.RemoveHeroGUIDs) < 1 { p.ResponseCode(session, code.HeroStarNotEnough) return } heroTable := p.HeroTable() starHero, ok := heroTable.Heroes.Get(req.HeroGUID) if !ok { p.ResponseCode(session, code.HeroNotExist) return } // 检查英雄是否可以升星 if errCode := p.isHeroStarViable(starHero); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } var ( curStar = starHero.Star oldStar = curStar curStarExp = starHero.StarExp ) // 检查材料 for _, removeGUID := range req.RemoveHeroGUIDs { removeHero, ok := heroTable.Heroes.Get(removeGUID) if !ok { p.ResponseCode(session, code.HeroNotExist) return } // 判断英雄是否可以作为升星材料 if errCode := p.isHeroStarMatch(starHero, removeHero); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 计算星级经验 starRow, ok := data.HeroStar.GetByStar(removeHero.Star) if !ok { p.Warn("[HeroStar] config not exists, star=%d", removeHero.Star) p.ResponseCode(session, code.ConfigNotFound_HeroStar) return } curStarExp += starRow.OfferExp } // 检查是否可以升星 (最多循环最大星级的次数) for range data.Const.HeroMaxStar { starRow, ok := data.HeroStar.GetByStar(curStar) if !ok { p.Warn("[HeroStar] config not exists, star=%d", curStar) p.ResponseCode(session, code.ConfigNotFound_HeroStar) return } if curStarExp < starRow.UpgradeExp { break } curStar++ curStarExp -= starRow.UpgradeExp if curStar >= data.Const.HeroMaxStar { break } } // 地图队伍信息判断是否能删除英雄 playerTable := p.PlayerTable() if len(playerTable.Map.MapNodeID) > 0 { req := &pb.I64List{ List: req.RemoveHeroGUIDs, } errCode := p.mapPlayerCallWait(playerTable.Map.MapNodeID, nameRemote.MapPlayer_HeroDelete, req) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } // 移除材料英雄 hero.Service().RemoveHeros(p.playerID, req.RemoveHeroGUIDs...) starHero.StarExp = curStarExp if starHero.Star != curStar { starHero.Star = curStar //检测英雄技能激活 hero.Service().CheckActiveHeroSkill(starHero) // 刷新英雄属性 hero.Service().UpdateAttrs(p.playerID, starHero) // 更新英雄星级数量 for addStar := oldStar + 1; addStar <= starHero.Star; addStar++ { heroTable.HeroExtend.IncrStar(addStar) } // 发送英雄升星事件 p.PostEvent(event.NewHeroStar(p.playerID, oldStar, starHero.Star)) } heroTable.Save2Queue() p.Response(session, starHero.ToProto()) // 同步更新到redis hero.Service().SyncUpdateHeroToRedis(p.playerID, starHero) // 推送变更信息 hero.Service().ChangePush(p.playerID, req.RemoveHeroGUIDs, starHero) } // isHeroStarViable 判断是否可以升星 func (p *actorPlayer) isHeroStarViable(hero *dbHero.Hero) int32 { // 已经最大星级 if hero.Star >= data.Const.HeroMaxStar { return code.HeroStarLimit } // TODO 其它条件: 如果是否在队伍中, 是否锁定.... return code.OK } // isHeroStarMatch 判断升星材料是否匹配 func (p *actorPlayer) isHeroStarMatch(starHero, removeHero *dbHero.Hero) int32 { // 不能吃自己 if starHero.GUID == removeHero.GUID { return code.HeroStarMismatch } // 升星需要同样的英雄 if removeHero.HeroID != starHero.HeroID { return code.HeroStarMismatch } // TODO 其它条件: 如果是否在队伍中, 是否锁定.... // TODO 暂时屏蔽 后面callwait去map判断或者在redis中判断 // if removeHero.IsAlreadyTeamUp() { // return code.TeamHeroAlreadyUp // } return code.OK } func (*actorPlayer) heroLordLevelUp(eventData cfacade.IEventData) { e, ok := eventData.(*event.LordLevelUp) if !ok { return } var ( playerID = e.UniqueID() playerTable, _ = db.GetPlayerTable(playerID) heroTable = db.GetHeroTable(playerID) ) for _, hero := range heroTable.Heroes { // 更新英雄等级(冗余) hero.Level = playerTable.Lord.Level // 更新英雄属性 gameFacade.Hero.UpdateAttrs(playerID, hero) } heroTable.Save2Queue() // 同步更新到redis // TODO 全量更新优化 hero.Service().SyncUpdateHeroToRedis(playerID, heroTable.Heroes.GetAll()...) // TODO FIXME 英雄太多全部推送数据太大 // TODO // TODO // gameFacade.Hero.Push(playerID) } // heroSkillUp 英雄技能升级 func (p *actorPlayer) heroSkillUp(session *cproto.Session, req *pb.I64I32) { var ( heroTable = p.HeroTable() heroGUID = req.Key skillID = req.Value ) heroEntity, found := heroTable.Heroes.Get(heroGUID) if !found { p.ResponseCode(session, code.HeroNotExist) return } // 判断当前技能是否已解锁 skillLevel := heroEntity.GetSkillLevel(skillID) if skillLevel < 1 { p.ResponseCode(session, code.SkillNotUnlock) return } skillRow, found := data.Skill.GetByID(skillID) if !found { p.ResponseCode(session, code.SkillNotExists) return } // 判断是否达到最大等级 if skillLevel >= skillRow.MaxLv { p.ResponseCode(session, code.SkillLevelMax) return } // 获取当前技能等级配置 skillLevelRow, found := data.SkillLevel.GetBySkillIDLevel(skillID, skillLevel) if !found { p.ResponseCode(session, code.ConfigNotExist_SkillLevel) return } // 消耗为空表示升到了最高级 if skillLevelRow.UpgradeCost.IsEmpty() { p.ResponseCode(session, code.SkillUpCostError) return } // 判断下一级配置是否存在 nextSkillLevelRow, found := data.SkillLevel.GetBySkillIDLevel(skillID, skillLevel+1) if !found { p.ResponseCode(session, code.ConfigNotExist_SkillLevel) return } // 升级星级不足 if heroEntity.Star < nextSkillLevelRow.UnlockStar { p.ResponseCode(session, code.SkillLevelLimit) return } // 消耗资源 _, errCode := asset.Service().Subs(p.playerID, skillLevelRow.UpgradeCost, ao.HeroSkillLevelUpCost) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 升级 skillLevel++ heroEntity.SetSkillLevel(skillID, skillLevel) heroTable.Save2Queue() rsp := &pb.HeroSkillResponse{ HeroGUID: heroGUID, HeroSkillID: skillID, HeroSkillLevel: skillLevel, } p.Response(session, rsp) // 同步更新到redis hero.Service().SyncUpdateHeroToRedis(p.playerID, heroEntity) } // 英雄穿戴装备 func (p *actorPlayer) heroWearEquip(session *cproto.Session, req *pb.HeroEquip) { var ( heroTable = p.HeroTable() equipTable = p.EquipTable() heroGUID = req.HeroGUID // 英雄GUID equipGUIDList = req.EquipGUIDList // 装备GUID列表 ) if heroGUID < 1 || len(equipGUIDList) > constant.HeroEquipSlotMax || len(equipGUIDList) < constant.HeroEquipSlotMin { p.ResponseCode(session, code.IllegalArgument) return } // 穿戴装备的英雄非待命状态 errCode := p.checkHeroCanUpdateEquip(heroGUID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } weHero, found := heroTable.Heroes.Get(heroGUID) if !found { p.ResponseCode(session, code.HeroNotExist) return } var ( validEquipList []*dbEquip.Equip // 过滤可穿戴装备 replaceEquipList []*dbEquip.Equip // 需要先脱下的装备,包括当前英雄和被其他英雄穿戴的装备 equipTypeList []enum.EquipType // 可穿戴的装备类型 ) for _, equipGUID := range equipGUIDList { equip, found := equipTable.Equips.Get(equipGUID) // 装备不存在 if !found { continue } // 装备已穿戴当前英雄身上 if weHero.EquipGUIDList.Contains(equipGUID) { continue } equipType := equip.GetEquipType() // 装备类型重复 if slices.Contains(equipTypeList, equipType) { continue } // 记录需要脱掉装备在其他英雄身上的装备 if equip.WearHeroGUID > 0 { // 卸下装备的英雄非待命状态 errCode := p.checkHeroCanUpdateEquip(equip.WearHeroGUID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } replaceEquipList = append(replaceEquipList, equip) } validEquipList = append(validEquipList, equip) equipTypeList = append(equipTypeList, equipType) } // 没有可穿戴的装备 if len(validEquipList) < 1 { p.ResponseCode(session, code.OK) return } // 检查当前英雄身上需要被替换的装备 for _, equipGUID := range weHero.EquipGUIDList { equip, _ := equipTable.Equips.Get(equipGUID) // 需要替换的装备先脱下 if slices.Contains(equipTypeList, equip.GetEquipType()) { replaceEquipList = append(replaceEquipList, equip) } } // 先一键脱下装备 changeHeroList := p.doTakeOffEquipList(heroTable, replaceEquipList) // 变更的装备 var changeEquipList = types.NewList(replaceEquipList...) // 穿戴装备,双向绑定关系 for _, equip := range validEquipList { weHero.EquipGUIDList.Add(equip.GUID) equip.WearHeroGUID = heroGUID // 记录变更的装备列表 if !changeEquipList.Contains(equip) { changeEquipList.Add(equip) } } // 更新英雄属性 gameFacade.Hero.UpdateAttrs(p.playerID, weHero) heroTable.Save2Queue() equipTable.Save2Queue() // 当前英雄也加入推送列表 if !slices.Contains(changeHeroList, weHero) { changeHeroList = append(changeHeroList, weHero) } // 同步更新到redis hero.Service().SyncUpdateHeroToRedis(p.playerID, changeHeroList...) // 推送英雄变更 hero.Service().ChangePush(p.playerID, nil, changeHeroList...) // 推送装备变更 equip.Service().ChangePush(p.playerID, nil, changeEquipList...) p.ResponseCode(session, code.OK) // 发送英雄穿戴装备事件 p.PostEvent(event.NewHeroWearEquip(p.playerID)) } // 英雄脱下装备 func (p *actorPlayer) heroTakeOffEquip(session *cproto.Session, req *pb.I64I64) { var ( heroTable = p.HeroTable() equipTable = p.EquipTable() heroGUID = req.Key // 英雄GUID equipGUID = req.Value // 装备GUID ) if heroGUID < 1 { p.ResponseCode(session, code.IllegalArgument) } // 检测英雄是否是出战状态 errCode := p.checkHeroCanUpdateEquip(heroGUID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } toeHero, found := heroTable.Heroes.Get(heroGUID) if !found { p.ResponseCode(session, code.HeroNotExist) return } if toeHero.EquipGUIDList.IsEmpty() { p.ResponseCode(session, code.HeroEquipNotWear) return } var validEquipList []*dbEquip.Equip // 可脱下的装备 if equipGUID > 0 { equip, found := equipTable.Equips.Get(equipGUID) if !found { p.ResponseCode(session, code.EquipNotFound) return } // 装备是否穿戴在当前英雄身上 if !toeHero.EquipGUIDList.Contains(equipGUID) { p.ResponseCode(session, code.HeroNotWearThisEquip) return } validEquipList = append(validEquipList, equip) } else { // 传0 一键脱下所有穿戴的装备 for _, eguid := range toeHero.EquipGUIDList { equip, _ := equipTable.Equips.Get(eguid) validEquipList = append(validEquipList, equip) } } if len(validEquipList) < 1 { p.ResponseCode(session, code.HeroEquipNotWear) return } changeHeroList := p.doTakeOffEquipList(heroTable, validEquipList) heroTable.Save2Queue() equipTable.Save2Queue() // 同步更新到redis hero.Service().SyncUpdateHeroToRedis(p.playerID, changeHeroList...) // 推送英雄变更 hero.Service().ChangePush(p.playerID, nil, changeHeroList...) // 推送装备变更 equip.Service().ChangePush(p.playerID, nil, validEquipList...) p.ResponseCode(session, code.OK) } // 一键脱下装备,解除英雄与装备的穿戴关系。需要在上层保存 // // wearEquipList 需要脱下的装备列表,上层过滤没有穿戴的装备 func (p *actorPlayer) doTakeOffEquipList(heroTable *db.HeroTable, wearEquipList []*dbEquip.Equip) []*dbHero.Hero { var changeHeroList []*dbHero.Hero for _, equip := range wearEquipList { hero, _ := heroTable.Heroes.Get(equip.WearHeroGUID) /// 双向解除装备穿戴关系 hero.RemoveEquip(equip.GUID) equip.WearHeroGUID = 0 // 更新英雄属性 gameFacade.Hero.UpdateAttrs(p.playerID, hero) // 记录需要推送变更的英雄列表 changeHeroList = append(changeHeroList, hero) } return changeHeroList } // 检测英雄是否能穿戴装备 如果英雄处于出征状态是不能穿戴装备的 func (p *actorPlayer) checkHeroCanUpdateEquip(heroGUIDs ...int64) int32 { if len(heroGUIDs) < 1 { return code.HeroNotExist } playerTable := p.PlayerTable() // 没有地图节点 if len(playerTable.Map.MapNodeID) <= 0 { return code.OK } req := &pb.I64List{ List: heroGUIDs, } // map检测英雄是否出征 如果出征中不能穿戴装备 return p.mapPlayerCallWait(playerTable.Map.MapNodeID, nameRemote.MapPlayer_HeroCheckBusy, req) }