package hero import ( capp "f1-game/internal/cherry_app" "f1-game/internal/code" "f1-game/internal/component/redis" "f1-game/internal/constant" "f1-game/internal/data" "f1-game/internal/enum" "f1-game/internal/event" "f1-game/internal/extend/times" facade "f1-game/internal/facade" nameRoute "f1-game/internal/name/route" "f1-game/internal/pb" "f1-game/internal/sessions" "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" ao "f1-game/nodes/game/player/asset/origin" ctime "github.com/cherry-game/cherry/extend/time" cproto "github.com/cherry-game/cherry/net/proto" clog "github.com/cherry-game/cherry/logger" ) var srv = &service{} func init() { } func Service() *service { return srv } type service struct { facade.PlayerServiceBase[*db.PlayerTable] } // func (*service) OnLogin(playerTable *db.PlayerTable, session *cproto.Session) { // } // func (*service) OnLogined(playerTable *db.PlayerTable, session *cproto.Session) { // } // func (*service) OnLogout(playerTable *db.PlayerTable) { // } func (*service) OnReset(playerTable *db.PlayerTable, _ *ctime.CherryTime, byLogin bool) { heroTable := db.GetHeroTable(playerTable.PlayerID) if playerTable.Stats.IsDayReset { heroTable.Recruit.FreeUsedNum = 0 heroTable.Save2Queue() } } // Push 英雄数据推送 func (*service) Push(playerTable *db.PlayerTable, session *cproto.Session) { heroTable := db.GetHeroTable(playerTable.PlayerID) //heroTable.CheckData() rsp := pb.HeroList{ List: heroTable.Heroes.ToProto(), } sessions.PushWithSession(session, nameRoute.PushGamePlayer_HeroList, &rsp) clog.Debugf("[playerID = %d] heroesCount = %d", playerTable.PlayerID, len(rsp.List)) //推送图鉴列表 bookRsp := pb.I32List{} for _, heroID := range heroTable.Books { bookRsp.List = append(bookRsp.List, heroID) } sessions.PushWithSession(session, nameRoute.PushGamePlayer_HeroBookList, &bookRsp) clog.Debugf("[playerID = %d] heroBooksCount = %d", playerTable.PlayerID, len(bookRsp.List), ) } func (p *service) ChangePushOne(playerID int64, hero *dbHero.Hero) { p.ChangePush(playerID, nil, hero) } // ChangePush 英雄变更推送 func (p *service) ChangePush(playerID int64, removeHeroes []int64, updateHeroes ...*dbHero.Hero) { if len(updateHeroes) > 0 { rsp := &pb.HeroList{} for _, hero := range updateHeroes { rsp.List = append(rsp.List, hero.ToProto()) } sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroChange, rsp) } if len(removeHeroes) > 0 { sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroDel, &pb.I64List{ List: removeHeroes, }) } } // HeroBookChangePush 英雄图鉴变更推送 func (p *service) HeroBookChangePush(playerID int64, updateHeroBooks ...int32) { if len(updateHeroBooks) > 0 { rsp := &pb.I32List{ List: updateHeroBooks, } sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroBookChange, rsp) } } // SetHerosInjuryByTeamID 设置英雄重伤状态 func (p *service) SetHerosInjuryByTeamID(playerID int64, teamID int32, injuriesEndTime int64, push bool) { clog.Debugf("[SetHerosInjuryByTeamID] playerID = %d, teamID = %d, injuriesEndTime = %s", playerID, teamID, times.SecondToDatetimeFormat(injuriesEndTime)) // TODO 暂时屏蔽 // teamTable := db.GetLineupTable(playerID) // team, ok := teamTable.Teams.GetTeam(teamID) // if !ok { // clog.Errorf("[SetHerosInjuryByTeamID] team not exist: playerID = %d, teamID = %d", playerID, teamID) // return // } // heroTable := db.GetHeroTable(playerID) // resp := &pb.I64I64List{} // for _, member := range team.Members { // hero, ok := heroTable.Heroes.Get(member.HeroGUID) // if !ok { // continue // } // hero.InjuriesEndTime = injuriesEndTime // resp.List = append(resp.List, &pb.I64I64{Key: member.HeroGUID, Value: injuriesEndTime}) // } // heroTable.Save2Queue() // if push { // sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroInjuries, resp) // } } // AddList 批量添加英雄(自动转碎片) func (p *service) AddList(playerID int64, cfgIDList []int32, origin ao.Origin, push bool) (types.Assets, int32) { var ( playerTable, _ = db.GetPlayerTable(playerID) heroTable = db.GetHeroTable(playerID) retAssets types.Assets changeHeroes []*dbHero.Hero updateHeroBooks []int32 ) for _, cfgID := range cfgIDList { heroRow, found := data.Hero.GetByID(cfgID) if !found { continue } // 添加英雄 changeHeroes, retAssets = p.NewAndAddHeroEntity(playerTable, heroTable, heroRow, changeHeroes, retAssets) if !heroTable.Books.Contains(cfgID) { heroTable.Books.Add(cfgID) updateHeroBooks = append(updateHeroBooks, cfgID) } } if len(changeHeroes) > 0 { // 保存英雄数据 heroTable.Save2Queue() // 同步更新到redis p.SyncUpdateHeroToRedis(playerID, changeHeroes...) if push { // TODO: event dispatch // 推送英雄变更 p.ChangePush(playerID, nil, changeHeroes...) } // 发送获得英雄事件 capp.PostEvent(event.NewHeroGet(playerID, retAssets)) } // 推送图鉴 if len(updateHeroBooks) > 0 && push { gameFacade.Hero.HeroBookChangePush(playerID, updateHeroBooks...) } return retAssets, code.OK } func (p *service) AddOne(playerID int64, heroID int32, num int32, origin ao.Origin, push bool) (types.Assets, int32) { heroRow, found := data.Hero.GetByID(heroID) if !found { return nil, code.ConfigNotFound_Hero } var ( playerTable, _ = db.GetPlayerTable(playerID) heroTable = db.GetHeroTable(playerID) retAssets types.Assets changeHeroes []*dbHero.Hero updateHeroBook bool ) for range num { // 添加英雄 changeHeroes, retAssets = p.NewAndAddHeroEntity(playerTable, heroTable, heroRow, changeHeroes, retAssets) } if !heroTable.Books.Contains(heroID) { heroTable.Books.Add(heroID) updateHeroBook = true } if len(changeHeroes) > 0 { // 保存英雄数据 heroTable.Save2Queue() // 同步更新到redis p.SyncUpdateHeroToRedis(playerID, changeHeroes...) if push { // TODO: event dispatch // 推送英雄变更 p.ChangePush(playerID, nil, changeHeroes...) } // 发送获得英雄事件 capp.PostEvent(event.NewHeroGet(playerID, retAssets)) } if updateHeroBook && push { gameFacade.Hero.HeroBookChangePush(playerID, heroID) } return retAssets, code.OK } // 创建并添加一个英雄 func (p *service) NewAndAddHeroEntity(playerTable *db.PlayerTable, heroTable *db.HeroTable, heroRow *data.HeroRow, changeHeroes []*dbHero.Hero, retAssets types.Assets) ([]*dbHero.Hero, types.Assets) { // 添加英雄 guid := redis.GUID.NewGUID() heroEntity := dbHero.NewHero(heroRow, guid, playerTable.Lord.Level) heroTable.Heroes.Add(heroEntity) // 检测初始化天赋、主动和被动技能 p.CheckActiveHeroSkill(heroEntity) // 更新英雄属性 p.UpdateAttrs(playerTable.PlayerID, heroEntity) changeHeroes = append(changeHeroes, heroEntity) retAssets.AddAsset(types.NewAssetWithGUID(heroEntity.HeroID, 1, heroEntity.GUID)) // 更新获得英雄个数 heroTable.HeroExtend.HeroCount += 1 return changeHeroes, retAssets } // UpdateAttrs 更新英雄属性,不过滤不同兵种的加成,后面统一在战斗参数构建的地方处理 func (*service) UpdateAttrs(playerID int64, heroEntity *dbHero.Hero) { attrs := types.Attrs{} for _, fn := range attrsFuncList { fn(playerID, heroEntity, &attrs) } // TODO 设置英雄当前血量为最大血量 attrs.Set(enum.Attr_HP, attrs.Get(enum.Attr_MaxHP)) heroEntity.Attrs = attrs } // GetChapterFighter 实时获取探险英雄战斗数据 func (*service) GetChapterFighter(playerID int64, heroEntity *dbHero.Hero, lineupID int32) *pb.Fighter { attrs := types.Attrs{} for _, fn := range attrsFuncList { fn(playerID, heroEntity, &attrs) } // 单独处理阵型羁绊属性 lineupFetterAttrs(playerID, heroEntity, &attrs, lineupID) // 过滤兵种加成 retAttrs := types.Attrs{} for key, attrVal := range attrs { if enum.SquadKeys[key] { continue } retAttrs.Add(key, attrVal) } // TODO 设置英雄当前血量为最大血量 retAttrs.Set(enum.Attr_HP, retAttrs.Get(enum.Attr_MaxHP)) return heroEntity.ToFighter(retAttrs) } // AddExp 给英雄加经验 func (p *service) AddExp(playerID int64, hero *dbHero.Hero, exp int32) int32 { // // 英雄等级已经最大 // if hero.Level >= data.HeroLevel.MaxLevel() { // return code.OK // } // hero.Exp += exp // levelUp := false // // 处理升级 // for hero.Level < data.HeroLevel.MaxLevel() { // levelRow, ok := data.HeroLevel.Get(hero.Level) // if !ok { // return code.ConfigNotExists // } // if hero.Exp < levelRow.Exp { // break // } // hero.Level++ // hero.Exp -= levelRow.Exp // levelUp = true // } // if levelUp { // p.UpdateAttr(playerID, hero) // } // p.ChangePushOne(playerID, hero) return code.OK } // 英雄等级变化检测英雄技能激活 func (p *service) CheckActiveHeroSkill(hero *dbHero.Hero) { if hero.Skills.Size() >= constant.HeroSkillNumLimit { return } heroRow, found := data.Hero.GetByID(hero.HeroID) if !found { clog.Warnf("[checkActiveHeroSkill] hero config not found: heroID=%d", hero.HeroID) return } // 天赋技能 p.heroSkillInit(hero, heroRow.Talent, hero.Star) // 主动技能 p.heroSkillInit(hero, heroRow.ActiveSkill, hero.Star) // 被动技能 p.heroSkillInit(hero, heroRow.PassiveSkill, hero.Star) // 专属技能 p.heroSkillInit(hero, heroRow.SpecialSkill, hero.Star) } // 初始化英雄技能 func (*service) heroSkillInit(hero *dbHero.Hero, skillID, heroStar int32) { // 已经初始化 跳过 if hero.GetSkillLevel(skillID) > 0 { return } skillRow, found := data.Skill.GetByID(skillID) if !found { clog.Warnf("[heroSkillInit] skill config not found, skillID: %d", skillID) return } if heroStar >= skillRow.UnlockStar { hero.SetSkillLevel(skillID, 1) } } // 删除英雄&处理 func (p *service) RemoveHeros(playerID int64, removeHeroGUIDs ...int64) { if len(removeHeroGUIDs) <= 0 { return } var ( heroTable = db.GetHeroTable(playerID) teamTable = db.GetLineupTable(playerID) equipTable = db.GetEquipTable(playerID) changeEquips = []*dbEquip.Equip{} equipSave = false ) // 执行删除英雄前处理 for _, heroGUID := range removeHeroGUIDs { hero, found := heroTable.Heroes.Get(heroGUID) if !found { continue } // 穿戴装备 if len(hero.EquipGUIDList) > 0 { // 解除装备穿戴 for _, equipGUID := range hero.EquipGUIDList { equip, found := equipTable.Equips.Get(equipGUID) if !found { continue } equip.WearHeroGUID = 0 changeEquips = append(changeEquips, equip) equipSave = true } } } // 检测阵容数据 updateLineups := teamTable.LineupMap.RemoveHeroClearLineup(removeHeroGUIDs...) if len(updateLineups) > 0 { teamTable.Save2Queue() // 通知客户端 for lineupType, lineups := range updateLineups { gameFacade.Lineup.LineupChangePush(playerID, lineupType, lineups...) } } if equipSave { equipTable.Save2Queue() // 推送装备变更 gameFacade.Equip.ChangePush(playerID, nil, changeEquips...) } // 删除英雄 heroTable.Heroes.Remove(removeHeroGUIDs...) // 同步删除英雄到redis p.SyncRemoveHeroToRedis(playerID, removeHeroGUIDs...) } // 同步更新英雄到redis // TODO 现在根据玩家操作更新到redis,领主升级触发全部英雄同步需要优化 func (p *service) SyncUpdateHeroToRedis(playerID int64, heroList ...*dbHero.Hero) { if len(heroList) <= 0 { return } var heroPlayerList []*pb.PlayerHero for _, hero := range heroList { heroPlayerList = append(heroPlayerList, hero.ToPlayerHero()) } redis.Game.UpdatePlayerHeros(playerID, heroPlayerList...) } // 同步删除英雄到redis func (p *service) SyncRemoveHeroToRedis(playerID int64, heroIDList ...int64) { if len(heroIDList) <= 0 { return } redis.Game.DeletePlayerHeros(playerID, heroIDList...) }