| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079 |
- 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
- }
|