service.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. package hero
  2. import (
  3. capp "f1-game/internal/cherry_app"
  4. "f1-game/internal/code"
  5. "f1-game/internal/component/redis"
  6. "f1-game/internal/constant"
  7. "f1-game/internal/data"
  8. "f1-game/internal/enum"
  9. "f1-game/internal/event"
  10. "f1-game/internal/extend/times"
  11. facade "f1-game/internal/facade"
  12. nameRoute "f1-game/internal/name/route"
  13. "f1-game/internal/pb"
  14. "f1-game/internal/sessions"
  15. "f1-game/internal/types"
  16. "f1-game/nodes/game/internal/db"
  17. dbEquip "f1-game/nodes/game/internal/db/data/equip"
  18. dbHero "f1-game/nodes/game/internal/db/data/hero"
  19. gameFacade "f1-game/nodes/game/internal/facade"
  20. ao "f1-game/nodes/game/player/asset/origin"
  21. ctime "github.com/cherry-game/cherry/extend/time"
  22. cproto "github.com/cherry-game/cherry/net/proto"
  23. clog "github.com/cherry-game/cherry/logger"
  24. )
  25. var srv = &service{}
  26. func init() {
  27. }
  28. func Service() *service {
  29. return srv
  30. }
  31. type service struct {
  32. facade.PlayerServiceBase[*db.PlayerTable]
  33. }
  34. // func (*service) OnLogin(playerTable *db.PlayerTable, session *cproto.Session) {
  35. // }
  36. // func (*service) OnLogined(playerTable *db.PlayerTable, session *cproto.Session) {
  37. // }
  38. // func (*service) OnLogout(playerTable *db.PlayerTable) {
  39. // }
  40. func (*service) OnReset(playerTable *db.PlayerTable, _ *ctime.CherryTime, byLogin bool) {
  41. heroTable := db.GetHeroTable(playerTable.PlayerID)
  42. if playerTable.Stats.IsDayReset {
  43. heroTable.Recruit.FreeUsedNum = 0
  44. heroTable.Save2Queue()
  45. }
  46. }
  47. // Push 英雄数据推送
  48. func (*service) Push(playerTable *db.PlayerTable, session *cproto.Session) {
  49. heroTable := db.GetHeroTable(playerTable.PlayerID)
  50. //heroTable.CheckData()
  51. rsp := pb.HeroList{
  52. List: heroTable.Heroes.ToProto(),
  53. }
  54. sessions.PushWithSession(session, nameRoute.PushGamePlayer_HeroList, &rsp)
  55. clog.Debugf("[playerID = %d] heroesCount = %d", playerTable.PlayerID, len(rsp.List))
  56. //推送图鉴列表
  57. bookRsp := pb.I32List{}
  58. for _, heroID := range heroTable.Books {
  59. bookRsp.List = append(bookRsp.List, heroID)
  60. }
  61. sessions.PushWithSession(session, nameRoute.PushGamePlayer_HeroBookList, &bookRsp)
  62. clog.Debugf("[playerID = %d] heroBooksCount = %d",
  63. playerTable.PlayerID,
  64. len(bookRsp.List),
  65. )
  66. }
  67. func (p *service) ChangePushOne(playerID int64, hero *dbHero.Hero) {
  68. p.ChangePush(playerID, nil, hero)
  69. }
  70. // ChangePush 英雄变更推送
  71. func (p *service) ChangePush(playerID int64, removeHeroes []int64, updateHeroes ...*dbHero.Hero) {
  72. if len(updateHeroes) > 0 {
  73. rsp := &pb.HeroList{}
  74. for _, hero := range updateHeroes {
  75. rsp.List = append(rsp.List, hero.ToProto())
  76. }
  77. sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroChange, rsp)
  78. }
  79. if len(removeHeroes) > 0 {
  80. sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroDel, &pb.I64List{
  81. List: removeHeroes,
  82. })
  83. }
  84. }
  85. // HeroBookChangePush 英雄图鉴变更推送
  86. func (p *service) HeroBookChangePush(playerID int64, updateHeroBooks ...int32) {
  87. if len(updateHeroBooks) > 0 {
  88. rsp := &pb.I32List{
  89. List: updateHeroBooks,
  90. }
  91. sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroBookChange, rsp)
  92. }
  93. }
  94. // SetHerosInjuryByTeamID 设置英雄重伤状态
  95. func (p *service) SetHerosInjuryByTeamID(playerID int64, teamID int32, injuriesEndTime int64, push bool) {
  96. clog.Debugf("[SetHerosInjuryByTeamID] playerID = %d, teamID = %d, injuriesEndTime = %s",
  97. playerID, teamID, times.SecondToDatetimeFormat(injuriesEndTime))
  98. // TODO 暂时屏蔽
  99. // teamTable := db.GetLineupTable(playerID)
  100. // team, ok := teamTable.Teams.GetTeam(teamID)
  101. // if !ok {
  102. // clog.Errorf("[SetHerosInjuryByTeamID] team not exist: playerID = %d, teamID = %d", playerID, teamID)
  103. // return
  104. // }
  105. // heroTable := db.GetHeroTable(playerID)
  106. // resp := &pb.I64I64List{}
  107. // for _, member := range team.Members {
  108. // hero, ok := heroTable.Heroes.Get(member.HeroGUID)
  109. // if !ok {
  110. // continue
  111. // }
  112. // hero.InjuriesEndTime = injuriesEndTime
  113. // resp.List = append(resp.List, &pb.I64I64{Key: member.HeroGUID, Value: injuriesEndTime})
  114. // }
  115. // heroTable.Save2Queue()
  116. // if push {
  117. // sessions.PushPlayer(playerID, nameRoute.PushGamePlayer_HeroInjuries, resp)
  118. // }
  119. }
  120. // AddList 批量添加英雄(自动转碎片)
  121. func (p *service) AddList(playerID int64, cfgIDList []int32, origin ao.Origin, push bool) (types.Assets, int32) {
  122. var (
  123. playerTable, _ = db.GetPlayerTable(playerID)
  124. heroTable = db.GetHeroTable(playerID)
  125. retAssets types.Assets
  126. changeHeroes []*dbHero.Hero
  127. updateHeroBooks []int32
  128. )
  129. for _, cfgID := range cfgIDList {
  130. heroRow, found := data.Hero.GetByID(cfgID)
  131. if !found {
  132. continue
  133. }
  134. // 添加英雄
  135. changeHeroes, retAssets = p.NewAndAddHeroEntity(playerTable, heroTable, heroRow, changeHeroes, retAssets)
  136. if !heroTable.Books.Contains(cfgID) {
  137. heroTable.Books.Add(cfgID)
  138. updateHeroBooks = append(updateHeroBooks, cfgID)
  139. }
  140. }
  141. if len(changeHeroes) > 0 {
  142. // 保存英雄数据
  143. heroTable.Save2Queue()
  144. // 同步更新到redis
  145. p.SyncUpdateHeroToRedis(playerID, changeHeroes...)
  146. if push { // TODO: event dispatch
  147. // 推送英雄变更
  148. p.ChangePush(playerID, nil, changeHeroes...)
  149. }
  150. // 发送获得英雄事件
  151. capp.PostEvent(event.NewHeroGet(playerID, retAssets))
  152. }
  153. // 推送图鉴
  154. if len(updateHeroBooks) > 0 && push {
  155. gameFacade.Hero.HeroBookChangePush(playerID, updateHeroBooks...)
  156. }
  157. return retAssets, code.OK
  158. }
  159. func (p *service) AddOne(playerID int64, heroID int32, num int32, origin ao.Origin, push bool) (types.Assets, int32) {
  160. heroRow, found := data.Hero.GetByID(heroID)
  161. if !found {
  162. return nil, code.ConfigNotFound_Hero
  163. }
  164. var (
  165. playerTable, _ = db.GetPlayerTable(playerID)
  166. heroTable = db.GetHeroTable(playerID)
  167. retAssets types.Assets
  168. changeHeroes []*dbHero.Hero
  169. updateHeroBook bool
  170. )
  171. for range num {
  172. // 添加英雄
  173. changeHeroes, retAssets = p.NewAndAddHeroEntity(playerTable, heroTable, heroRow, changeHeroes, retAssets)
  174. }
  175. if !heroTable.Books.Contains(heroID) {
  176. heroTable.Books.Add(heroID)
  177. updateHeroBook = true
  178. }
  179. if len(changeHeroes) > 0 {
  180. // 保存英雄数据
  181. heroTable.Save2Queue()
  182. // 同步更新到redis
  183. p.SyncUpdateHeroToRedis(playerID, changeHeroes...)
  184. if push { // TODO: event dispatch
  185. // 推送英雄变更
  186. p.ChangePush(playerID, nil, changeHeroes...)
  187. }
  188. // 发送获得英雄事件
  189. capp.PostEvent(event.NewHeroGet(playerID, retAssets))
  190. }
  191. if updateHeroBook && push {
  192. gameFacade.Hero.HeroBookChangePush(playerID, heroID)
  193. }
  194. return retAssets, code.OK
  195. }
  196. // 创建并添加一个英雄
  197. func (p *service) NewAndAddHeroEntity(playerTable *db.PlayerTable, heroTable *db.HeroTable,
  198. heroRow *data.HeroRow, changeHeroes []*dbHero.Hero, retAssets types.Assets) ([]*dbHero.Hero, types.Assets) {
  199. // 添加英雄
  200. guid := redis.GUID.NewGUID()
  201. heroEntity := dbHero.NewHero(heroRow, guid, playerTable.Lord.Level)
  202. heroTable.Heroes.Add(heroEntity)
  203. // 检测初始化天赋、主动和被动技能
  204. p.CheckActiveHeroSkill(heroEntity)
  205. // 更新英雄属性
  206. p.UpdateAttrs(playerTable.PlayerID, heroEntity)
  207. changeHeroes = append(changeHeroes, heroEntity)
  208. retAssets.AddAsset(types.NewAssetWithGUID(heroEntity.HeroID, 1, heroEntity.GUID))
  209. // 更新获得英雄个数
  210. heroTable.HeroExtend.HeroCount += 1
  211. return changeHeroes, retAssets
  212. }
  213. // UpdateAttrs 更新英雄属性,不过滤不同兵种的加成,后面统一在战斗参数构建的地方处理
  214. func (*service) UpdateAttrs(playerID int64, heroEntity *dbHero.Hero) {
  215. attrs := types.Attrs{}
  216. for _, fn := range attrsFuncList {
  217. fn(playerID, heroEntity, &attrs)
  218. }
  219. // TODO 设置英雄当前血量为最大血量
  220. attrs.Set(enum.Attr_HP, attrs.Get(enum.Attr_MaxHP))
  221. heroEntity.Attrs = attrs
  222. }
  223. // GetChapterFighter 实时获取探险英雄战斗数据
  224. func (*service) GetChapterFighter(playerID int64, heroEntity *dbHero.Hero, lineupID int32) *pb.Fighter {
  225. attrs := types.Attrs{}
  226. for _, fn := range attrsFuncList {
  227. fn(playerID, heroEntity, &attrs)
  228. }
  229. // 单独处理阵型羁绊属性
  230. lineupFetterAttrs(playerID, heroEntity, &attrs, lineupID)
  231. // 过滤兵种加成
  232. retAttrs := types.Attrs{}
  233. for key, attrVal := range attrs {
  234. if enum.SquadKeys[key] {
  235. continue
  236. }
  237. retAttrs.Add(key, attrVal)
  238. }
  239. // TODO 设置英雄当前血量为最大血量
  240. retAttrs.Set(enum.Attr_HP, retAttrs.Get(enum.Attr_MaxHP))
  241. return heroEntity.ToFighter(retAttrs)
  242. }
  243. // AddExp 给英雄加经验
  244. func (p *service) AddExp(playerID int64, hero *dbHero.Hero, exp int32) int32 {
  245. // // 英雄等级已经最大
  246. // if hero.Level >= data.HeroLevel.MaxLevel() {
  247. // return code.OK
  248. // }
  249. // hero.Exp += exp
  250. // levelUp := false
  251. // // 处理升级
  252. // for hero.Level < data.HeroLevel.MaxLevel() {
  253. // levelRow, ok := data.HeroLevel.Get(hero.Level)
  254. // if !ok {
  255. // return code.ConfigNotExists
  256. // }
  257. // if hero.Exp < levelRow.Exp {
  258. // break
  259. // }
  260. // hero.Level++
  261. // hero.Exp -= levelRow.Exp
  262. // levelUp = true
  263. // }
  264. // if levelUp {
  265. // p.UpdateAttr(playerID, hero)
  266. // }
  267. // p.ChangePushOne(playerID, hero)
  268. return code.OK
  269. }
  270. // 英雄等级变化检测英雄技能激活
  271. func (p *service) CheckActiveHeroSkill(hero *dbHero.Hero) {
  272. if hero.Skills.Size() >= constant.HeroSkillNumLimit {
  273. return
  274. }
  275. heroRow, found := data.Hero.GetByID(hero.HeroID)
  276. if !found {
  277. clog.Warnf("[checkActiveHeroSkill] hero config not found: heroID=%d", hero.HeroID)
  278. return
  279. }
  280. // 天赋技能
  281. p.heroSkillInit(hero, heroRow.Talent, hero.Star)
  282. // 主动技能
  283. p.heroSkillInit(hero, heroRow.ActiveSkill, hero.Star)
  284. // 被动技能
  285. p.heroSkillInit(hero, heroRow.PassiveSkill, hero.Star)
  286. // 专属技能
  287. p.heroSkillInit(hero, heroRow.SpecialSkill, hero.Star)
  288. }
  289. // 初始化英雄技能
  290. func (*service) heroSkillInit(hero *dbHero.Hero, skillID, heroStar int32) {
  291. // 已经初始化 跳过
  292. if hero.GetSkillLevel(skillID) > 0 {
  293. return
  294. }
  295. skillRow, found := data.Skill.GetByID(skillID)
  296. if !found {
  297. clog.Warnf("[heroSkillInit] skill config not found, skillID: %d", skillID)
  298. return
  299. }
  300. if heroStar >= skillRow.UnlockStar {
  301. hero.SetSkillLevel(skillID, 1)
  302. }
  303. }
  304. // 删除英雄&处理
  305. func (p *service) RemoveHeros(playerID int64, removeHeroGUIDs ...int64) {
  306. if len(removeHeroGUIDs) <= 0 {
  307. return
  308. }
  309. var (
  310. heroTable = db.GetHeroTable(playerID)
  311. teamTable = db.GetLineupTable(playerID)
  312. equipTable = db.GetEquipTable(playerID)
  313. changeEquips = []*dbEquip.Equip{}
  314. equipSave = false
  315. )
  316. // 执行删除英雄前处理
  317. for _, heroGUID := range removeHeroGUIDs {
  318. hero, found := heroTable.Heroes.Get(heroGUID)
  319. if !found {
  320. continue
  321. }
  322. // 穿戴装备
  323. if len(hero.EquipGUIDList) > 0 {
  324. // 解除装备穿戴
  325. for _, equipGUID := range hero.EquipGUIDList {
  326. equip, found := equipTable.Equips.Get(equipGUID)
  327. if !found {
  328. continue
  329. }
  330. equip.WearHeroGUID = 0
  331. changeEquips = append(changeEquips, equip)
  332. equipSave = true
  333. }
  334. }
  335. }
  336. // 检测阵容数据
  337. updateLineups := teamTable.LineupMap.RemoveHeroClearLineup(removeHeroGUIDs...)
  338. if len(updateLineups) > 0 {
  339. teamTable.Save2Queue()
  340. // 通知客户端
  341. for lineupType, lineups := range updateLineups {
  342. gameFacade.Lineup.LineupChangePush(playerID, lineupType, lineups...)
  343. }
  344. }
  345. if equipSave {
  346. equipTable.Save2Queue()
  347. // 推送装备变更
  348. gameFacade.Equip.ChangePush(playerID, nil, changeEquips...)
  349. }
  350. // 删除英雄
  351. heroTable.Heroes.Remove(removeHeroGUIDs...)
  352. // 同步删除英雄到redis
  353. p.SyncRemoveHeroToRedis(playerID, removeHeroGUIDs...)
  354. }
  355. // 同步更新英雄到redis
  356. // TODO 现在根据玩家操作更新到redis,领主升级触发全部英雄同步需要优化
  357. func (p *service) SyncUpdateHeroToRedis(playerID int64, heroList ...*dbHero.Hero) {
  358. if len(heroList) <= 0 {
  359. return
  360. }
  361. var heroPlayerList []*pb.PlayerHero
  362. for _, hero := range heroList {
  363. heroPlayerList = append(heroPlayerList, hero.ToPlayerHero())
  364. }
  365. redis.Game.UpdatePlayerHeros(playerID, heroPlayerList...)
  366. }
  367. // 同步删除英雄到redis
  368. func (p *service) SyncRemoveHeroToRedis(playerID int64, heroIDList ...int64) {
  369. if len(heroIDList) <= 0 {
  370. return
  371. }
  372. redis.Game.DeletePlayerHeros(playerID, heroIDList...)
  373. }