package player import ( "f1-game/internal/battle" capp "f1-game/internal/cherry_app" "f1-game/internal/code" "f1-game/internal/constant" "f1-game/internal/data" "f1-game/internal/enum" "f1-game/internal/event" "f1-game/internal/extend/math" nameEvent "f1-game/internal/name/event" nameLocal "f1-game/internal/name/local" "f1-game/internal/pb" "f1-game/internal/types" "f1-game/nodes/game/player/asset" ao "f1-game/nodes/game/player/asset/origin" "f1-game/nodes/game/player/chapter" "fmt" "time" ctime "github.com/cherry-game/cherry/extend/time" cfacade "github.com/cherry-game/cherry/facade" cproto "github.com/cherry-game/cherry/net/proto" ) func (p *actorPlayer) initChapter() { p.Local().Register(nameLocal.Player_ChapterBattle, p.chapterBattle) p.Local().Register(nameLocal.Player_ChapterVerify, p.chapterVerify) p.Local().Register(nameLocal.Player_ChapterLevelSweep, p.chapterLevelSweep) p.Local().Register(nameLocal.Player_ChapterStarReward, p.chapterReward) p.Local().Register(nameLocal.Player_ChapterLevelTimesBuy, p.chapterLevelTimesBuy) p.Local().Register(nameLocal.Player_ChapterLastRecoverTime, p.chapterLastRecoverTime) p.Local().Register(nameLocal.Player_ChapterTakeOutStamina, p.chapterTakeOutStamina) p.EventRegister(nameEvent.Vip_LevelUp, p.chapterRefreshEvent) } // 发起探险战斗 func (p *actorPlayer) chapterBattle(session *cproto.Session, req *pb.I32) { var ( levelID = req.Value lineupID = constant.Adventure_Type // 阵容ID默认填1,如果后面扩展了再由前端传入 ) errCode := p.CheckChapterEnter(levelID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } playerBattleParam, errCode := chapter.Service().GetChapterBattleCampParam(p.playerID, lineupID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } chapterLevelRow, found := data.ChapterLevel.GetByID(levelID) if !found { p.ResponseCode(session, code.ConfigNotFound_ChapterLevel) return } var ( chapterTable = p.ChapterTable() monsterGroupID = chapterLevelRow.MonsterGroupID battleSceneID = chapterLevelRow.BattleSceneID ) monsterBattleParam := data.BuildBattleCampParam(monsterGroupID, enum.BattleCampType_Right) if len(monsterBattleParam.Squads) < 1 { p.ResponseCode(session, code.ChapterMonsterGroupConfigError) return } // 缓存关卡ID和随机种子 battleRandomSeed := time.Now().UnixNano() battleParam := &pb.BattleParam{ BattleType: enum.BattleType_Chapter, RandomSeed: uint64(battleRandomSeed), MapID: battleSceneID, Camps: []*pb.BattleCampParam{ playerBattleParam, monsterBattleParam, }, } // 发起战斗就直接去验证服获取校验码 recordID := fmt.Sprintf("pve_%d_%d_%s", p.playerID, levelID, ctime.Now().ToShortDateTimeFormat()) validation := battle.Validation(recordID, battleParam) if validation == nil { p.ResponseCode(session, code.ChapterBattleError) return } chapterTable.Chapter.SetBattleData(levelID, validation.Checksum) chapterTable.Save2Queue() p.Response(session, battleParam) } // 探险战斗验证 func (p *actorPlayer) chapterVerify(session *cproto.Session, req *pb.BattleResult) { var ( chapterTable = p.ChapterTable() ) // 没有挑战过,返回失败 if chapterTable.Chapter.BattleLevelID == 0 || chapterTable.Chapter.Checksum == 0 { p.ResponseCode(session, code.IllegalArgument) return } if capp.IsBattleValidation() && chapterTable.Chapter.Checksum != req.Checksum { p.Debug("chapterVerify checksum not match. req.Checksum = %d server.Checksum = %d", req.Checksum, chapterTable.Chapter.Checksum) p.ResponseCode(session, code.ChapterBattleResultVerifyFail) return } var ( isWin = req.VictoriousCamp == enum.BattleCampType_Left rsp = &pb.ChapterVerifyResponse{ Result: isWin, } ) if isWin { challengeLevel, errCode := p.ChapterBattleResult(session, req) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } rsp.Challenge = challengeLevel } p.Response(session, rsp) } // 关卡扫荡 func (p *actorPlayer) chapterLevelSweep(session *cproto.Session, req *pb.I32I32) { var ( chapterTable = p.ChapterTable() levelID = req.Key sweepNum = req.Value ) chapterLevelRow, found := data.ChapterLevel.GetByID(levelID) if !found { p.ResponseCode(session, code.ConfigNotFound_ChapterLevel) return } // 判断关卡是否解锁 chapterLevel, found := chapterTable.Chapter.GetLevel(levelID) if !found { p.ResponseCode(session, code.ChapterLevelNotUnlocked) return } // 判断是否满星通关 if chapterLevel.IsNotFullStar(len(chapterLevelRow.Conditions)) { p.ResponseCode(session, code.ChapterLevelNotFullStar) return } // 如果存在每日挑战次数上限 if chapterLevelRow.DayChallengeCount > 0 { // 总挑战次数 totalChallengeCount := chapterLevel.DayBuyChallengeCount + chapterLevelRow.DayChallengeCount // 判断条件次数是否满足 if !chapterLevel.CheckChallengeCount(sweepNum, totalChallengeCount) { p.ResponseCode(session, code.ChapterLevelCountNotEnough) return } } var ( beforeIsFull = chapterTable.Chapter.LastRecoverTime <= 0 costs = chapterLevelRow.GetChapterLevelCost(sweepNum) ) // 扣除精力 _, errCode := asset.Service().Subs(p.playerID, costs, ao.ChapterLevelSweepCost) if code.IsFail(errCode) { p.ResponseCode(session, code.ChapterStaminaNotEnough) return } // 原来是满精力 判断扣除精力后精力是否还满 if beforeIsFull { if full := chapter.Service().IsStaminaFull(p.playerID); !full { // 体力不满 更新上次精力的恢复时间 chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond() } } var ( resp = &pb.ChapterLevelSweepReward{} rewards = types.Assets{} ) // 获取扫荡奖励 for num := int32(0); num < sweepNum; num++ { // 扫荡一次随机奖励 singleRewards := chapterLevelRow.RandomChapterLevelReward() rewards.AddAssets(singleRewards) proto := singleRewards.ToAssetListProto() resp.Rewards = append(resp.Rewards, &proto) } _, errCode = asset.Service().Adds(p.playerID, rewards, ao.ChapterLevelSweepReward, true) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } chapterLevel.DayChallengeCount += sweepNum chapterTable.Chapter.FinishChallengeCount += sweepNum chapterTable.Chapter.TotalSweepCount += sweepNum chapterTable.Save2Queue() // 发送探险次数事件 p.PostEvent(event.NewChapterChallenge(p.playerID, levelID, sweepNum, true)) // 关卡信息更新 通知客户端 chapter.Service().ChangePush(p.playerID, levelID) // 返回扫荡奖励 p.Response(session, resp) } // 领取累计星级奖励 func (p *actorPlayer) chapterReward(session *cproto.Session, req *pb.I32) { var ( chapterTable = p.ChapterTable() chapterID = req.Value ) chapterRow, found := data.Chapter.GetByID(chapterID) if !found { p.ResponseCode(session, code.ConfigNotFound_Chapter) return } // 判断章节是否解锁 if !chapterTable.Chapter.IsChapterUnlocked(chapterID) { p.ResponseCode(session, code.ChapterNotUnlocked) return } chapterRewardRows, found := data.ChapterReward.ListByID(chapterRow.RewardPoolID) if !found { p.ResponseCode(session, code.ConfigNotFound_ChapterReward) return } var ( // 当前星级 curStars = chapterTable.Chapter.GetChapterStar(chapterRow.StartID, chapterRow.EndID) rewards = types.Assets{} stars = []int32{} ) for _, chapterRewardRow := range chapterRewardRows { star := chapterRewardRow.StarNum // 判断章节星级奖励是否已领取 已领取跳过 if chapterTable.Chapter.IsRewardReceived(chapterID, star) { continue } // 判断星数不足,直接退出 if curStars < star { break } rewards.AddAssets(chapterRewardRow.Reward) stars = append(stars, star) } // 暂无可领取奖励 if rewards.IsEmpty() { p.ResponseCode(session, code.ChapterRewardIsEmpty) return } // 领取奖励 _, errCode := asset.Service().Adds(p.playerID, rewards, ao.ChapterStarRewardGet, true) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 更新领取信息 chapterReward := chapterTable.Chapter.ReceiveChapterReward(chapterID, stars...) chapterTable.Save2Queue() resp := chapterReward.ToChapterRewardProto() p.Response(session, resp) } // 探险关卡挑战次数购买 func (p *actorPlayer) chapterLevelTimesBuy(session *cproto.Session, req *pb.I32I32) { var ( chapterTable = p.ChapterTable() levelID = req.Key buyTimes = req.Value ) if buyTimes <= 0 { p.ResponseCode(session, code.ChapterLevelBuyTimesError) return } chapterLevelRow, found := data.ChapterLevel.GetByID(levelID) if !found { p.ResponseCode(session, code.ConfigNotFound_ChapterLevel) return } level, found := chapterTable.Chapter.GetLevel(levelID) if !found { p.ResponseCode(session, code.ChapterLevelNotUnlocked) return } // 超出每日购买次数上限 if level.DayBuyChallengeCount+buyTimes > chapterLevelRow.DayCanBuyTimes { p.ResponseCode(session, code.ChapterLevelBuyTimesNotEnough) return } cost := data.Const.ChapterTimesBuyCost _, errCode := asset.Service().Sub(p.playerID, cost.ID, cost.Num*int64(buyTimes), ao.ChapterLevelTimesBuyCost) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 更新关卡挑战购买次数 level.DayBuyChallengeCount += buyTimes chapterTable.Save2Queue() // 更新关卡信息 通知客户端 chapter.Service().ChangePush(p.playerID, levelID) p.ResponseCode(session, code.OK) } // 精力池上一次恢复时间 func (p *actorPlayer) chapterLastRecoverTime(session *cproto.Session, _ *pb.None) { chapterTable := p.ChapterTable() resp := &pb.I64{ Value: chapterTable.Chapter.LastRecoverTime, } p.Response(session, resp) } // 取出精力 func (p *actorPlayer) chapterTakeOutStamina(session *cproto.Session, req *pb.I64) { var ( chapterTable = p.ChapterTable() takeOutNum = req.Value ) if takeOutNum <= 0 { p.ResponseCode(session, code.IllegalArgument) return } // 获取缺少的精力值 var ( curStaminaNum = asset.Service().Get(p.playerID, data.Const.ChapterStaminaAddValue.ID) staminaLimit = chapter.Service().GetStaminaLimit(p.playerID) diffStamina = staminaLimit - curStaminaNum ) // 当前精力已满 if diffStamina <= 0 { p.ResponseCode(session, code.ChapterStaminaFull) return } // 实际取出的精力值 takeOutNum = math.Min(takeOutNum, diffStamina) // 判断消耗是否足够 _, errCode := asset.Service().Sub(p.playerID, enum.ItemIDTempStamina, takeOutNum, ao.ChapterStaminaTakeOutCost) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 恢复精力值 _, errCode = asset.Service().Add(p.playerID, data.Const.ChapterStaminaAddValue.ID, takeOutNum, ao.ChapterStaminaTakeOutReward, true) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 判断是否需要定时恢复精力 if chapterTable.Chapter.LastRecoverTime <= 0 { chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond() chapterTable.Save2Queue() } resp := &pb.I64I64{ Key: takeOutNum, Value: chapterTable.Chapter.LastRecoverTime, } p.Response(session, resp) } // vip升级触发精力恢复检测(vip提升精力&精力池上限) func (p *actorPlayer) chapterRefreshEvent(e cfacade.IEventData) { if _, ok := e.(*event.VipLevelUp); !ok { return } chapterTable := p.ChapterTable() if chapterTable.Chapter.LastRecoverTime > 0 { return } // 精力未满 if staminaFull := chapter.Service().IsStaminaFull(p.playerID); !staminaFull { chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond() chapterTable.Save2Queue() return } // 精力池未满 if staminaPoolFull := chapter.Service().IsStaminaPoolFull(p.playerID); !staminaPoolFull { chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond() chapterTable.Save2Queue() } } // 进入探险的检查 func (p *actorPlayer) CheckChapterEnter(levelID int32) int32 { chapterLevelRow, found := data.ChapterLevel.GetByID(levelID) if !found { return code.ConfigNotFound_ChapterLevel } // 章节ID chapterID := chapterLevelRow.ChapterID chapterRow, found := data.Chapter.GetByID(chapterID) if !found { return code.ConfigNotFound_Chapter } // 验证当前章节是否有这个关卡 if !chapterRow.VaildateLevel(levelID) { return code.ChapterLevelError } chapterTable := p.ChapterTable() // 章节是否已解锁 if !chapterTable.Chapter.IsChapterUnlocked(chapterID) { return code.ChapterNotUnlocked } // 关卡是否已解锁 if !chapterTable.Chapter.IsLevelUnlocked(levelID) { return code.ChapterLevelNotUnlocked } // 如果存在每日挑战次数上限 判断每日挑战次数是否达到上限 if chapterLevelRow.DayChallengeCount > 0 { // 总挑战次数 totalChallengeCount := chapterLevelRow.DayChallengeCount chapterLevel, found := chapterTable.Chapter.GetLevel(levelID) if found { totalChallengeCount += chapterLevel.DayBuyChallengeCount } // 判断是否存在进入次数 首次挑战也会扣除次数 if !chapterTable.Chapter.CheckLevelChallengeCount(levelID, totalChallengeCount) { return code.ChapterLevelCountNotEnough } } // 检测精力是否足够 errCode := asset.Service().IsAllEnough(p.playerID, chapterLevelRow.CostStamina) if code.IsFail(errCode) { return errCode } return code.OK } // 关卡挑战结果 func (p *actorPlayer) ChapterBattleResult(session *cproto.Session, result *pb.BattleResult) (*pb.ChapterLevelChallenge, int32) { var ( chapterTable = p.ChapterTable() levelID = chapterTable.Chapter.BattleLevelID ) chapterLevelRow, found := data.ChapterLevel.GetByID(levelID) if !found { return nil, code.ConfigNotFound_ChapterLevel } chapterRow, found := data.Chapter.GetByID(chapterLevelRow.ChapterID) if !found { return nil, code.ConfigNotFound_Chapter } // 扣除体力前是否满体力 beforeIsFull := chapterTable.Chapter.LastRecoverTime <= 0 // 扣除精力 _, errCode := asset.Service().Subs(p.playerID, chapterLevelRow.CostStamina, ao.ChapterLevelChallengeCost) if code.IsFail(errCode) { return nil, errCode } // 原来是满体力 判断扣除精力后精力是否还满 if beforeIsFull { if full := chapter.Service().IsStaminaFull(p.playerID); !full { // 体力不满 更新上次精力的恢复时间 chapterTable.Chapter.LastRecoverTime = ctime.Now().ToSecond() } } stars := chapter.Service().GetChapterStars(p.playerID, chapterLevelRow, result) // 更新关卡数据 firstWin := chapterTable.Chapter.BattleEndHandle(levelID, stars) var ( rewards types.Assets origin ao.Origin ) // 首胜奖励 if firstWin { rewards = chapterLevelRow.GetFirstRewards() origin = ao.ChapterLevelFirstReward } else { rewards = chapterLevelRow.RandomChapterLevelReward() origin = ao.ChapterLevelChallengeReward } // 发送首胜/挑战奖励 _, errCode = asset.Service().Adds(p.playerID, rewards, origin, true) if code.IsFail(errCode) { p.Warn("chapter battle win add asset error: %v", errCode) } // 首胜检测是否更新下一章节或者下一关 if firstWin { chapter.Service().CheckNextLevelOrChapter(chapterTable, chapterRow, levelID) } chapterTable.Chapter.FinishChallengeCount += 1 // 清理战斗数据 chapterTable.Chapter.ClearBattleData() chapterTable.Save2Queue() // 发送探险次数事件 p.PostEvent(event.NewChapterChallenge(p.playerID, levelID, 1, false)) level, _ := chapterTable.Chapter.GetLevel(levelID) challengeLevel := &pb.ChapterLevelChallenge{ ChapterID: chapterTable.Chapter.ChapterID, LevelID: chapterTable.Chapter.LevelID, Level: level.ToChapterLevelProto(levelID), } // 奖励 if !rewards.IsEmpty() { proto := rewards.ToAssetListProto() challengeLevel.Rewards = &proto } return challengeLevel, code.OK }