boss_player.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455
  1. package model
  2. import (
  3. "container/heap"
  4. "math/rand"
  5. "rocommon/util"
  6. "roserver/baseserver/model"
  7. "roserver/baseserver/set"
  8. "roserver/serverproto"
  9. "sort"
  10. )
  11. ////////////////////////////////PlayerBoss
  12. type BossRoom struct {
  13. roomId int32
  14. uidList map[uint64]struct{}
  15. }
  16. func (this *BossRoom) AddUid(uid uint64) {
  17. this.uidList[uid] = struct{}{}
  18. }
  19. func (this *BossRoom) RemUid(uid uint64) {
  20. delete(this.uidList, uid)
  21. }
  22. type PlayerBoss struct {
  23. model.StateMachineCore
  24. mag *WorldBossManager
  25. bossUid uint64 //worldbosscfgid
  26. summonBossId int32 //bossCfgId
  27. summonBossType int32 //SummonBossType
  28. summonBossIdx int32 //召唤次数
  29. summonTime uint64 //召唤时间戳
  30. durationTime uint64 //boss持续时间s
  31. totalHp int32
  32. maxHp int32
  33. hpDirty bool
  34. SummonRewardList []*serverproto.KeyValueType //参与boss挑战奖励
  35. KillNormalRewardList, KillSpecialRewardList serverproto.KeyValueType //击杀参与奖 drop //击杀大奖 drop
  36. challengeList map[uint64]model.ClientID //当前正在挑战的玩家列表
  37. allChallengeList set.Interface //挑战玩家列表,包行当前正在挑战玩家
  38. challengeDataList map[uint64]*serverproto.FightRoleInfo //玩家数据
  39. maxPoint int32 //最大点数
  40. uidRoomList map[uint64]int32 //[uid,roomId]
  41. roomList map[int32]*BossRoom //[roomId,[uid...]]
  42. freeRoomList *model.MinHeap //[roomId]
  43. maxRoomId int32
  44. lastHPReduceTime uint64
  45. finish bool
  46. }
  47. func newPlayerBoss(mag *WorldBossManager) *PlayerBoss {
  48. playerBoss := &PlayerBoss{
  49. mag: mag,
  50. roomList: map[int32]*BossRoom{},
  51. uidRoomList: map[uint64]int32{},
  52. }
  53. playerBoss.challengeList = map[uint64]model.ClientID{}
  54. playerBoss.allChallengeList = set.New(set.NonThreadSafe)
  55. playerBoss.challengeDataList = map[uint64]*serverproto.FightRoleInfo{}
  56. //min heap
  57. playerBoss.freeRoomList = &model.MinHeap{}
  58. heap.Init(playerBoss.freeRoomList)
  59. return playerBoss
  60. }
  61. func (this *PlayerBoss) Init() {
  62. this.InitState()
  63. this.RegisterState(int32(BOSS_STATE_FIGHTING), bossStateFighting)
  64. this.RegisterState(int32(BOSS_STATE_DIED), bossStateDie)
  65. this.RegisterState(int32(BOSS_STATE_TIME_OUT), bossStateTimeout)
  66. }
  67. func (this *PlayerBoss) SetTotalHp(hp int32) {
  68. this.totalHp = hp
  69. this.maxHp = hp
  70. }
  71. func (this *PlayerBoss) clear() {
  72. for uid, _ := range this.challengeList {
  73. delete(this.mag.challengePlayerList, uid)
  74. delete(this.challengeList, uid)
  75. }
  76. this.allChallengeList.Clear()
  77. this.challengeDataList = map[uint64]*serverproto.FightRoleInfo{}
  78. this.maxRoomId = 0
  79. this.uidRoomList = map[uint64]int32{}
  80. this.roomList = map[int32]*BossRoom{}
  81. }
  82. //是否在挑战boss时间内
  83. func (this *PlayerBoss) checkTimeValid(nowTime uint64) bool {
  84. if this.summonTime > 0 {
  85. oldTime := this.summonTime + this.durationTime
  86. return oldTime > nowTime
  87. }
  88. return false
  89. }
  90. func (this *PlayerBoss) addChallengePlayer(uid uint64, id model.ClientID, roleData *serverproto.FightRoleInfo) {
  91. if !this.allChallengeList.Has(uid) {
  92. this.allChallengeList.Add(uid)
  93. WorldBossListAddChallenge(this, uid)
  94. }
  95. //self
  96. this.bossChangePlay(roleData)
  97. this.challengeList[uid] = id
  98. this.challengeDataList[uid] = roleData
  99. //manager
  100. this.mag.challengePlayerList[uid] = this
  101. //use for notify(enter/leave)
  102. var tmpRoomId int32 = 0
  103. if this.freeRoomList.Len() > 0 {
  104. tmpRoomId = heap.Pop(this.freeRoomList).(int32)
  105. } else {
  106. this.maxRoomId++
  107. tmpRoomId = this.maxRoomId
  108. }
  109. this.uidRoomList[uid] = tmpRoomId
  110. if this.roomList[tmpRoomId] == nil {
  111. this.roomList[tmpRoomId] = &BossRoom{
  112. uidList: map[uint64]struct{}{},
  113. }
  114. }
  115. this.roomList[tmpRoomId].AddUid(uid)
  116. if len(this.roomList[tmpRoomId].uidList) < MAX_BOSS_CHALLENGE_PLAYER_NUM {
  117. heap.Push(this.freeRoomList, tmpRoomId)
  118. }
  119. //通知自己和其他玩家
  120. this.enterNotify(uid, tmpRoomId)
  121. }
  122. //玩家挑战boss触发变身操作
  123. func (this *PlayerBoss) bossChangePlay(roleData *serverproto.FightRoleInfo) {
  124. switch this.summonBossType {
  125. case model.SummonBossType_ChangePlay:
  126. cfgData, ok := model.ConvertWorldBossChangePlayList[int32(this.bossUid)]
  127. if !ok {
  128. util.InfoF("AddBossFromRefresh WorldBossChangePlayCfgLoader not find boss=%v ", this.summonBossId)
  129. return
  130. }
  131. changePlayId := cfgData.RandChangePlayId()
  132. if changePlayId > 0 {
  133. roleData.ChangePlayId = changePlayId
  134. }
  135. }
  136. }
  137. func (this *PlayerBoss) canChallenge(uid uint64) serverproto.ErrorCode {
  138. if len(this.challengeList) >= 1000 {
  139. return serverproto.ErrorCode_ERROR_AOI_BOSS_CHALLENGE_NUM_LIMIT
  140. }
  141. if this.totalHp <= 0 {
  142. return serverproto.ErrorCode_ERROR_AOI_BOSS_NOT_FOUND
  143. }
  144. return serverproto.ErrorCode_ERROR_OK
  145. }
  146. //通知自己和其他玩家
  147. func (this *PlayerBoss) enterNotify(enterUid uint64, roomId int32) {
  148. //发送其他玩家信息给自己,并发送自己信息给其他玩家
  149. selfNtfMsg := &serverproto.SSPlayerEnterChallengeNtf{
  150. BossUid: this.bossUid,
  151. EnterUid: enterUid,
  152. SelfChangePlayId: this.challengeDataList[enterUid].ChangePlayId,
  153. SummonBossType: this.summonBossType,
  154. }
  155. otherNtfMsg := &serverproto.SSPlayerEnterChallengeNtf{
  156. BossUid: this.bossUid,
  157. EnterUid: enterUid,
  158. SummonBossType: this.summonBossType,
  159. }
  160. otherNtfMsg.FightList = append(otherNtfMsg.FightList, this.challengeDataList[enterUid])
  161. var otherSendList = map[string][]uint64{}
  162. roomInfo := this.roomList[roomId]
  163. for tmpUid, _ := range roomInfo.uidList {
  164. if tmpUid == enterUid {
  165. continue
  166. }
  167. tmpUidCli, ok := this.challengeList[tmpUid]
  168. if !ok {
  169. continue
  170. }
  171. selfNtfMsg.FightList = append(selfNtfMsg.FightList, this.challengeDataList[tmpUid])
  172. otherSendList[tmpUidCli.ServiceID] = append(otherSendList[tmpUidCli.ServiceID], tmpUid)
  173. }
  174. sort.Slice(selfNtfMsg.FightList, func(i, j int) bool {
  175. return selfNtfMsg.FightList[i].BriefInfo.Uid < selfNtfMsg.FightList[j].BriefInfo.Uid
  176. })
  177. //send self
  178. this.mag.SendGame(selfNtfMsg, this.challengeList[enterUid].ServiceID, 0)
  179. //send other
  180. for idx := range otherSendList {
  181. otherNtfMsg.NotifyList = otherNtfMsg.NotifyList[:0]
  182. otherNtfMsg.NotifyList = append(otherSendList[idx])
  183. this.mag.SendGame(otherNtfMsg, idx, 0)
  184. }
  185. }
  186. //离开boss,发送其他玩家信息给当前玩家
  187. func (this *PlayerBoss) leaveNotify(leaveUid uint64) {
  188. var notifyList = map[string][]uint64{}
  189. if leaveUidData, ok := this.challengeList[leaveUid]; ok {
  190. //需要做rand点处理
  191. _, ok := this.mag.challengePlayerList[leaveUid]
  192. if ok {
  193. //需要做rand点处理
  194. pointNtfMsg := &serverproto.SCPlayerWorldBossRandNtf{
  195. PointInfo: &serverproto.WorldBossRandPointInfo{
  196. BossSummonType: this.summonBossType,
  197. },
  198. }
  199. pointNtfMsg.PointInfo.BossId = int32(this.bossUid)
  200. pointNtfMsg.PointInfo.BossSummonIdx = this.summonBossIdx
  201. this.mag.SendGame(pointNtfMsg, leaveUidData.ServiceID, leaveUid)
  202. delete(this.mag.challengePlayerList, leaveUid)
  203. }
  204. delete(this.challengeList, leaveUid)
  205. if roomId, ok := this.uidRoomList[leaveUid]; ok {
  206. roomInfo := this.roomList[roomId]
  207. for tmpUid, _ := range roomInfo.uidList {
  208. if tmpUid == leaveUid {
  209. continue
  210. }
  211. if notifyData, ok := this.challengeList[tmpUid]; ok {
  212. notifyList[notifyData.ServiceID] = append(notifyList[notifyData.ServiceID], tmpUid)
  213. }
  214. }
  215. roomInfo.RemUid(leaveUid)
  216. bPushed := false
  217. for idx := 0; idx < this.freeRoomList.Len(); idx++ {
  218. if this.freeRoomList.ItemList[idx] == roomId {
  219. bPushed = true
  220. break
  221. }
  222. }
  223. if !bPushed {
  224. heap.Push(this.freeRoomList, roomId)
  225. }
  226. delete(this.uidRoomList, leaveUid)
  227. }
  228. }
  229. leaveNtfMsg := &serverproto.SSPlayerLeaveChallengeNtf{
  230. LeaveUid: leaveUid,
  231. }
  232. for serviceNode := range notifyList {
  233. leaveNtfMsg.NotifyList = leaveNtfMsg.NotifyList[:0]
  234. leaveNtfMsg.NotifyList = append(notifyList[serviceNode])
  235. this.mag.SendGame(leaveNtfMsg, serviceNode, 0)
  236. }
  237. }
  238. func (this *PlayerBoss) updateBossHp(ms uint64) {
  239. //boss没人打过,则不触发掉血
  240. if this.GetState() == BOSS_STATE_TIME_OUT {
  241. return
  242. }
  243. if this.totalHp >= this.maxHp || this.lastHPReduceTime == 0 {
  244. return
  245. }
  246. cfgData, ok := model.ConvertWorldBossList[int32(this.bossUid)]
  247. if !ok || cfgData.ReduceHp.Key <= 0 || cfgData.ReduceHp.Value <= 0 {
  248. return
  249. }
  250. if ms > this.lastHPReduceTime+uint64(cfgData.ReduceHp.Key)*1000 {
  251. reduceHp := (this.maxHp / 10000) * cfgData.ReduceHp.Value
  252. if reduceHp > 0 {
  253. this.hpDirty = true
  254. this.totalHp -= reduceHp
  255. if this.totalHp <= 0 {
  256. this.totalHp = 0
  257. util.InfoF("update world boss reduce hp bossid= %v hp= %v", this.bossUid, cfgData.ReduceHp.Value)
  258. this.SwitchState(int32(BOSS_STATE_DIED), nil)
  259. }
  260. this.lastHPReduceTime = ms
  261. }
  262. }
  263. }
  264. func (this *PlayerBoss) broadcastBossHp() {
  265. if len(this.challengeList) <= 0 {
  266. return
  267. }
  268. if !this.hpDirty {
  269. return
  270. }
  271. this.hpDirty = false
  272. //update to db
  273. UpdateWorldBossList(this)
  274. var notifyList = map[string][]uint64{}
  275. for uid, data := range this.challengeList {
  276. notifyList[data.ServiceID] = append(notifyList[data.ServiceID], uid)
  277. }
  278. hpNtfMsg := &serverproto.SSPlayerChallengeHpNtf{
  279. CurBossHp: this.totalHp,
  280. }
  281. sendCount := 50
  282. for serviceNode := range notifyList {
  283. uidLen := len(notifyList[serviceNode])
  284. if uidLen <= 0 {
  285. continue
  286. }
  287. idx := 0
  288. if uidLen > sendCount {
  289. idx = 0
  290. }
  291. for {
  292. if idx+sendCount < uidLen {
  293. hpNtfMsg.NotifyList = notifyList[serviceNode][idx : idx+sendCount]
  294. idx += sendCount
  295. } else {
  296. hpNtfMsg.NotifyList = notifyList[serviceNode][idx:uidLen]
  297. this.mag.SendGame(hpNtfMsg, serviceNode, 0)
  298. break
  299. }
  300. this.mag.SendGame(hpNtfMsg, serviceNode, 0)
  301. }
  302. }
  303. }
  304. func (this *PlayerBoss) broadcastResult(result int32) {
  305. if len(this.challengeList) <= 0 {
  306. return
  307. }
  308. var notifyList = map[string][]uint64{}
  309. for uid, data := range this.challengeList {
  310. notifyList[data.ServiceID] = append(notifyList[data.ServiceID], uid)
  311. }
  312. resultNtfMsg := &serverproto.SSPlayerChallengeResultNtf{
  313. Result: result,
  314. }
  315. for serviceNode := range notifyList {
  316. resultNtfMsg.NotifyList = resultNtfMsg.NotifyList[:0]
  317. resultNtfMsg.NotifyList = append(notifyList[serviceNode])
  318. this.mag.SendGame(resultNtfMsg, serviceNode, 0)
  319. }
  320. }
  321. func (this *PlayerBoss) battleTimeOut(fromDB bool) {
  322. util.InfoF("battleTimeOut bossid=%v", this.bossUid)
  323. if !fromDB {
  324. //非正常结束战斗
  325. this.broadcastResult(BOSS_RESULT_TIME_OUT)
  326. }
  327. this.finish = true
  328. //清理数据
  329. this.clear()
  330. }
  331. func (this *PlayerBoss) battleBossDie(fromDB bool) {
  332. util.InfoF("battleWin bossid=%v", this.bossUid)
  333. if !fromDB {
  334. this.broadcastResult(BOSS_RESULT_WIN)
  335. this.battleReward()
  336. }
  337. this.finish = true
  338. //清理数据
  339. this.clear()
  340. }
  341. func (this *PlayerBoss) battleReward() {
  342. if len(this.challengeList) > 0 {
  343. //需要做rand点处理
  344. pointNtfMsg := &serverproto.SCPlayerWorldBossRandNtf{
  345. PointInfo: &serverproto.WorldBossRandPointInfo{},
  346. }
  347. for uid, data := range this.challengeList {
  348. pointNtfMsg.PointInfo.BossId = int32(this.bossUid)
  349. this.mag.SendGame(pointNtfMsg, data.ServiceID, uid)
  350. }
  351. }
  352. ////参与boss奖励
  353. //// 发送邮件奖励
  354. //cfgData, ok := model.ConvertWorldBossList[int32(this.bossUid)]
  355. //if ok {
  356. // normalMailNtfMsg := &serverproto.SSAddMailNtf{
  357. // MailConfigId: BOSS_REWARD_MAIL_CONFIG_ID_Other,
  358. // MailType: int32(serverproto.MailType_MailType_Boss),
  359. // }
  360. // normalMailNtfMsg.RewardList = append(normalMailNtfMsg.RewardList, cfgData.SummonRewardList...)
  361. // normalMailNtfMsg.MailParamList = append(normalMailNtfMsg.MailParamList, cfgData.Id)
  362. //
  363. // for _, data := range this.allChallengeList.List() {
  364. // normalMailNtfMsg.NotifyList = append(normalMailNtfMsg.NotifyList, data.(uint64))
  365. // }
  366. //
  367. // this.mag.SendSocial(normalMailNtfMsg)
  368. //}
  369. }
  370. //boss战斗
  371. func (this *PlayerBoss) ProcessBattle(damageHp int32) {
  372. if this.totalHp > 0 && damageHp > 0 {
  373. this.hpDirty = true
  374. this.totalHp -= damageHp
  375. if this.totalHp <= 0 {
  376. this.totalHp = 0
  377. util.InfoF("ProcessBattle battle finish success")
  378. this.SwitchState(int32(BOSS_STATE_DIED), nil)
  379. }
  380. this.lastHPReduceTime = util.GetTimeMilliseconds()
  381. }
  382. }
  383. func (this *PlayerBoss) RandPoint(uid uint64) (serverproto.ErrorCode, bool, int32) {
  384. _, ok := this.challengeDataList[uid]
  385. if !ok {
  386. return serverproto.ErrorCode_ERROR_FAIL, false, 0
  387. }
  388. rand.Seed(int64(util.GetTimeMilliseconds()))
  389. randNum := rand.Int31n(100) + 1
  390. if randNum >= this.maxPoint {
  391. return serverproto.ErrorCode_ERROR_OK, true, randNum
  392. }
  393. return serverproto.ErrorCode_ERROR_OK, false, randNum
  394. }