player_chapter.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. package player
  2. import (
  3. "f1-game/internal/battle"
  4. capp "f1-game/internal/cherry_app"
  5. "f1-game/internal/code"
  6. "f1-game/internal/constant"
  7. "f1-game/internal/data"
  8. "f1-game/internal/enum"
  9. "f1-game/internal/event"
  10. nameLocal "f1-game/internal/name/local"
  11. "f1-game/internal/pb"
  12. "f1-game/internal/types"
  13. "f1-game/nodes/game/player/asset"
  14. ao "f1-game/nodes/game/player/asset/origin"
  15. "f1-game/nodes/game/player/chapter"
  16. "fmt"
  17. "time"
  18. ctime "github.com/cherry-game/cherry/extend/time"
  19. cproto "github.com/cherry-game/cherry/net/proto"
  20. )
  21. func (p *actorPlayer) initChapter() {
  22. p.Local().Register(nameLocal.Player_ChapterBattle, p.chapterBattle)
  23. p.Local().Register(nameLocal.Player_ChapterVerify, p.chapterVerify)
  24. p.Local().Register(nameLocal.Player_ChapterLevelSweep, p.chapterLevelSweep)
  25. p.Local().Register(nameLocal.Player_ChapterStarReward, p.chapterReward)
  26. p.Local().Register(nameLocal.Player_ChapterLevelTimesBuy, p.chapterLevelTimesBuy)
  27. }
  28. // 发起探险战斗
  29. func (p *actorPlayer) chapterBattle(session *cproto.Session, req *pb.I32) {
  30. var (
  31. levelID = req.Value
  32. lineupID = constant.Adventure_Type // 阵容ID默认填1,如果后面扩展了再由前端传入
  33. )
  34. errCode := p.CheckChapterEnter(levelID)
  35. if code.IsFail(errCode) {
  36. p.ResponseCode(session, errCode)
  37. return
  38. }
  39. playerBattleParam, errCode := chapter.Service().GetChapterBattleCampParam(p.playerID, lineupID)
  40. if code.IsFail(errCode) {
  41. p.ResponseCode(session, errCode)
  42. return
  43. }
  44. chapterLevelRow, found := data.ChapterLevel.GetByID(levelID)
  45. if !found {
  46. p.ResponseCode(session, code.ConfigNotFound_ChapterLevel)
  47. return
  48. }
  49. var (
  50. chapterTable = p.ChapterTable()
  51. monsterGroupID = chapterLevelRow.MonsterGroupID
  52. battleSceneID = chapterLevelRow.BattleSceneID
  53. )
  54. monsterBattleParam := data.BuildBattleCampParam(monsterGroupID, enum.BattleCampType_Right)
  55. if len(monsterBattleParam.Squads) < 1 {
  56. p.ResponseCode(session, code.ChapterMonsterGroupConfigError)
  57. return
  58. }
  59. // 缓存关卡ID和随机种子
  60. battleRandomSeed := time.Now().UnixNano()
  61. battleParam := &pb.BattleParam{
  62. BattleType: enum.BattleType_Chapter,
  63. RandomSeed: uint64(battleRandomSeed),
  64. MapID: battleSceneID,
  65. Camps: []*pb.BattleCampParam{
  66. playerBattleParam,
  67. monsterBattleParam,
  68. },
  69. }
  70. // 发起战斗就直接去验证服获取校验码
  71. recordID := fmt.Sprintf("pve_%d_%d_%s", p.playerID, levelID, ctime.Now().ToShortDateTimeFormat())
  72. validation := battle.Validation(recordID, battleParam)
  73. if validation == nil {
  74. p.ResponseCode(session, code.ChapterBattleError)
  75. return
  76. }
  77. chapterTable.Chapter.SetBattleData(levelID, validation.Checksum)
  78. chapterTable.Save2Queue()
  79. p.Response(session, battleParam)
  80. }
  81. // 探险战斗验证
  82. func (p *actorPlayer) chapterVerify(session *cproto.Session, req *pb.BattleResult) {
  83. var (
  84. chapterTable = p.ChapterTable()
  85. )
  86. // 没有挑战过,返回失败
  87. if chapterTable.Chapter.BattleLevelID == 0 || chapterTable.Chapter.Checksum == 0 {
  88. p.ResponseCode(session, code.IllegalArgument)
  89. return
  90. }
  91. if capp.IsBattleValidation() && chapterTable.Chapter.Checksum != req.Checksum {
  92. p.Debug("chapterVerify checksum not match. req.Checksum = %d server.Checksum = %d", req.Checksum, chapterTable.Chapter.Checksum)
  93. p.ResponseCode(session, code.ChapterBattleResultVerifyFail)
  94. return
  95. }
  96. var (
  97. isWin = req.VictoriousCamp == enum.BattleCampType_Left
  98. rsp = &pb.ChapterVerifyResponse{
  99. Result: isWin,
  100. }
  101. )
  102. if isWin {
  103. challengeLevel, errCode := p.ChapterBattleResult(session, req)
  104. if code.IsFail(errCode) {
  105. p.ResponseCode(session, errCode)
  106. return
  107. }
  108. rsp.Challenge = challengeLevel
  109. }
  110. p.Response(session, rsp)
  111. }
  112. // 关卡扫荡
  113. func (p *actorPlayer) chapterLevelSweep(session *cproto.Session, req *pb.I32I32) {
  114. var (
  115. chapterTable = p.ChapterTable()
  116. levelID = req.Key
  117. sweepNum = req.Value
  118. )
  119. chapterLevelRow, found := data.ChapterLevel.GetByID(levelID)
  120. if !found {
  121. p.ResponseCode(session, code.ConfigNotFound_ChapterLevel)
  122. return
  123. }
  124. // 判断关卡是否解锁
  125. chapterLevel, found := chapterTable.Chapter.GetLevel(levelID)
  126. if !found {
  127. p.ResponseCode(session, code.ChapterLevelNotUnlocked)
  128. return
  129. }
  130. // 判断是否满星通关
  131. if chapterLevel.IsNotFullStar(len(chapterLevelRow.Conditions)) {
  132. p.ResponseCode(session, code.ChapterLevelNotFullStar)
  133. return
  134. }
  135. // 总挑战次数
  136. totalChallengeCount := chapterLevel.DayBuyChallengeCount + chapterLevelRow.DayChallengeCount
  137. // 判断条件次数是否满足
  138. if !chapterLevel.CheckChallengeCount(sweepNum, totalChallengeCount) {
  139. p.ResponseCode(session, code.ChapterLevelCountNotEnough)
  140. return
  141. }
  142. var (
  143. curStaminaValue = asset.Service().Get(p.playerID, data.Const.ChapterStaminaAddValue.ID)
  144. beforeIsFull = curStaminaValue >= data.Const.ChapterStaminaLimit
  145. )
  146. costs := chapterLevelRow.GetChapterLevelCost(sweepNum)
  147. // 扣除精力
  148. _, errCode := asset.Service().Subs(p.playerID, costs, ao.ChapterLevelSweepCost)
  149. if code.IsFail(errCode) {
  150. p.ResponseCode(session, code.ChapterStaminaNotEnough)
  151. return
  152. }
  153. // 原来是满精力 判断扣除精力后精力是否还满
  154. if beforeIsFull {
  155. curStaminaValue = asset.Service().Get(p.playerID, data.Const.ChapterStaminaAddValue.ID)
  156. // 体力不满 更新上次精力的恢复时间
  157. if curStaminaValue < data.Const.ChapterStaminaLimit {
  158. chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond()
  159. }
  160. }
  161. var (
  162. resp = &pb.ChapterLevelSweepReward{}
  163. rewards = types.Assets{}
  164. )
  165. // 获取扫荡奖励
  166. for num := int32(0); num < sweepNum; num++ {
  167. // 扫荡一次随机奖励
  168. singleRewards := chapterLevelRow.RandomChapterLevelReward()
  169. rewards.AddAssets(singleRewards)
  170. proto := singleRewards.ToAssetListProto()
  171. resp.Rewards = append(resp.Rewards, &proto)
  172. }
  173. _, errCode = asset.Service().Adds(p.playerID, rewards, ao.ChapterLevelSweepReward, true)
  174. if code.IsFail(errCode) {
  175. p.ResponseCode(session, errCode)
  176. return
  177. }
  178. chapterLevel.DayChallengeCount += sweepNum
  179. chapterTable.Chapter.FinishChallengeCount += sweepNum
  180. chapterTable.Chapter.TotalSweepCount += sweepNum
  181. chapterTable.Save2Queue()
  182. // 发送探险次数事件
  183. p.PostEvent(event.NewChapterChallenge(p.playerID, levelID, sweepNum, true))
  184. // 关卡信息更新 通知客户端
  185. chapter.Service().ChangePush(p.playerID, levelID)
  186. // 返回扫荡奖励
  187. p.Response(session, resp)
  188. }
  189. // 领取累计星级奖励
  190. func (p *actorPlayer) chapterReward(session *cproto.Session, req *pb.I32) {
  191. var (
  192. chapterTable = p.ChapterTable()
  193. chapterID = req.Value
  194. )
  195. chapterRow, found := data.Chapter.GetByID(chapterID)
  196. if !found {
  197. p.ResponseCode(session, code.ConfigNotFound_Chapter)
  198. return
  199. }
  200. // 判断章节是否解锁
  201. if !chapterTable.Chapter.IsChapterUnlocked(chapterID) {
  202. p.ResponseCode(session, code.ChapterNotUnlocked)
  203. return
  204. }
  205. chapterRewardRows, found := data.ChapterReward.ListByID(chapterRow.RewardPoolID)
  206. if !found {
  207. p.ResponseCode(session, code.ConfigNotFound_ChapterReward)
  208. return
  209. }
  210. var (
  211. // 当前星级
  212. curStars = chapterTable.Chapter.GetChapterStar(chapterRow.StartID, chapterRow.EndID)
  213. rewards = types.Assets{}
  214. stars = []int32{}
  215. )
  216. for _, chapterRewardRow := range chapterRewardRows {
  217. star := chapterRewardRow.StarNum
  218. // 判断章节星级奖励是否已领取 已领取跳过
  219. if chapterTable.Chapter.IsRewardReceived(chapterID, star) {
  220. continue
  221. }
  222. // 判断星数不足,直接退出
  223. if curStars < star {
  224. break
  225. }
  226. rewards.AddAssets(chapterRewardRow.Reward)
  227. stars = append(stars, star)
  228. }
  229. // 暂无可领取奖励
  230. if rewards.IsEmpty() {
  231. p.ResponseCode(session, code.ChapterRewardIsEmpty)
  232. return
  233. }
  234. // 领取奖励
  235. _, errCode := asset.Service().Adds(p.playerID, rewards, ao.ChapterStarRewardGet, true)
  236. if code.IsFail(errCode) {
  237. p.ResponseCode(session, errCode)
  238. return
  239. }
  240. // 更新领取信息
  241. chapterReward := chapterTable.Chapter.ReceiveChapterReward(chapterID, stars...)
  242. chapterTable.Save2Queue()
  243. resp := chapterReward.ToChapterRewardProto()
  244. p.Response(session, resp)
  245. }
  246. // 探险关卡挑战次数购买
  247. func (p *actorPlayer) chapterLevelTimesBuy(session *cproto.Session, req *pb.I32I32) {
  248. var (
  249. chapterTable = p.ChapterTable()
  250. levelID = req.Key
  251. buyTimes = req.Value
  252. )
  253. if buyTimes <= 0 {
  254. p.ResponseCode(session, code.ChapterLevelBuyTimesError)
  255. return
  256. }
  257. chapterLevelRow, found := data.ChapterLevel.GetByID(levelID)
  258. if !found {
  259. p.ResponseCode(session, code.ConfigNotFound_ChapterLevel)
  260. return
  261. }
  262. level, found := chapterTable.Chapter.GetLevel(levelID)
  263. if !found {
  264. p.ResponseCode(session, code.ChapterLevelNotUnlocked)
  265. return
  266. }
  267. // 超出每日购买次数上限
  268. if level.DayBuyChallengeCount+buyTimes > chapterLevelRow.DayCanBuyTimes {
  269. p.ResponseCode(session, code.ChapterLevelBuyTimesNotEnough)
  270. return
  271. }
  272. cost := data.Const.ChapterTimesBuyCost
  273. _, errCode := asset.Service().Sub(p.playerID, cost.ID, cost.Num*int64(buyTimes), ao.ChapterLevelTimesBuyCost)
  274. if code.IsFail(errCode) {
  275. p.ResponseCode(session, errCode)
  276. return
  277. }
  278. // 更新关卡挑战购买次数
  279. level.DayBuyChallengeCount += buyTimes
  280. chapterTable.Save2Queue()
  281. // 更新关卡信息 通知客户端
  282. chapter.Service().ChangePush(p.playerID, levelID)
  283. p.ResponseCode(session, code.OK)
  284. }
  285. // 进入探险的检查
  286. func (p *actorPlayer) CheckChapterEnter(levelID int32) int32 {
  287. chapterLevelRow, found := data.ChapterLevel.GetByID(levelID)
  288. if !found {
  289. return code.ConfigNotFound_ChapterLevel
  290. }
  291. // 章节ID
  292. chapterID := chapterLevelRow.ChapterID
  293. chapterRow, found := data.Chapter.GetByID(chapterID)
  294. if !found {
  295. return code.ConfigNotFound_Chapter
  296. }
  297. // 验证当前章节是否有这个关卡
  298. if !chapterRow.VaildateLevel(levelID) {
  299. return code.ChapterLevelError
  300. }
  301. chapterTable := p.ChapterTable()
  302. // 章节是否已解锁
  303. if !chapterTable.Chapter.IsChapterUnlocked(chapterID) {
  304. return code.ChapterNotUnlocked
  305. }
  306. // 关卡是否已解锁
  307. if !chapterTable.Chapter.IsLevelUnlocked(levelID) {
  308. return code.ChapterLevelNotUnlocked
  309. }
  310. // 总挑战次数
  311. totalChallengeCount := chapterLevelRow.DayChallengeCount
  312. chapterLevel, found := chapterTable.Chapter.GetLevel(levelID)
  313. if found {
  314. totalChallengeCount += chapterLevel.DayBuyChallengeCount
  315. }
  316. // 判断是否存在进入次数 首次挑战也会扣除次数
  317. if !chapterTable.Chapter.CheckLevelChallengeCount(levelID, totalChallengeCount) {
  318. return code.ChapterLevelCountNotEnough
  319. }
  320. // 检测精力是否足够
  321. errCode := asset.Service().IsAllEnough(p.playerID, chapterLevelRow.CostStamina)
  322. if code.IsFail(errCode) {
  323. return errCode
  324. }
  325. return code.OK
  326. }
  327. // 关卡挑战结果
  328. func (p *actorPlayer) ChapterBattleResult(session *cproto.Session, result *pb.BattleResult) (*pb.ChapterLevelChallenge, int32) {
  329. var (
  330. chapterTable = p.ChapterTable()
  331. levelID = chapterTable.Chapter.BattleLevelID
  332. )
  333. chapterLevelRow, found := data.ChapterLevel.GetByID(levelID)
  334. if !found {
  335. return nil, code.ConfigNotFound_ChapterLevel
  336. }
  337. chapterRow, found := data.Chapter.GetByID(chapterLevelRow.ChapterID)
  338. if !found {
  339. return nil, code.ConfigNotFound_Chapter
  340. }
  341. var (
  342. curStaminaValue = asset.Service().Get(p.playerID, data.Const.ChapterStaminaAddValue.ID)
  343. beforeIsFull = curStaminaValue >= data.Const.ChapterStaminaLimit
  344. )
  345. // 扣除精力
  346. _, errCode := asset.Service().Subs(p.playerID, chapterLevelRow.CostStamina, ao.ChapterLevelChallengeCost)
  347. if code.IsFail(errCode) {
  348. return nil, errCode
  349. }
  350. // 原来是满体力 判断扣除精力后精力是否还满
  351. if beforeIsFull {
  352. curStaminaValue = asset.Service().Get(p.playerID, data.Const.ChapterStaminaAddValue.ID)
  353. // 体力不满 更新上次精力的恢复时间
  354. if curStaminaValue < data.Const.ChapterStaminaLimit {
  355. chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond()
  356. }
  357. }
  358. stars := chapter.Service().GetChapterStars(p.playerID, chapterLevelRow, result)
  359. // 更新关卡数据
  360. firstWin := chapterTable.Chapter.BattleEndHandle(levelID, stars)
  361. var (
  362. rewards types.Assets
  363. origin ao.Origin
  364. )
  365. // 首胜奖励
  366. if firstWin {
  367. rewards = chapterLevelRow.GetFirstRewards()
  368. origin = ao.ChapterLevelFirstReward
  369. } else {
  370. rewards = chapterLevelRow.RandomChapterLevelReward()
  371. origin = ao.ChapterLevelChallengeReward
  372. }
  373. // 发送首胜/挑战奖励
  374. _, errCode = asset.Service().Adds(p.playerID, rewards, origin, true)
  375. if code.IsFail(errCode) {
  376. p.Warn("chapter battle win add asset error: %v", errCode)
  377. }
  378. // 首胜检测是否更新下一章节或者下一关
  379. if firstWin {
  380. chapter.Service().CheckNextLevelOrChapter(chapterTable, chapterRow, levelID)
  381. }
  382. chapterTable.Chapter.FinishChallengeCount += 1
  383. // 清理战斗数据
  384. chapterTable.Chapter.ClearBattleData()
  385. chapterTable.Save2Queue()
  386. // 发送探险次数事件
  387. p.PostEvent(event.NewChapterChallenge(p.playerID, levelID, 1, false))
  388. level, _ := chapterTable.Chapter.GetLevel(levelID)
  389. challengeLevel := &pb.ChapterLevelChallenge{
  390. ChapterID: chapterTable.Chapter.ChapterID,
  391. LevelID: chapterTable.Chapter.LevelID,
  392. Level: level.ToChapterLevelProto(levelID),
  393. }
  394. // 奖励
  395. if !rewards.IsEmpty() {
  396. proto := rewards.ToAssetListProto()
  397. challengeLevel.Rewards = &proto
  398. }
  399. return challengeLevel, code.OK
  400. }