package mapPlayer import ( "f1-game/internal/battle" "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" nameActor "f1-game/internal/name/actor" nameLocal "f1-game/internal/name/local" nameRemote "f1-game/internal/name/remote" nameRoute "f1-game/internal/name/route" "f1-game/internal/pb" "f1-game/internal/sessions" "f1-game/internal/types" ao "f1-game/nodes/game/player/asset/origin" mapCall "f1-game/nodes/map/internal/call" "f1-game/nodes/map/internal/db" dbBattleReport "f1-game/nodes/map/internal/db/data/battle_report" "sort" cherryNUID "github.com/cherry-game/cherry/extend/nuid" 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) initBattle() { p.Local().Register(nameLocal.MapPlayer_ReportInfo, p.reportInfo) p.Local().Register(nameLocal.MapPlayer_ReportList, p.reportList) p.Local().Register(nameLocal.MapPlayer_ReportLike, p.reportLike) p.Local().Register(nameLocal.MapPlayer_ReportRead, p.reportRead) p.Remote().Register(nameRemote.MapPlayer_BattleStart, p.battleStart) p.Remote().Register(nameRemote.MapPlayer_ReportAdd, p.reportAdd) // 初始化战斗处理函数 p.initBattleHandler() } func (p *actorPlayer) initBattleHandler() { p.battleHandlers = map[enum.TileBattleType]func(*pb.BattleReq, *pb.BattleRsp){ enum.TileBattle_PVP_Meet: p.battleStartPlayer, enum.TileBattle_PVP_Occupy: p.battleStartPlayer, enum.TileBattle_PVE_Occupy: p.battleStartRes, enum.TileBattle_PVE_SiegeGarrison: p.battleStartSiegePVE, enum.TileBattle_PVE_SiegeDurability: p.battleStartSiegePVE, enum.TileBattle_PVP_Siege: p.battleStartPlayer, enum.TileBattle_PVE_SnatchGarrison: p.battleStartSiegePVE, enum.TileBattle_PVP_Snatch: p.battleStartPlayer, enum.TileBattle_PVE_Snatch: p.battleStartSiegePVE, enum.TileBattle_PVE_Crusade: p.battleStartMonster, enum.TileBattle_PVN_SiegeDurability: p.battleStartSiegePVN, enum.TileBattle_PVN_Snatch: p.battleStartSiegePVN, } } // 个人战报列表 func (p *actorPlayer) reportList(session *cproto.Session, req *pb.I32Str) { var ( mapPlayerReportTable = db.GetMapPlayerReportTable(p.playerID) category = enum.ReportCategory(req.Key) // 分类 0:全部 1:开拓 2:交战 groupID = req.Value // 战报组ID rsp = &pb.ReportGroupList{} ) // 根据分类和战报组ID来精确获取战报组列表 reportGroups := dbBattleReport.FilterReportGroups(mapPlayerReportTable.BattleReport.Groups, category, groupID, data.Const.MapBattleReportPage) if len(reportGroups) == 0 { p.Response(session, rsp) return } var ( groupIDList []string // 战报组ID列表 reportIDList []string // 战报ID列表 ) for _, group := range reportGroups { groupIDList = append(groupIDList, group.GroupID) } // 批量获取战报组ID对应的战报ID列表 reportIDMap := redis.Game.GetReportIDListOfGroup(groupIDList...) for _, ids := range reportIDMap { reportIDList = append(reportIDList, ids...) } // 从redis批量获取战报集合 reportMap := redis.Game.GetBattleReports(reportIDList...) for _, group := range reportGroups { groupProto := group.ToProto(reportIDMap, reportMap, false) if groupProto == nil { continue } rsp.List = append(rsp.List, groupProto) } p.Response(session, rsp) } // 个人收藏战报和未读数量信息 func (p *actorPlayer) reportInfo(session *cproto.Session, _ *pb.None) { var ( mapPlayerReportTable = db.GetMapPlayerReportTable(p.playerID) groupIDList []string // 战报组ID列表 reportIDList []string // 战报ID列表 ) likes := mapPlayerReportTable.BattleReport.Likes for _, like := range likes { switch like.LikeType { case enum.ReportLikeType_Group: groupIDList = append(groupIDList, like.LikeID) case enum.ReportLikeType_Report: reportIDList = append(reportIDList, like.LikeID) } } // 获取战报组ID对应的战报ID列表 groupReportIDMap := redis.Game.GetReportIDListOfGroup(groupIDList...) for _, ids := range groupReportIDMap { reportIDList = append(reportIDList, ids...) } // 从redis批量获取战报信息 reportIDMap := redis.Game.GetBattleReports(reportIDList...) var likeList []*pb.ReportLike for _, like := range likes { var reportList []*pb.Report switch like.LikeType { case enum.ReportLikeType_Group: for _, reportID := range groupReportIDMap[like.LikeID] { if report, ok := reportIDMap[reportID]; ok { reportList = append(reportList, report) } } case enum.ReportLikeType_Report: if report, ok := reportIDMap[like.LikeID]; ok { reportList = append(reportList, report) } } if len(reportList) == 0 { continue } if len(reportList) > 1 { sort.Slice(reportList, func(i, j int) bool { return reportList[i].Time > reportList[j].Time }) } likeList = append(likeList, like.ToProto(reportList)) } p.Response(session, &pb.ReportInfo{ Count: mapPlayerReportTable.BattleReport.GetUnreadCount(), List: likeList, }) } // 收藏战报或取消收藏 func (p *actorPlayer) reportLike(session *cproto.Session, req *pb.ReportLikeRequest) { if req.LikeType < 0 || req.LikeID == "" { p.ResponseCode(session, code.IllegalArgument) return } var ( mapPlayerReportTable = db.GetMapPlayerReportTable(p.playerID) likeType = enum.ReportLikeType(req.LikeType) // 收藏类型 1:战报组 2:单个战报 likeID = req.LikeID // 收藏ID isLike = req.IsLike // 是否收藏 true:收藏 false:取消收藏 ) if !isLike { // 取消收藏 if mapPlayerReportTable.BattleReport.IsLike(likeType, likeID) { mapPlayerReportTable.BattleReport.RemoveLike(likeType, likeID) mapPlayerReportTable.Save2Queue() } p.ResponseCode(session, code.OK) return } // 检查战报组或战报是否存在 switch likeType { case enum.ReportLikeType_Group: if len(redis.Game.GetReportIDListOfGroup(likeID)) == 0 { p.ResponseCode(session, code.BattleReportNotExist) return } case enum.ReportLikeType_Report: if len(redis.Game.GetBattleReports(likeID)) == 0 { p.ResponseCode(session, code.BattleReportNotExist) return } default: p.ResponseCode(session, code.IllegalArgument) return } // 已经收藏过 if mapPlayerReportTable.BattleReport.IsLike(likeType, likeID) { p.ResponseCode(session, code.OK) return } mapPlayerReportTable.BattleReport.AddLike(likeType, likeID, ctime.Now().ToSecond()) mapPlayerReportTable.Save2Queue() p.ResponseCode(session, code.OK) } // 战报一键阅读 func (p *actorPlayer) reportRead(session *cproto.Session, req *pb.Str) { var ( mapPlayerReportTable = db.GetMapPlayerReportTable(p.playerID) groupID = req.Value // 战报组ID 0:全部 ) ok := mapPlayerReportTable.BattleReport.Read(groupID) if !ok { p.ResponseCode(session, code.BattleReportNotExist) return } mapPlayerReportTable.Save2Queue() p.ResponseCode(session, code.OK) } // 增加战报组 func (p *actorPlayer) reportAdd(reportGroup *pb.ReportGroup) { if len(reportGroup.List) < 1 { return } // 战报放入redis redis.Game.AddReports(reportGroup.GroupID, reportGroup.List...) // 添加个人战报记录 mapPlayerReportTable := db.GetMapPlayerReportTable(p.playerID) dbBattleReport.AddReportGroup(&mapPlayerReportTable.BattleReport.Groups, reportGroup.GroupID, enum.ReportCategory(reportGroup.Category), reportGroup.List[0].Time) mapPlayerReportTable.Save2Queue() // 通知客户端 sessions.PushPlayer(p.playerID, nameRoute.PushMapPlayer_ReportNew, reportGroup) playerTable := db.GetMapPlayerTable(p.playerID) if playerTable != nil && playerTable.LeagueID > 0 && !enum.IsDurabilityBattle(enum.TileBattleType(reportGroup.List[0].BattleType)) { // 添加联盟战报 mapCall.League.BattleReportAdd(playerTable.LeagueID, reportGroup) } // 发送战斗结果事件 p.PostEvent(event.NewBattleResult(p.playerID, reportGroup)) } // 根据玩家ID从战斗参数里获取玩家部队信息 func getTeamDataByPlayerID(battleReq *pb.BattleReq, playerID int64) *pb.BattleTeamData { for _, attacker := range battleReq.Attackers { if attacker.PlayerID == playerID { return attacker } } return battleReq.Defender } // 构建战报 func (p *actorPlayer) buildReportGroup(battleReq *pb.BattleReq, attacker *pb.BattleCampParam, defender *pb.BattleCampParam, resultVerify *pb.BattleResult, isDefender bool, recordID string, feat int64) *pb.ReportGroup { attackerTeamData := getTeamDataByPlayerID(battleReq, attacker.PlayerID) defenderTeamData := getTeamDataByPlayerID(battleReq, defender.PlayerID) report := &pb.Report{ ReportID: cherryNUID.Next(), BattleType: battleReq.BattleType, Point: battleReq.Tile.Point, ConfigID: battleReq.Tile.ObjectConfigID, Time: ctime.Now().ToMillisecond(), BattleRecordID: recordID, Attacker: buildReportTeam(attackerTeamData, attacker, resultVerify), Defender: buildReportTeam(defenderTeamData, defender, resultVerify), IsDefender: isDefender, } var ( groupID = attackerTeamData.ReportGroupID // 战报组 result = resultVerify.VictoriousCamp // TODO 战斗结果 战斗验证逻辑会有平局,但是现有业务逻辑判断只有胜负 ) if isDefender { // 防守方取反 if resultVerify.VictoriousCamp == enum.BattleCampType_Left { result = enum.BattleCampType_Right } else if resultVerify.VictoriousCamp == enum.BattleCampType_Right { result = enum.BattleCampType_Left } groupID = battleReq.Defender.ReportGroupID } // 战斗结果 0:赢 report.Result = result // 扣除耐久 report.ReportResult = &pb.ReportResult{ Rewards: &pb.AssetList{ List: []*pb.Asset{}, }, } // 进攻方赢,防守方输才显示战果,耐久,资源 if isDefender { if result != enum.BattleCampType_Left { // 被扣除了耐久 report.ReportResult.Durability = battleReq.Durability // 被掠夺了资源 if battleReq.Rewards != nil { for _, reward := range battleReq.Rewards.List { report.ReportResult.Rewards.List = append(report.ReportResult.Rewards.List, reward) } } } } else { if result == enum.BattleCampType_Left { // 扣除耐久 report.ReportResult.Durability = battleReq.Durability // 获得的资源 if battleReq.Rewards != nil { for _, reward := range battleReq.Rewards.List { report.ReportResult.Rewards.List = append(report.ReportResult.Rewards.List, reward) } } } } // 功勋 if feat > 0 { report.ReportResult.Rewards.List = append(report.ReportResult.Rewards.List, &pb.Asset{ Id: enum.ItemIDFeat, Num: feat, }) } // TODO 战报分类 新增侦察类型再进行判断处理 category := enum.ReportCategory_PVE if enum.IsTilePVPBattle(enum.TileBattleType(battleReq.BattleType)) { category = enum.ReportCategory_PVP } // 如果地图没有给战报组ID,则生成一个唯一的战报组ID避免战报无法记录,最多就是单独的一个战报 if len(groupID) == 0 { groupID = cherryNUID.Next() } return &pb.ReportGroup{ GroupID: groupID, Category: int32(category), List: []*pb.Report{report}, } } func buildReportTeam(battleTeamData *pb.BattleTeamData, camp *pb.BattleCampParam, resultVerify *pb.BattleResult) *pb.BattleReportTeam { if camp == nil { return nil } team := &pb.BattleReportTeam{} for _, squad := range camp.Squads { squadResult := battle.GetBattleSquadResult(camp.Camp, squad.Fighter.Id, resultVerify) team.List = append(team.List, &pb.BattleReportObject{ Id: squad.Fighter.Id, ObjConfigID: squad.Fighter.ConfigID, BattleObjType: squad.Fighter.BattleObjType, Level: squad.Fighter.Level, Star: squad.Fighter.Star, LineupID: squad.TileIndex, IsDead: squadResult.FighterResult.IsDead, TroopsID: squad.TroopsID, TotalSoldier: squad.TroopsNum, RemainSoldier: squadResult.SoldierNum, MaxHealth: battle.GetMaxHealth(squad.Fighter.Attrs), Health: squadResult.FighterResult.Hp, }) } // 玩家信息 if battleTeamData != nil { team.PlayerID = battleTeamData.PlayerID team.PlayerName = battleTeamData.PlayerName team.LeagueID = battleTeamData.LeagueID team.LeagueName = battleTeamData.LeagueName } return team } // 守军战斗逻辑, 返回是否胜利 func (p *actorPlayer) onBattleDefend(req *pb.BattleReq, attackerServerParam *pb.ServerBattleCampParam, teamBattleDamage *pb.TeamBattleDamage, defender *pb.BattleCampParam, monsterIndex int32) (int64, bool) { var ( playerTable = db.GetMapPlayerTable(p.playerID) attacker = attackerServerParam.Param ) // 设置守军参数 setDefendBattleParam(playerTable, req.Tile.ObjectID, monsterIndex, defender) // 判断守军是否还能参战,兵力都为0就跳过 if checkDefendSoldier(playerTable, req.Tile.ObjectID, monsterIndex) { return 0, true } // 录像ID recordID := cherryNUID.Next() // 战斗验证 resultVerify, isVerify := p.battleVerify(recordID, attacker, defender) if resultVerify == nil { return 0, false } // 如果不是战斗验证的没有录像 if !isVerify { recordID = "" } // 构建战报 reportGroup := p.buildReportGroup(req, attacker, defender, resultVerify, false, recordID, 0) p.reportAdd(reportGroup) // 如果打的地块有归属也需要构建对方战报 if req.Defender.PlayerID > 0 { defenderReport := p.buildReportGroup(req, defender, attacker, resultVerify, true, recordID, 0) mapCall.Player.BattleReportAdd(req.Defender.PlayerID, defenderReport) } // 更新进攻方兵力 battle.UpdateBattleParam(attacker, resultVerify) // 战斗结束后保存守军信息 recoverTime := saveDefendStatus(playerTable, req.Tile.ObjectID, monsterIndex, resultVerify) // 记录进攻方剩余兵力 recordBattleSoldierNum(teamBattleDamage, attackerServerParam.HeroTeamIndex, battle.GetBattleCampResult(resultVerify, enum.BattleCampType_Left)) return recoverTime, resultVerify.VictoriousCamp == enum.BattleCampType_Left } // 战斗验证 func (p *actorPlayer) battleVerify(recordID string, attacker *pb.BattleCampParam, defender *pb.BattleCampParam) (*pb.BattleResult, bool) { // 构建战斗参数 param := &pb.BattleParam{ MapID: 2, //TODO 根据战斗的类型配置不同的战斗地图ID RandomSeed: uint64(ctime.Now().ToMillisecond()), BattleType: enum.BattleType_SLG, Camps: []*pb.BattleCampParam{ attacker, defender, }, } // 只要双方有战斗小队数量为0直接构建战斗结果 if !battle.CheckBattleSquadTroopsNum(attacker) || !battle.CheckBattleSquadTroopsNum(defender) { return battle.BuildValidationResult(param), false } // 验证战斗结果 return battle.Validation(recordID, param), true } func (p *actorPlayer) battleStart(req *pb.BattleReq) { rsp := &pb.BattleRsp{ Data: req, } battleType := enum.TileBattleType(req.BattleType) if fn, ok := p.battleHandlers[battleType]; ok { fn(req, rsp) } else { p.Error("battleStart battleType = %d, not found", battleType) } // 打耐久的战斗不需要返回,只需要记录战报 if battleType != enum.TileBattle_PVN_SiegeDurability { // 把战斗结果返回给map mapCall.Logic.BattleEnd(rsp) } } // battleStartSiegePVE 与攻城守军战斗 func (p *actorPlayer) battleStartSiegePVE(req *pb.BattleReq, rsp *pb.BattleRsp) { // 批量构建进攻方战斗数据 battleParamMap, ok := battle.GetServerBattleCampParams(req) if !ok { p.Error("battleStartSiegePVE GetServerBattleCampParams error") return } if len(req.Tile.Monsters) == 0 { p.Warn("battleStartSiegePVE failed. tileID = %d, monsters is empty", req.Tile.Id) return } monsterGroupID := req.Tile.Monsters[0] defender := data.BuildBattleCampParam(monsterGroupID, enum.BattleCampType_Right) if len(defender.Squads) == 0 { p.Warn("battleStartSiegePVE failed. tileID = %d, monsterGroupID = %d", req.Tile.Id, monsterGroupID) return } // 构建进攻方数据 var ( attackerTeamData = req.Attackers[0] attackerServerParam = battleParamMap[attackerTeamData.PlayerID] attacker = attackerServerParam.Param attackerTeamBattleDamage = battle.NewTeamBattleDamage(attackerTeamData.TeamID, attackerTeamData.ObjectID) ) // 记录战斗之前的兵力 recordBattleTroops(attackerTeamBattleDamage, attackerServerParam) // 录像ID recordID := cherryNUID.Next() // 战斗验证 resultVerify, isVerify := p.battleVerify(recordID, attacker, defender) if resultVerify == nil { p.Warn("battleStartSiegePVE verify failed. tileID = %d, monsterGroupID = %d", req.Tile.Id, monsterGroupID) return } // 如果不是战斗验证的没有录像 if !isVerify { recordID = "" } // 构建战报 reportGroup := p.buildReportGroup(req, attacker, defender, resultVerify, false, recordID, 0) if p.playerID == attackerTeamData.PlayerID { p.reportAdd(reportGroup) } else { mapCall.Player.BattleReportAdd(attackerTeamData.PlayerID, reportGroup) } // 如果打建筑有归属也需要构建对方战报 if req.Defender.PlayerID > 0 || req.Defender.LeagueID > 0 { defenderReport := p.buildReportGroup(req, defender, attacker, resultVerify, true, "", 0) if req.Defender.PlayerID > 0 { mapCall.Player.BattleReportAdd(req.Defender.PlayerID, defenderReport) } if req.Defender.LeagueID > 0 { mapCall.League.BattleReportAdd(req.Defender.LeagueID, defenderReport) } } // 记录进攻方剩余兵力 recordBattleSoldierNum(attackerTeamBattleDamage, attackerServerParam.HeroTeamIndex, battle.GetBattleCampResult(resultVerify, enum.BattleCampType_Left)) // 更新进攻方兵力 battle.UpdateBattleParam(attacker, resultVerify) // 更新防守方兵力 troopLoss := battle.UpdateBattleParam(defender, resultVerify) if troopLoss > 0 { attackerTeamBattleDamage.KillTroops += troopLoss } // 把战损扣除的兵力带回去 rsp.MarchTroops = append(rsp.MarchTroops, convertMarchSyncTroops(attackerTeamBattleDamage)) // 兵力扣除和更新 p.subTeamTroopsNum(attackerTeamBattleDamage) rsp.IsWin = resultVerify.VictoriousCamp == enum.BattleCampType_Left if !rsp.IsWin { // 失败设置重伤 var ( injuresTime = ctime.Now().ToSecond() + data.Const.HeroInjureTime arg = &pb.I32I64{Key: attackerTeamData.TeamID, Value: injuresTime} ) p.setTeamInjury(arg) } } // battleStartSiegePVN 无守军的拆耐久战 func (p *actorPlayer) battleStartSiegePVN(req *pb.BattleReq, rsp *pb.BattleRsp) { // 批量构建进攻方战斗数据 battleParamMap, ok := battle.GetServerBattleCampParams(req) if !ok { p.Error("battleStartSiegePVN GetServerBattleCampParams error") return } // 构建一个没有小队的敌方阵营战斗参数 defender := &pb.BattleCampParam{ Camp: enum.BattleCampType_Right, Squads: []*pb.BattleSquadParam{}, } // 构建进攻方数据 var ( attackerTeamData = req.Attackers[0] attackerServerParam = battleParamMap[attackerTeamData.PlayerID] attacker = attackerServerParam.Param ) // 战斗验证,走自动构建的战斗结果 resultVerify, _ := p.battleVerify("", attacker, defender) // 构建战报 reportGroup := p.buildReportGroup(req, attacker, defender, resultVerify, false, "", 0) p.reportAdd(reportGroup) // 建筑有归属也需要构建对方战报,互斥,优先判断归属是玩家 if req.Defender.PlayerID > 0 || req.Defender.LeagueID > 0 { defenderReport := p.buildReportGroup(req, defender, attacker, resultVerify, true, "", 0) if req.Defender.PlayerID > 0 { mapCall.Player.BattleReportAdd(req.Defender.PlayerID, defenderReport) } else if req.Defender.LeagueID > 0 { // 联盟建筑,单独添加到对应联盟战报里去 mapCall.League.BattleReportAdd(req.Defender.LeagueID, defenderReport) } } } // battleStartPlayer 玩家交战 func (p *actorPlayer) battleStartPlayer(req *pb.BattleReq, rsp *pb.BattleRsp) { battleParamMap, ok := battle.GetServerBattleCampParams(req) if !ok { p.Error("battleStartPlayer GetServerBattleCampParams error") return } var ( // 进攻方的战斗数据实时构建 attackerTeamData = req.Attackers[0] attacker = battleParamMap[attackerTeamData.PlayerID] attackerBattleDamage = battle.NewTeamBattleDamage(attackerTeamData.TeamID, attackerTeamData.ObjectID) // 对方战斗数据从redis中获取 defender = battleParamMap[req.Defender.PlayerID] defenderBattleDamage = battle.NewTeamBattleDamage(req.Defender.TeamID, req.Defender.ObjectID) ) recordBattleTroops(attackerBattleDamage, attacker) recordBattleTroops(defenderBattleDamage, defender) // 录像ID recordID := cherryNUID.Next() // 战斗验证 resultVerify, isVerify := p.battleVerify(recordID, attacker.Param, defender.Param) if resultVerify == nil { return } // 如果不是战斗验证的没有录像 if !isVerify { recordID = "" } // 更新进攻方剩余兵力 recordBattleSoldierNum(attackerBattleDamage, attacker.HeroTeamIndex, battle.GetBattleCampResult(resultVerify, enum.BattleCampType_Left)) // 更新防守方剩余兵力 recordBattleSoldierNum(defenderBattleDamage, defender.HeroTeamIndex, battle.GetBattleCampResult(resultVerify, enum.BattleCampType_Right)) // 计算进攻方损失兵力 attackerTotalLossTroops := calcBattleTotalLossTroops(attackerBattleDamage) // 计算防守方损失兵力 defenderTotalLossTroops := calcBattleTotalLossTroops(defenderBattleDamage) // 双方功勋计算 addAttackerFeat := int64(defenderTotalLossTroops * data.Const.FeatRatio / constant.RatioBase) if addAttackerFeat > 0 { assets := types.Assets{} assets.Add(enum.ItemIDFeat, addAttackerFeat) mapCall.Player.AddAssets(attackerTeamData.PlayerID, assets, ao.None) } addDefenderFeat := int64(attackerTotalLossTroops * data.Const.FeatRatio / constant.RatioBase) if addDefenderFeat > 0 { assets := types.Assets{} assets.Add(enum.ItemIDFeat, addDefenderFeat) mapCall.Player.AddAssets(req.Defender.PlayerID, assets, ao.None) } // 构建战报 reportGroup := p.buildReportGroup(req, attacker.Param, defender.Param, resultVerify, false, recordID, addAttackerFeat) // 给进攻方发送战报 p.reportAdd(reportGroup) // 构建对方战报 defenderReport := p.buildReportGroup(req, defender.Param, attacker.Param, resultVerify, true, recordID, addDefenderFeat) // 给对方发送战报 mapCall.Player.BattleReportAdd(req.Defender.PlayerID, defenderReport) // 更新进攻方兵力 battle.UpdateBattleParam(attacker.Param, resultVerify) // 更新防守方兵力 battle.UpdateBattleParam(defender.Param, resultVerify) // 通知更新进攻方兵力 p.subTeamTroopsNum(attackerBattleDamage) // 通知更新防守兵力变化 mapCall.Player.SubTeamTroopsNum(req.Defender.PlayerID, defenderBattleDamage) // 把战损扣除的兵力带回去 rsp.MarchTroops = append(rsp.MarchTroops, convertMarchSyncTroops(attackerBattleDamage), convertMarchSyncTroops(defenderBattleDamage)) if resultVerify.VictoriousCamp == enum.BattleCampType_Left { rsp.IsWin = true } // 重伤时间 var injuresTime = ctime.Now().ToSecond() + data.Const.HeroInjureTime if rsp.IsWin { // 胜利设置对方玩家重伤 arg := &pb.I32I64{Key: req.Defender.TeamID, Value: injuresTime} targetPath := cfacade.NewChildPath("", nameActor.Map_Player, req.Defender.PlayerID) p.Call(targetPath, nameRemote.MapPlayer_SetTeamInjury, arg) } else { // 失败设置重伤 arg := &pb.I32I64{Key: attackerTeamData.TeamID, Value: injuresTime} p.setTeamInjury(arg) } } // 单人和多人组队讨伐 func (p *actorPlayer) battleStartMonster(req *pb.BattleReq, rsp *pb.BattleRsp) { if len(req.Tile.Monsters) == 0 { p.Warn("battle start monster failed, monster is empty. resID = %d", req.Tile.Id) return } // 批量构建进攻方战斗数据 battleParamMap, ok := battle.GetServerBattleCampParams(req) if !ok { p.Error("battleStartMonster GetServerBattleCampParams error") return } // 构建进攻方数据 teamBattleDamageMap := map[int64]*pb.TeamBattleDamage{} for _, attacker := range req.Attackers { attackerBattleParam, found := battleParamMap[attacker.PlayerID] if found { teamBattleDamage := battle.NewTeamBattleDamage(attacker.TeamID, attacker.ObjectID) // 记录战斗之前的兵力 recordBattleTroops(teamBattleDamage, attackerBattleParam) // 记录所有玩家的部队战斗兵力数据,战前兵力和剩余兵力 teamBattleDamageMap[attacker.PlayerID] = teamBattleDamage } else { p.Warn("battle start monster failed, player not found. playerID = %d", attacker.PlayerID) } } var ( attackerIndex int = 0 // 按照顺序打,打完一个接着打,输了就给下一个继续打 ) for index, monsterGroupID := range req.Tile.Monsters { defender := data.BuildBattleCampParam(monsterGroupID, enum.BattleCampType_Right) if len(defender.Squads) == 0 { p.Warn("battle start monster failed. tileID = %d, monsterGroupID = %d", req.Tile.Id, monsterGroupID) return } for i := attackerIndex; i < len(req.Attackers); i++ { var ( attacker = req.Attackers[i] // 进攻方请求数据 attackerParam = battleParamMap[attacker.PlayerID] // 进攻方战斗数据 attackerBattleDemage = teamBattleDamageMap[attacker.PlayerID] // 进攻方战损记录 ) isWin := p.onBattleMonster(req, attackerParam, attackerBattleDemage, defender, monsterGroupID) // 打赢了就跳出循环打下一个怪 if isWin { // 全部打完了 if index == len(req.Tile.Monsters)-1 { if isWin { rsp.IsWin = true } // 获取进攻方兵力 if p.playerID == attacker.PlayerID { p.subTeamTroopsNum(attackerBattleDemage) } else { // 给对方发送兵力变化 mapCall.Player.SubTeamTroopsNum(attacker.PlayerID, attackerBattleDemage) } } break } else { if p.playerID == attacker.PlayerID { p.subTeamTroopsNum(attackerBattleDemage) } else { // 给对方发送兵力变化 mapCall.Player.SubTeamTroopsNum(attacker.PlayerID, attackerBattleDemage) } var ( injuresTime = ctime.Now().ToSecond() + data.Const.HeroInjureTime arg = &pb.I32I64{Key: attacker.TeamID, Value: injuresTime} ) // 如果打输了,就设置重伤状态 if p.playerID == attacker.PlayerID { p.setTeamInjury(arg) } else { targetPath := cfacade.NewChildPath("", nameActor.Map_Player, req.Defender.PlayerID) p.Call(targetPath, nameRemote.MapPlayer_SetTeamInjury, arg) } // 打输了就给下一个继续打 attackerIndex++ } } // 如果所有人都打完了,还没有打赢,就算输了 if attackerIndex >= len(req.Attackers) { break } } // 把战损扣除的兵力带回去 for _, marchSyncTroops := range teamBattleDamageMap { rsp.MarchTroops = append(rsp.MarchTroops, convertMarchSyncTroops(marchSyncTroops)) } } func (p *actorPlayer) onBattleMonster(req *pb.BattleReq, attackerParam *pb.ServerBattleCampParam, attackerBattleDemage *pb.TeamBattleDamage, defender *pb.BattleCampParam, monsterGroupID int32) bool { // 进攻方战斗参数 attacker := attackerParam.Param // 录像ID recordID := cherryNUID.Next() // 战斗验证 resultVerify, isVerify := p.battleVerify(recordID, attacker, defender) if resultVerify == nil { p.Warn("onBattleMonster battle verify failed. tileID = %d, monsterGroupID = %d", req.Tile.Id, monsterGroupID) return false } // 如果不是战斗验证的没有录像 if !isVerify { recordID = "" } // 构建战报 reportGroup := p.buildReportGroup(req, attacker, defender, resultVerify, false, recordID, 0) if p.playerID == attacker.PlayerID { p.reportAdd(reportGroup) } else { mapCall.Player.BattleReportAdd(attacker.PlayerID, reportGroup) } // 记录进攻方剩余兵力 recordBattleSoldierNum(attackerBattleDemage, attackerParam.HeroTeamIndex, battle.GetBattleCampResult(resultVerify, enum.BattleCampType_Left)) // 更新进攻方兵力 battle.UpdateBattleParam(attacker, resultVerify) // 更新防守方兵力 troopLoss := battle.UpdateBattleParam(defender, resultVerify) if troopLoss > 0 { attackerBattleDemage.KillTroops += troopLoss } // 打赢了就跳出循环打下一个怪 return resultVerify.VictoriousCamp == enum.BattleCampType_Left } // battleStartRes 资源守军战 func (p *actorPlayer) battleStartRes(req *pb.BattleReq, rsp *pb.BattleRsp) { if len(req.Tile.Monsters) == 0 { p.Warn("battle start res failed, monster is empty. resID = %d", req.Tile.Id) return } // 批量构建进攻方战斗数据 battleParamMap, ok := battle.GetServerBattleCampParams(req) if !ok { p.Error("battleStartRes GetServerBattleCampParams error") return } var ( attackerTeamData = req.Attackers[0] attackerServerParam = battleParamMap[attackerTeamData.PlayerID] attackerTeamBattleDamage = battle.NewTeamBattleDamage(attackerTeamData.TeamID, attackerTeamData.ObjectID) ) // 记录战斗之前的兵力 recordBattleTroops(attackerTeamBattleDamage, attackerServerParam) var ( isWin bool // 是否胜利 recoverTime int64 // 资源恢复时间 ) for index, monsterGroupID := range req.Tile.Monsters { defender := data.BuildBattleCampParam(monsterGroupID, enum.BattleCampType_Right) if len(defender.Squads) == 0 { p.Warn("battleStartRes failed. tileID = %d, monsterGroupID = %d", req.Tile.Id, monsterGroupID) break } // 进入战斗 retRecoverTime, retIsWin := p.onBattleDefend(req, attackerServerParam, attackerTeamBattleDamage, defender, int32(index)) if retRecoverTime > 0 { recoverTime = retRecoverTime } if !retIsWin { break } // 全部打完才算赢 if index == len(req.Tile.Monsters)-1 { isWin = true } } rsp.IsWin = isWin // 胜利就清空守军状态信息 if rsp.IsWin { playerTable := db.GetMapPlayerTable(p.playerID) playerTable.Defends.CleanDefend(req.Tile.ObjectID) } else { // 失败则通知恢复时间 if recoverTime > 0 { sessions.PushPlayer(p.playerID, nameRoute.PushMapPlayer_ResDefendRecover, &pb.I64I64{ Key: req.Tile.ObjectID, Value: recoverTime, }) } } // 把战损扣除的兵力带回去 rsp.MarchTroops = append(rsp.MarchTroops, convertMarchSyncTroops(attackerTeamBattleDamage)) // 兵力扣除和更新 p.subTeamTroopsNum(attackerTeamBattleDamage) if !rsp.IsWin { var ( injuresTime = ctime.Now().ToSecond() + data.Const.HeroInjureTime arg = &pb.I32I64{Key: req.Attackers[0].TeamID, Value: injuresTime} ) // 失败设置重伤 p.setTeamInjury(arg) } } // 记录战斗之前的兵力 func recordBattleTroops(teamBattleDamage *pb.TeamBattleDamage, serverParam *pb.ServerBattleCampParam) { for _, squad := range serverParam.Param.Squads { teamIndex := serverParam.HeroTeamIndex.Value[squad.Fighter.Id] teamBattleDamage.Members[teamIndex] = &pb.TeamMemberDamage{ TeamIndex: teamIndex, TroopsNum: squad.TroopsNum, } } } // 记录剩余兵力 func recordBattleSoldierNum(teamBattleDamage *pb.TeamBattleDamage, heroTeamIndex *pb.I64I32Map, battleCampResult *pb.BattleCampResult) { for _, marchTroops := range teamBattleDamage.Members { for _, squad := range battleCampResult.SquadResults { if heroTeamIndex.Value[squad.FighterResult.Id] == marchTroops.TeamIndex { marchTroops.SoldierNum = squad.SoldierNum break } } } } // 计算战损累计兵力 func calcBattleTotalLossTroops(teamBattleDamage *pb.TeamBattleDamage) int32 { totalLossTroops := int32(0) for _, member := range teamBattleDamage.Members { totalLossTroops += max(0, member.TroopsNum-member.SoldierNum) } return totalLossTroops } // 转换成同步兵力结构 func convertMarchSyncTroops(teamBattleDamage *pb.TeamBattleDamage) *pb.MarchSyncTroops { var troops []*pb.MarchTroops for _, member := range teamBattleDamage.Members { troops = append(troops, &pb.MarchTroops{ TeamIndex: member.TeamIndex, TroopsNum: max(0, member.TroopsNum-member.SoldierNum), }) } return &pb.MarchSyncTroops{ TeamID: teamBattleDamage.TeamID, ObjectID: teamBattleDamage.ObjectID, Troops: troops, KillTroops: teamBattleDamage.KillTroops, } } // 设置守军战斗参数 func setDefendBattleParam(playerTable *db.MapPlayerTable, objectID int64, monsterIndex int32, defender *pb.BattleCampParam) { var newSquads []*pb.BattleSquadParam // 新的守军列表 for _, squad := range defender.Squads { defend, found := playerTable.Defends.GetDefend(objectID) if found { defendStatus, found := defend.GetDefendStatus(monsterIndex, squad.Fighter.Id) if found { // 剩余兵力 squad.TroopsNum = defendStatus.RemainSoldier } } // 剩余兵力为0的就过滤掉 if squad.TroopsNum > 0 { newSquads = append(newSquads, squad) } } defender.Squads = newSquads } // 检查守军剩余小兵数量是否为0 func checkDefendSoldier(playerTable *db.MapPlayerTable, objectID int64, monsterIndex int32) bool { defend, found := playerTable.Defends.GetDefend(objectID) if !found { return false } // 记录是否存在 var isExist bool for _, status := range defend.DefendStatusMap { if status.MonsterIndex == monsterIndex { isExist = true if status.RemainSoldier > 0 { return false } } } // 没有记录就返回false if !isExist { return false } return true } // 保存守军状态 func saveDefendStatus(playerTable *db.MapPlayerTable, objectID int64, monsterIndex int32, battleResult *pb.BattleResult) int64 { var recoverTime int64 for _, result := range battleResult.CampResult { if result.Camp == enum.BattleCampType_Right { for _, squad := range result.SquadResults { defend, isNew := playerTable.Defends.GetOrCreateDefend(objectID) if isNew { recoverTime = defend.RecoverTime } defend.UpdateDefendStatus(monsterIndex, squad.FighterResult.Id, squad.SoldierNum) } } } return recoverTime }