package mapPlayer import ( capp "f1-game/internal/cherry_app" "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" nameRedis "f1-game/internal/name/redis" 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" dbMapPlayer "f1-game/nodes/map/internal/db/data/player" mapFacade "f1-game/nodes/map/internal/facade" mapTypes "f1-game/nodes/map/internal/types" "f1-game/nodes/map/player/battle" "f1-game/nodes/map/player/facility" sandMap "f1-game/nodes/map/player/map" "f1-game/nodes/map/player/player" "f1-game/nodes/map/player/scout" "f1-game/nodes/map/player/storage" "f1-game/nodes/map/player/team" "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) initMap() { p.Local().Register(nameLocal.MapPlayer_Login, p.login) p.Local().Register(nameLocal.MapPlayer_Enter, p.enter) p.Local().Register(nameLocal.MapPlayer_SelectState, p.selectState) p.Local().Register(nameLocal.MapPlayer_BornStateInfo, p.bornStateInfo) p.Local().Register(nameLocal.MapPlayer_Join, p.join) p.Local().Register(nameLocal.MapPlayer_Quit, p.quit) p.Local().Register(nameLocal.MapPlayer_Slide, p.slide) p.Local().Register(nameLocal.MapPlayer_March, p.march) p.Local().Register(nameLocal.MapPlayer_Retreat, p.retreat) p.Local().Register(nameLocal.MapPlayer_MarchStop, p.marchStop) p.Local().Register(nameLocal.MapPlayer_Discard, p.discard) p.Local().Register(nameLocal.MapPlayer_DiscardCancel, p.discardCancel) p.Local().Register(nameLocal.MapPlayer_Build, p.build) p.Local().Register(nameLocal.MapPlayer_BuildUpgrade, p.buildUpgrade) p.Local().Register(nameLocal.MapPlayer_Relocate, p.relocate) p.Local().Register(nameLocal.MapPlayer_Comeback, p.comeback) p.Local().Register(nameLocal.MapPlayer_Reborn, p.reborn) p.Local().Register(nameLocal.MapPlayer_MapMark, p.mapMark) p.Local().Register(nameLocal.MapPlayer_MapMarkDelete, p.mapMarkDelete) p.Local().Register(nameLocal.MapPlayer_SiegeAssemble, p.siegeAssemble) p.Local().Register(nameLocal.MapPlayer_SiegeRank, p.siegeRank) p.Local().Register(nameLocal.MapPlayer_FindPath, p.findPath) p.Local().Register(nameLocal.MapPlayer_OccupyCastleList, p.occupyCastleList) p.Local().Register(nameLocal.MapPlayer_ScoutRequest, p.scoutRequest) p.Local().Register(nameLocal.MapPlayer_Reinforce, p.reinforce) p.Local().Register(nameLocal.MapPlayer_ReinforceCancel, p.reinforceCancel) p.Local().Register(nameLocal.MapPlayer_ResArmies, p.resArmies) p.Local().Register(nameLocal.MapPlayer_MapObjectPosition, p.mapObjectPosition) p.Remote().Register(nameRemote.MapPlayer_AutoRetreat, p.autoRetreat) p.Remote().Register(nameRemote.MapPlayer_AutoReborn, p.autoReborn) p.Remote().Register(nameRemote.MapPlayer_UpdatePrestige, p.updatePrestige) p.Remote().Register(nameRemote.MapPlayer_MainCitySiege, p.mainCitySiege) p.Remote().Register(nameRemote.MapPlayer_UpdateWatcherPoint, p.updateWatcherPoint) p.Remote().Register(nameRemote.MapPlayer_AutoDefendQuit, p.autoDefendQuit) p.Remote().Register(nameRemote.MapPlayer_AutoDefendRequest, p.autoDefendRequest) p.Remote().Register(nameRemote.MapPlayer_ResOccupy, p.resOccupy) p.Remote().Register(nameRemote.MapPlayer_ResUpdate, p.resUpdate) p.Remote().Register(nameRemote.MapPlayer_UpdateCastleResYield, p.updateCastleResYield) p.Remote().Register(nameRemote.MapScout_UpdateNotice, p.scoutUpdateNotice) p.Remote().Register(nameRemote.MapScout_Seach, p.scoutSearch) p.Remote().Register(nameRemote.MapScout_SeachResult, p.scoutSearchResult) p.Remote().Register(nameRemote.MapPlayer_NotifyRebornPush, p.notifyRebornPush) p.Remote().Register(nameRemote.MapPlayer_AutoMarchRequest, p.autoMarch) p.Remote().Register(nameRemote.Game_UpdateTimeOffset, p.updateTimeOffset) } // login 玩家登录地图 func (p *actorPlayer) login(session *cproto.Session, _ *pb.None) { p.Debug("player login map") if p.isOnline { p.ResponseCode(session, code.OK) return } p.updateNowTime() // 检查玩家表是否存在,没有则创建 playerTable, isNewPlayer := db.CheckMapPlayerTable(p.playerID) if isNewPlayer { session.Add(sessions.IsNewPlayer, isNewPlayer) } p.Debug("Player login map success") p.onReset(playerTable, true) // 登录前事件处理 ServiceOnLogin(playerTable, session) // 推送各个模块数据 ServicePush(playerTable, session) // 设置actor为登录状态 p.isOnline = true // 返回玩家地图的信息 p.Response(session, playerTable.ToMapLoginResponse()) // 在线状态设置后,再启动定时器 p.startTimer() // 登录后事件处理 ServiceOnLogined(playerTable, session) } // enter 玩家进入地图 func (p *actorPlayer) enter(session *cproto.Session, _ *pb.None) { p.Debug("player enter map") if !p.isOnline { p.ResponseCode(session, code.PlayerNotLogin) return } playerTable := db.GetMapPlayerTable(p.playerID) // 未选择出生州 if !playerTable.IsSelectBornState() { p.ResponseCode(session, code.Map_NotSelectState) return } rsp, errCode := mapCall.Logic.Enter(p.playerID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Debug("Player enter map success") // TODO: 这些分散的信息后序根据业务看看是否可以放在一起,比如 warcher 中 rsp.CityBaseEndTime = playerTable.Builds.CityBaseEndTime rsp.ComebackEndTime = playerTable.ComebackEndTime rsp.RechooseComebackEndTime = playerTable.RechooseComebackEndTime rsp.RelocateEndTime = playerTable.Builds.RelocateEndTime rsp.ResDefends = playerTable.Defends.ToProto() p.Response(session, rsp) } // selectState 选择出生州,只在玩家首次进入地图时调用 // 后序跨逻辑调选州再起 func (p *actorPlayer) selectState(session *cproto.Session, req *pb.I32) { playerTable := db.GetMapPlayerTable(p.playerID) if playerTable.IsSelectBornState() { p.ResponseCode(session, code.Map_AlreadySelectState) return } arg := &pb.SelectStateRequest{ StateID: req.Value, PlayerID: p.playerID, LeagueID: playerTable.LeagueID, } // 新玩家从 redis 获取一次数据 playerData := redis.Game.GetPlayerData(p.playerID, nameRedis.PlayerInfoFields...) if playerData != nil { arg.PlayerName = playerData.PlayerName arg.IconID = playerData.IconID arg.FrameID = playerData.FrameID } // 调用 logic 选择出生州 rsp, errCode := mapCall.Logic.SelectState(arg) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 更新出生州ID playerTable.ObjectID = rsp.ObjectID playerTable.SetPoint(rsp.Point.X, rsp.Point.Y) playerTable.BornStateID = req.Value playerTable.Save2Queue() // 同步城建属性给logic watcher capp.PostEvent(event.NewMapWatcherSync(playerTable.PlayerID, &event.MapWatcherData{ PlayerID: playerTable.PlayerID, IsFacilitySync: true, FacilityAttrs: playerTable.Facilities.Attrs, })) p.ResponseCode(session, code.OK) } // bornStateInfo 获取出生州信息 func (p *actorPlayer) bornStateInfo(session *cproto.Session, _ *pb.None) { rsp, errCode := mapCall.Logic.BornStateInfo() if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, rsp) } // join 以观察者进入地图 func (p *actorPlayer) join(session *cproto.Session, _ *pb.None) { p.Debug("player join map") playerTable := db.GetMapPlayerTable(p.playerID) mapCall.AOI.Enter(playerTable.ToAOIWatcher()) } // quit 以观察者退出地图 func (p *actorPlayer) quit(session *cproto.Session, _ *pb.None) { p.Debug("player quit map") playerTable := db.GetMapPlayerTable(p.playerID) mapCall.AOI.Leave(playerTable.ObjectID, enum.ObjectType_Player) mapCall.Logic.Quit(p.playerID) } // slide 滑动屏幕 func (p *actorPlayer) slide(session *cproto.Session, req *pb.Point) { p.Debug("player slide: %v", req) playerTable := db.GetMapPlayerTable(p.playerID) aoiRequest := mapTypes.NewAOIRequest(p.playerID, playerTable.ObjectID, enum.ObjectType_Player, req.X, req.Y, true) mapCall.AOI.Move(aoiRequest) } // march 行军 func (p *actorPlayer) march(session *cproto.Session, req *pb.MapMarchRequest) { marchType := enum.MarchType(req.MarchType) // 检查是否为不支持的行军类型 if enum.IsNotAllowMarchType(marchType) { p.ResponseCode(session, code.MapUnsupportMarchType) return } playerTable := db.GetMapPlayerTable(p.playerID) dbTeam, ok := playerTable.Teams.GetTeam(req.TeamID) if !ok { p.ResponseCode(session, code.MapTeamNotFound) return } if errCode := dbTeam.IsBusy(); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } var startPoint types.Point if dbTeam.IsNeedSync() { startPoint.Set(&playerTable.Point) } else { // 获取行军对象坐标 errCode := mapCall.Logic.GetObject1Point(dbTeam.MarchObjectID, enum.ObjectType_March, &startPoint) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } // 寻路 path, errCode := p.findMarchPath(playerTable, startPoint.X, startPoint.Y, req.Point.X, req.Point.Y, req.TargetObjectID, marchType) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 检查消耗 errCode = team.Service().CheckMarchCost(p.playerID, req.TeamID, marchType, len(path)) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 调用地图行军 arg := &pb.MarchRequest{ TeamID: dbTeam.TeamID, ObjectID: dbTeam.MarchObjectID, MarchType: req.MarchType, Point: req.Point, Path: path, IsAutoRetreat: req.IsAutoRetreat, TargetObjectID: req.TargetObjectID, } if dbTeam.IsNeedSync() { arg.MarchSync = team.Service().GetTeamMarchSyncProto(playerTable, dbTeam) } // 取消城外自动征兵 p.marchOutsideTroopsCancel(playerTable, dbTeam) rsp, errCode := mapCall.Logic.March(arg) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 保存行军对象ID if dbTeam.MarchObjectID == 0 { dbTeam.MarchObjectID = rsp.Key } // 设置自动征兵 dbTeam.IsAutoTroops = req.IsAutoConscript playerTable.Save2Queue() // 扣除资源 team.Service().MarchCost(p.playerID, req.TeamID, marchType, len(path)) // 更新队伍状态 team.Service().UpdateTeamStateByMarchState(playerTable, dbTeam, enum.MarchState(rsp.Value)) p.ResponseCode(session, code.OK) } func (p *actorPlayer) findMarchPath(playerTable *db.MapPlayerTable, sx, sy, ex, ey int32, targetObjectID int64, marchType enum.MarchType) ([]*pb.Point, int32) { path, errCode := mapCall.PathFind.Find(sx, sy, ex, ey, p.playerID, playerTable.LeagueID, marchType) if code.IsFail(errCode) { return nil, errCode } // 如果不是攻城类型,寻路到请求的坐标点 if marchType != enum.MarchType_Siege && marchType != enum.MarchType_Snatch { return path, code.OK } siegePoints, errCode := mapCall.Logic.GetSiegePoints(targetObjectID) if code.IsFail(errCode) { return nil, errCode } p.Debug("[findMarchPath] get siege points: %+v", siegePoints) if len(siegePoints.Points) == 0 { return path, code.OK } // 从最小距离到最大距离,对路点列表进行比较 pathLen := len(path) i := siegePoints.MinDistance outer: for ; i <= siegePoints.MaxDistance; i++ { point := path[pathLen-int(i)] for _, siegePoint := range siegePoints.Points { if siegePoint.X == point.X && siegePoint.Y == point.Y { break outer // found } } } return path[:pathLen-int(i)+1], code.OK } // retreat 主动回城 func (p *actorPlayer) retreat(session *cproto.Session, req *pb.I32I64) { var ( teamID = req.Key redeployObjectID = req.Value ) playerTable := db.GetMapPlayerTable(p.playerID) dbTeam, ok := playerTable.Teams.GetTeam(teamID) if !ok { p.ResponseCode(session, code.MapTeamNotFound) return } if dbTeam.IsStandby() { p.ResponseCode(session, code.OK) return } var ( startPoint types.Point endPoint types.Point ) // 没有传入调动对象ID,就回主城 // 撤退回主城是不是就要把调动值清除了? if redeployObjectID == 0 { // 默认回主城,只拿部队的坐标 endPoint.Set(&playerTable.Point) errCode := mapCall.Logic.GetObject1Point(dbTeam.MarchObjectID, enum.ObjectType_March, &startPoint) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 把部队的调动信息删除 dbTeam.RedeployObject.Set(0, 0) playerTable.Save2Queue() } else { // 回调动点 if redeployObjectID != dbTeam.RedeployObject.Key { p.ResponseCode(session, code.MapRetreat_NotRedeploy) return } // 获取行军对象和调动点的坐标 var ( marchObject = enum.NewObject(dbTeam.MarchObjectID, enum.ObjectType_March) redeployObject = enum.NewObject(redeployObjectID, dbTeam.RedeployObject.Value) errCode = mapCall.Logic.GetObject2Point(marchObject, redeployObject, &startPoint, &endPoint) ) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } // 取消城外自动征兵 p.marchOutsideTroopsCancel(playerTable, dbTeam) // 寻路 path, errCode := mapCall.PathFind.Find(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y, p.playerID, playerTable.LeagueID, enum.MarchType_Retreat) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 调用地图行军 arg := &pb.MarchRequest{ TeamID: dbTeam.TeamID, ObjectID: dbTeam.MarchObjectID, MarchType: int32(enum.MarchType_Retreat), Point: endPoint.ToProto(), Path: path, RetreatState: int32(enum.MarchState_Retreat), TargetObjectID: redeployObjectID, // 如果是0,要在 logic 判断撤退的目标 } rsp, errCode := mapCall.Logic.Retreat(arg) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 更新队伍状态 team.Service().UpdateTeamStateByMarchState(playerTable, dbTeam, enum.MarchState(rsp.Value)) p.ResponseCode(session, code.OK) } // 行军中止 func (p *actorPlayer) marchStop(session *cproto.Session, req *pb.I32) { playerTable := db.GetMapPlayerTable(p.playerID) march, ok := playerTable.Teams.GetTeam(req.Value) if !ok { p.ResponseCode(session, code.MapTeamNotFound) return } errCode := mapCall.Logic.MarchStop(march.MarchObjectID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.ResponseCode(session, code.OK) } // discard 放弃地图上的对象 func (p *actorPlayer) discard(session *cproto.Session, req *pb.I64I32) { var ( objectID = req.Key objectType = enum.ObjectType(req.Value) ) // 放弃资源 if objectType == enum.ObjectType_Res { errCode := mapCall.Logic.ResDiscard(p.playerID, objectID) p.ResponseCode(session, errCode) return } // 放弃建筑 if objectType == enum.ObjectType_Build { _, errCode := mapCall.Logic.BuildDiscard(p.playerID, objectID) p.ResponseCode(session, errCode) return } p.ResponseCode(session, code.MapObjectTypeUnkown) } // discardCancel 取消放弃对象 func (p *actorPlayer) discardCancel(session *cproto.Session, req *pb.I64I32) { var ( objectID = req.Key objectType = req.Value ) p.Debug("discardCancel playerID: %d, objectID: %d, objectType: %s", p.playerID, objectID, enum.GetObjectTypeName(enum.ObjectType(objectType))) errCode := mapCall.Logic.DiscardCancelPlayer(p.playerID, objectID, objectType) p.ResponseCode(session, errCode) } func (p *actorPlayer) build(session *cproto.Session, req *pb.MapBuildRequest) { row, ok := data.MapBuild.GetByConfigID(req.ConfigID) if !ok { p.ResponseCode(session, code.ConfigNotFound_MapBuild) return } if row.BuildType != enum.BuildType_Player { p.ResponseCode(session, code.MapBuild_NotPlayerBuild) return } playerTable := db.GetMapPlayerTable(p.playerID) // 如果当前建造的是玩家地基 需要判断是否在冷却时间内 if req.ConfigID == constant.MapBuild_CityBase && playerTable.Builds.IsCityBaseCooldowning() { p.ResponseCode(session, code.MapBuild_FoundateCooldown) return } if !mapFacade.Storage.IsAllEnough(p.playerID, row.BuildCost) { p.ResponseCode(session, code.ItemNotEnough) return } var ( buildCount = playerTable.Builds.GetBuildCount(req.ConfigID) buildCountLimit = facility.Service().GetBuildCountLimit(playerTable, row) ) // 计算建造时间 buildTime := row.BuildTime // 首次建造 if buildCount == 0 { if time, ok := data.Const.MapBuildFirstTimeElapsed[req.ConfigID]; ok { buildTime = time } } arg := pb.BuildRequest{ ConfigID: req.ConfigID, PlayerID: p.playerID, LeagueID: playerTable.LeagueID, X: req.Point.X, Y: req.Point.Y, BuildEndTime: ctime.Now().ToMillisecond() + buildTime*ctime.MillisecondsPerSecond, BuildLimit: buildCountLimit, } _, errCode := mapCall.Logic.Build(&arg) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 扣除道具 mapFacade.Storage.AssetSubs(p.playerID, row.BuildCost, ao.None, true) // 增加建筑数量 playerTable.Builds.AddBuildCount(req.ConfigID) resp := &pb.I64{} // 记录地基建造冷却时间戳 if req.ConfigID == constant.MapBuild_CityBase { playerTable.Builds.CityBaseEndTime = ctime.Now().ToMillisecond() + data.Const.GetMapCityBaseCooldown() resp.Value = playerTable.Builds.CityBaseEndTime } playerTable.Save2Queue() p.Response(session, resp) } // 玩家建筑升级 func (p *actorPlayer) buildUpgrade(session *cproto.Session, req *pb.MapBuildUpgradeRequest) { var ( objectID = req.ObjectID configID = req.ConfigID level = req.Level ) mapBuildRow, found := data.MapBuild.GetByConfigID(configID) if !found { p.ResponseCode(session, code.ConfigNotFound_MapBuild) return } // 不是玩家建筑 if mapBuildRow.BuildType != enum.BuildType_Player { p.ResponseCode(session, code.MapBuild_NotPlayerBuild) return } mapBuildUpgradeRow, found := data.MapBuildUpgrade.GetByConfigIDLevel(configID, level) if !found { p.ResponseCode(session, code.ConfigNotFound_MapBuildUpgrade) return } if !mapFacade.Storage.IsAllEnough(p.playerID, mapBuildUpgradeRow.UpgradeCost) { p.ResponseCode(session, code.ItemNotEnough) return } arg := &pb.BuildUpgradeRequest{ ObjectID: objectID, ConfigID: configID, PlayerID: p.playerID, Level: level, UpgradeEndTime: ctime.Now().ToMillisecond() + mapBuildUpgradeRow.UpgradeTime*ctime.MillisecondsPerSecond, } errCode := mapCall.Logic.BuildUpgrade(arg) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } mapFacade.Storage.AssetSubs(p.playerID, mapBuildUpgradeRow.UpgradeCost, ao.None, true) p.ResponseCode(session, code.OK) } // relocate 玩家迁城 func (p *actorPlayer) relocate(session *cproto.Session, req *pb.I64) { // 判断是否可以迁城 if errCode := p.relocatable(); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } cityBaseObjectID := req.Value point, errCode := mapCall.Logic.Relocate(p.playerID, cityBaseObjectID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } playerTable := db.GetMapPlayerTable(p.playerID) // 如果玩家存在联盟 删除驻地信息 if playerTable.LeagueID > 0 { removeReq := &pb.I64{Value: p.playerID} target := cfacade.NewChildPath("", nameActor.Map_League, playerTable.LeagueID) capp.Call(target, nameRemote.MapLeague_ResidenceRemove, removeReq) } // 更新迁城冷却时间 playerTable.Builds.RelocateEndTime = p.now.ToMillisecond() + data.Const.GetMapRelocateCooldown() playerTable.SetPoint(point.X, point.Y) playerTable.Save2Queue() resp := &pb.I64{ Value: playerTable.Builds.RelocateEndTime, } p.Response(session, resp) } func (p *actorPlayer) relocatable() int32 { p.updateNowTime() playerTable := db.GetMapPlayerTable(p.playerID) // 迁城冷却中 if playerTable.Builds.RelocateEndTime > p.now.ToMillisecond() { return code.MapRelocate_Cooldown } // 没有加入联盟,不能迁城 if playerTable.LeagueID == 0 { return code.MapRelocate_NoLeague } // TODO 其它限制,比如:主城被攻击,攻城时间 return code.OK } // comeback 玩家再起 func (p *actorPlayer) comeback(session *cproto.Session, req *pb.I32Bool) { var ( stateID = req.Key // 州ID rechoose = req.Value // 是否重新选州 ) p.Debug("[comeback] stateID = %d, rechoose = %v", stateID, rechoose) // 再起冷却中 playerTable := db.GetMapPlayerTable(p.playerID) p.updateNowTime() if rechoose { if playerTable.RechooseComebackEndTime > p.now.ToMillisecond() { p.ResponseCode(session, code.MapComeback_Cooldown) return } // 不能重选当前州 if stateID == playerTable.BornStateID { p.ResponseCode(session, code.MapComeback_SameState) return } // 需要退联盟 if playerTable.LeagueID != 0 { p.ResponseCode(session, code.MapComeback_NeedLeagueQuit) return } } else { if playerTable.ComebackEndTime > p.now.ToMillisecond() { p.ResponseCode(session, code.MapComeback_Cooldown) return } // 非重新选州,使用当前出生州 stateID = playerTable.BornStateID } point, errCode := mapCall.Logic.Comeback(p.playerID, rechoose, stateID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } if rechoose { playerTable.BornStateID = stateID playerTable.RechooseComebackEndTime = p.now.ToMillisecond() + data.Const.GetMapRechooseComebackCoolDown() } else { playerTable.ComebackEndTime = p.now.ToMillisecond() + data.Const.GetMapComebackCoolDown() } player.Service().UpdateWatcherPoint(playerTable, point.X, point.Y) // 如果玩家存在联盟 删除驻地信息 if playerTable.LeagueID > 0 { removeReq := &pb.I64{Value: p.playerID} target := cfacade.NewChildPath("", nameActor.Map_League, playerTable.LeagueID) capp.Call(target, nameRemote.MapLeague_ResidenceRemove, removeReq) } rsp := pb.ComebackResponse{ Point: point, ComebackEndTime: playerTable.ComebackEndTime, RechooseComebackEndTime: playerTable.RechooseComebackEndTime, } p.Response(session, &rsp) } // autoReborn 玩家自动重新出生 func (p *actorPlayer) autoReborn() { p.Debug("[autoReborn]") playerTable := db.GetMapPlayerTable(p.playerID) if playerTable.BornStateID == 0 { p.Warn("[autoReborn] player not select state") return } point, errCode := mapCall.Logic.Reborn(p.playerID, playerTable.BornStateID) if code.IsFail(errCode) { p.Debug("[autoReborn] errCode = %d", errCode) // 重新出生失败,保存失败信息,以待重试 playerTable.RebornInfo.IsFailed = true playerTable.Save2Queue() // 推送通知 if p.isOnline { sessions.PushPlayer(p.playerID, nameRoute.PushMapPlayer_Reborn, playerTable.ToRebornPush()) } return } playerTable.RebornInfo.IsFailed = false // 更新坐标 player.Service().UpdateWatcherPoint(playerTable, point.X, point.Y) // 推送重生 if p.isOnline { sessions.PushPlayer(p.playerID, nameRoute.PushMapPlayer_Reborn, &pb.RebornPush{ Point: point, }) } else { // 保存以登录时推送 playerTable.RebornInfo.Point.SetValue(point.X, point.Y) playerTable.RebornInfo.NeedPush = true playerTable.Save2Queue() } } // reborn 重新随机出生点 func (p *actorPlayer) reborn(session *cproto.Session, _ *pb.None) { playerTable := db.GetMapPlayerTable(p.playerID) if !playerTable.RebornInfo.IsFailed { p.ResponseCode(session, code.OperationNotSupportYet) return } // 还有选过州,不能重新随机出生点 if playerTable.BornStateID == 0 { p.ResponseCode(session, code.Map_NotSelectState) return } // 重新随机出生点 point, errCode := mapCall.Logic.Reborn(p.playerID, playerTable.BornStateID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } playerTable.RebornInfo.IsFailed = false playerTable.RebornInfo.NeedPush = false playerTable.Save2Queue() player.Service().UpdateWatcherPoint(playerTable, point.X, point.Y) p.Response(session, point) } // autoRetreat 自动撤退 // TODO 自动撤退的流程可以改一下,因为是地图上发过来的消息,所以应该是有部队的坐标和调动点的坐标的, // 应该带过来,这里就只做寻路就OK了 func (p *actorPlayer) autoRetreat(req *pb.AutoRetreat) { var ( teamID = req.TeamID marchState = enum.MarchState(req.MarchState) isRemoveRedeploy = req.IsRemoveRedeploy ) playerTable := db.GetMapPlayerTable(p.playerID) dbTeam, ok := playerTable.Teams.GetTeam(teamID) if !ok { p.Warn("auto retreat team not found, teamID = %d", teamID) return } // 移除调动点 if isRemoveRedeploy { dbTeam.RedeployObject.Set(0, 0) playerTable.Save2Queue() } var ( startPoint types.Point endPoint types.Point ) // 如果有调动点,就撤退回调动点,否则就撤退回主城 if dbTeam.RedeployObject.Key == 0 { // 默认回主城,只拿部队的坐标 endPoint.Set(&playerTable.Point) errCode := mapCall.Logic.GetObject1Point(dbTeam.MarchObjectID, enum.ObjectType_March, &startPoint) if code.IsFail(errCode) { p.Warn("[autoRetreat] get march point failed. teamID = %d, errCode = %d", teamID, errCode) return } } else { // 回调动点 // 获取行军对象和调动点的坐标 var ( marchObject = enum.NewObject(dbTeam.MarchObjectID, enum.ObjectType_March) deployObject = enum.NewObject(dbTeam.RedeployObject.Key, dbTeam.RedeployObject.Value) errCode = mapCall.Logic.GetObject2Point(marchObject, deployObject, &startPoint, &endPoint) ) if code.IsFail(errCode) { p.Warn("[autoRetreat] get object points failed. teamID = %d, RedeployObject = (%d, %s), errCode = %d", teamID, dbTeam.RedeployObject.Key, enum.GetObjectTypeName(dbTeam.RedeployObject.Value), errCode) return } } path, errCode := mapCall.PathFind.Find(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y, p.playerID, playerTable.LeagueID, enum.MarchType_Retreat) if code.IsFail(errCode) { p.Warn("auto retreat find path failed. teamID = %d, errCode = %d", teamID, errCode) return } // 调用地图行军 arg := &pb.MarchRequest{ TeamID: dbTeam.TeamID, ObjectID: dbTeam.MarchObjectID, MarchType: int32(enum.MarchType_Retreat), Point: endPoint.ToProto(), Path: path, IsAutoRetreat: true, // 自动撤退 RetreatState: int32(marchState), TargetObjectID: dbTeam.RedeployObject.Key, // 如果是0,要在 logic 判断撤退的目标 } rsp, errCode := mapCall.Logic.Retreat(arg) if code.IsFail(errCode) { p.Warn("auto retreat failed. teamID = %d, errCode = %d", teamID, errCode) return } team.Service().UpdateTeamStateByMarchState(playerTable, dbTeam, enum.MarchState(rsp.Value)) // 取消自动征兵 如果存在 p.marchOutsideTroopsCancel(playerTable, dbTeam) } // 更新声望 func (p *actorPlayer) updatePrestige(req *pb.I32Map) { prestigeMap := map[enum.PrestigeType]int32{} for k, v := range req.Value { prestigeMap[enum.PrestigeType(k)] = v } player.Service().UpdatePrestige(p.playerID, prestigeMap) } // 地图标记 func (p *actorPlayer) mapMark(session *cproto.Session, req *pb.MapMarkRequest) { var ( playerTable = db.GetMapPlayerTable(p.playerID) title = req.Title point = req.Point update = false markID, ok = data.PointToTileID(point.X, point.Y) ) // TODO 判断坐标是否正确 if !ok { p.ResponseCode(session, code.MapGetPointFail) return } // 判断地图标记上限 if playerTable.MapMarks.Size() >= data.Const.MapMarkNumLimit { p.ResponseCode(session, code.MapMark_NumLimit) return } // 判断标题长度是否合法 if !data.Const.CheckMapMarkTitle(title) { p.ResponseCode(session, code.MapMark_TitleLenLimit) return } mapMark, found := playerTable.MapMarks[markID] if !found { mapMark = &dbMapPlayer.MapMark{ Title: title, Point: types.NewPoint(point.X, point.Y), } playerTable.MapMarks.Put(markID, mapMark) update = true } if mapMark.Title != title { mapMark.Title = title update = true } if update { // 更新标记时间戳 mapMark.MarkTime = ctime.Now().ToSecond() playerTable.Save2Queue() } resp := mapMark.ToProto(markID) p.Response(session, resp) } // 地图标记删除 func (p *actorPlayer) mapMarkDelete(session *cproto.Session, req *pb.I32) { var ( playerTable = db.GetMapPlayerTable(p.playerID) markID = req.Value ) _, found := playerTable.MapMarks.Get(markID) if !found { p.ResponseCode(session, code.MapMark_NotExist) return } playerTable.MapMarks.Remove(markID) playerTable.Save2Queue() p.ResponseCode(session, code.OK) } // 攻城集结 func (p *actorPlayer) siegeAssemble(session *cproto.Session, req *pb.SiegeAssembleRequest) { p.updateNowTime() playerTable := db.GetMapPlayerTable(p.playerID) dbTeam, ok := playerTable.Teams.GetTeam(req.TeamID) if !ok { p.ResponseCode(session, code.MapTeamNotFound) return } if errCode := dbTeam.IsBusy(); code.IsFail(errCode) { p.ResponseCode(session, errCode) return } var ( startPoint types.Point endPoint types.Point ) // 如果是待命状态,行军点就是玩家当前位置 if dbTeam.IsNeedSync() { startPoint = playerTable.Point // 只打目标集结点就够了 errCode := mapCall.Logic.GetObject1Point(req.SiegeBaseObjectID, enum.ObjectType_Castle, &endPoint) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } else { // 非待命状态,要找行军对象坐标和集结点坐标 var ( marchObject = enum.NewObject(dbTeam.MarchObjectID, enum.ObjectType_March) siegeObject = enum.NewObject(req.SiegeBaseObjectID, enum.ObjectType_Castle) errCode = mapCall.Logic.GetObject2Point(marchObject, siegeObject, &startPoint, &endPoint) ) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } path, errCode := mapCall.PathFind.Find(startPoint.X, startPoint.Y, endPoint.X, endPoint.Y, p.playerID, playerTable.LeagueID, enum.MarchType_SiegeAssemble) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 检查资源是否足够 errCode = team.Service().CheckMarchCost(p.playerID, req.TeamID, enum.MarchType_SiegeAssemble, len(path)) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 调用地图行军 arg := &pb.MarchRequest{ TeamID: dbTeam.TeamID, ObjectID: dbTeam.MarchObjectID, MarchType: int32(enum.MarchType_SiegeAssemble), Point: endPoint.ToProto(), Path: path, IsMainForce: req.IsMainForce, TargetObjectID: req.SiegeBaseObjectID, } if dbTeam.IsNeedSync() { arg.MarchSync = team.Service().GetTeamMarchSyncProto(playerTable, dbTeam) } rsp, errCode := mapCall.Logic.SiegeAssemble(arg) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } if dbTeam.MarchObjectID == 0 { dbTeam.MarchObjectID = rsp.Key } // 更新部队调动对象 dbTeam.RedeployObject.Set(req.SiegeBaseObjectID, enum.ObjectType_Castle) playerTable.Save2Queue() // 扣除资源 team.Service().MarchCost(p.playerID, req.TeamID, enum.MarchType_SiegeAssemble, len(path)) // 更新队伍状态 team.Service().UpdateTeamStateByMarchState(playerTable, dbTeam, enum.MarchState(rsp.Value)) p.ResponseCode(session, code.OK) } // 获取城池首占信息 func (p *actorPlayer) siegeRank(session *cproto.Session, req *pb.I64) { rsp, errCode := mapCall.Logic.SiegeRank(req.Value) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, rsp) } // 查询寻路信息 func (p *actorPlayer) findPath(session *cproto.Session, req *pb.MapFindPathRequest) { playerTable := db.GetMapPlayerTable(p.playerID) dbTeam, ok := playerTable.Teams.GetTeam(req.TeamID) if !ok { p.ResponseCode(session, code.MapTeamNotFound) return } var startPoint types.Point if dbTeam.IsStandby() { startPoint.Set(&playerTable.Point) } else { // 获取行军对象坐标 errCode := mapCall.Logic.GetObject1Point(dbTeam.MarchObjectID, enum.ObjectType_March, &startPoint) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } } paths, errCode := p.findMarchPath(playerTable, startPoint.X, startPoint.Y, req.Point.X, req.Point.Y, 0, enum.MarchType_Retreat) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } rsp := &pb.Points{} for _, path := range paths { point := &pb.Point{ X: path.X, Y: path.Y, } rsp.List = append(rsp.List, point) } p.Response(session, rsp) } // 获取地图对象位置 func (p *actorPlayer) mapObjectPosition(session *cproto.Session, req *pb.MapObjectPositionRequest) { var position types.Point errCode := mapCall.Logic.GetObject1Point(req.ObjectID, enum.ObjectType(req.ObjectType), &position) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, &pb.Point{ X: position.X, Y: position.Y, }) } // 自动防守退出 func (p *actorPlayer) autoDefendQuit(req *pb.I64) { p.Debug("[autoDefendQuit] req = %+v", req) playerTable := db.GetMapPlayerTable(p.playerID) playerTable.AutoDefend.Remove(req.Value) playerTable.Save2Queue() } // 玩家主城被攻打 func (p *actorPlayer) mainCitySiege(req *pb.I64) { p.Debug("[mainCitySiege] ObjectID = %v", req.Value) playerTable := db.GetMapPlayerTable(p.playerID) // 让所有在城内的部队加入自动防守 for _, dbTeam := range playerTable.Teams { if dbTeam.IsStandby() { defendTeams, ok := playerTable.AutoDefend[req.Value] if !ok { defendTeams = types.Map[int32, int64]{} playerTable.AutoDefend[req.Value] = defendTeams } defendTeams.Put(dbTeam.TeamID, 0) } } playerTable.Save2Queue() p.updateNowTime() // 立即触发一次自动防守 sandMap.Service().CheckAutoDefend(playerTable, p.now.ToMillisecond()) } func (p *actorPlayer) updateWatcherPoint(req *pb.Point) { playerTable := db.GetMapPlayerTable(p.playerID) if playerTable == nil { return } p.Debug("update watcher point: %+v", req) player.Service().UpdateWatcherPoint(playerTable, req.X, req.Y) } // autoDefendNotice 自动防守通知 func (p *actorPlayer) autoDefendRequest(req *pb.AutoDefendRequest) { p.Debug("[autoDefendRequest] req = %+v", req) playerTable := db.GetMapPlayerTable(p.playerID) defendTeams, ok := playerTable.AutoDefend.Get(req.BuildObjectID) if !ok { defendTeams = types.Map[int32, int64]{} playerTable.AutoDefend[req.BuildObjectID] = defendTeams } // 所有部队加入自动防守 defendTeams.PutKeys(req.TeamIDs, 0) playerTable.Save2Queue() p.updateNowTime() // 立即触发一次自动防守 sandMap.Service().CheckAutoDefend(playerTable, p.now.ToMillisecond()) } func (p *actorPlayer) resOccupy(e cfacade.IEventData) { evt, ok := e.(*event.MapResOccupy) if !ok { p.Warn("[resOccupy] event type error. e = %+v", e) return } p.Debug("[resOccupy] %+v", evt) data := evt.Data() playerTable := db.GetMapPlayerTable(p.playerID) playerTable.ResMap.Put(data.ObjectID, &dbMapPlayer.Res{ ObjectID: data.ObjectID, TileID: data.TileID, ConfigID: data.ConfigID, InLeagueArea: data.InLeagueArea, }) // TODO 更新产量 playerTable.Save2Queue() } func (p *actorPlayer) resUpdate(e cfacade.IEventData) { evt, ok := e.(*event.MapResUpdate) if !ok { p.Warn("[resUpdate] event type error. e = %+v", e) return } p.Debug("[resUpdate] %+v", evt) data := evt.Data() playerTable := db.GetMapPlayerTable(p.playerID) res, ok := playerTable.ResMap.Get(data.ObjectID) if !ok { p.Warn("[resUpdate] res not found. e = %+v", e) return } switch data.UpdateType { case enum.ResUpdate_InLeagueArea: res.InLeagueArea = data.InLeagueArea case enum.ResUpdate_Delete: playerTable.ResMap.Remove(data.ObjectID) } // TODO 更新产量 playerTable.Save2Queue() } func (p *actorPlayer) updateCastleResYield(req *pb.I32Bool) { var ( configID = req.Key isRemove = req.Value ) p.Debug("[updateCastleResYield] configID = %v, isRemove = %v", configID, isRemove) playerTable := db.GetMapPlayerTable(p.playerID) if isRemove { playerTable.CastleResYield.Remove(configID) } else { playerTable.CastleResYield.Add(configID) } playerTable.Save2Queue() } // 获取攻占城池列表 func (p *actorPlayer) occupyCastleList(session *cproto.Session, _ *pb.None) { // 请求logic 获取攻占城池列表 rsp, errCode := mapCall.Logic.OccupyCastleList() if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, rsp) } // 主城加固 func (p *actorPlayer) reinforce(session *cproto.Session, _ *pb.None) { rsp, errCode := mapCall.Logic.Reinforce(p.playerID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, rsp) } // 主城加固取消 func (p *actorPlayer) reinforceCancel(session *cproto.Session, _ *pb.None) { errCode := mapCall.Logic.ReinforceCancel(p.playerID) p.ResponseCode(session, errCode) } // 获取资源守军 func (p *actorPlayer) resArmies(session *cproto.Session, req *pb.I64) { rsp, errCode := mapCall.Logic.ResArmies(req) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } p.Response(session, rsp) } // 发起侦察 func (p *actorPlayer) scoutRequest(session *cproto.Session, req *pb.I32I64) { var ( playerTable = db.GetMapPlayerTable(p.playerID) queueID = req.Key objectID = req.Value curTime = ctime.Now().ToMillisecond() ) if objectID <= 0 { p.ResponseCode(session, code.IllegalArgument) return } // 侦察队列不存在 scout, found := playerTable.Scouts.GetScout(queueID) if !found { p.ResponseCode(session, code.ScoutQueueNotExist) return } // 检测侦察队列是否正在使用 if scout.IsScouting(curTime) { p.ResponseCode(session, code.ScoutQueueBusy) return } // 道具不足 if !storage.Service().IsAllEnough(p.playerID, data.Const.MapScoutCosts) { p.ResponseCode(session, code.ItemNotEnough) return } // 去logic校验是否可侦察 resp, errCode := mapCall.Logic.ScoutCheck(p.playerID, objectID) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } endTime := data.Const.GetMapScoutMarchTime() // 更新当前为侦察-行军状态 errCode = mapCall.Scout.ScoutUpdateStatus(queueID, enum.ScoutType_March, p.playerID, endTime) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } // 侦察消耗 _, errCode = storage.Service().AssetSubs(p.playerID, data.Const.MapScoutCosts, ao.MapScoutCost, true) if code.IsFail(errCode) { p.ResponseCode(session, errCode) return } scout.ObjectID = objectID scout.TargetPlayerID = resp.TargetPlayerID scout.ScoutStatus = enum.ScoutType_March scout.Point = types.NewPoint(resp.Point.X, resp.Point.Y) scout.EndTime = endTime playerTable.Save2Queue() proto := scout.ToScoutProto(queueID) p.Response(session, proto) } // 开始侦察通知处理 func (p *actorPlayer) scoutUpdateNotice(req *pb.ScoutUpdateNotice) { var ( starts = req.Starts ends = req.Ends playerTable = db.GetMapPlayerTable(p.playerID) updateScouts = []int32{} ) // 处理侦察开始队列 for _, queueID := range starts { curScout, found := playerTable.Scouts.GetScout(queueID) if !found { p.Warn("[scoutUpdateNotice] start fail, queue not found. queueID = %d", queueID) continue } errCode := scout.Service().CheckScoutSuccess(playerTable, queueID, curScout) if code.IsFail(errCode) { curScout.Reset() updateScouts = append(updateScouts, queueID) continue } // 更新侦察结束时间 curScout.EndTime = data.Const.GetMapScoutTime(curScout.EndTime) errCode = mapCall.Scout.ScoutUpdateStatus(queueID, enum.ScoutType_Start, p.playerID, curScout.EndTime) if code.IsFail(errCode) { curScout.Reset() updateScouts = append(updateScouts, queueID) p.Warn("[scoutUpdateNotice] start fail, update status fail. queueID = %d", queueID) continue } curScout.ScoutStatus = enum.ScoutType_Start updateScouts = append(updateScouts, queueID) } // 处理侦察结束队列 for _, queueID := range ends { curScout, found := playerTable.Scouts.GetScout(queueID) if !found { p.Warn("[scoutUpdateNotice] end fail, queue not found. queueID = %d", queueID) continue } errCode := scout.Service().CheckScoutSuccess(playerTable, queueID, curScout) if code.IsFail(errCode) { curScout.Reset() updateScouts = append(updateScouts, queueID) continue } // 发送call到目标玩家获取目标玩家的木石粮铁 errCode = mapCall.Player.ScoutSearch(queueID, p.playerID, curScout.TargetPlayerID) if code.IsFail(errCode) { curScout.Reset() updateScouts = append(updateScouts, queueID) } } if len(updateScouts) > 0 { playerTable.SetChangeData() // 通知客户端更新 if p.isOnline { scout.Service().ChangePush(playerTable, updateScouts...) } } } // 侦察查询 func (p *actorPlayer) scoutSearch(req *pb.I32I64) { // 获取目标玩家的木石粮铁 var ( playerStorageTable = db.GetMapPlayerStorageTable(p.playerID) playerTable = db.GetMapPlayerTable(p.playerID) ) // 获取木石粮铁 assets := playerStorageTable.GetScoutCurrencys(enum.MapScoutItemIDList...) // 返回给侦察者 mapCall.Player.ScoutSearchResult(req.Key, req.Value, p.playerID, playerTable.PlayerName, assets) // TODO 判断是否需要通知被侦察玩家 } // 侦察结果 func (p *actorPlayer) scoutSearchResult(req *pb.ScoutSearchResult) { var ( queueID = req.QueueID playerTable = db.GetMapPlayerTable(p.playerID) ) curScout, found := playerTable.Scouts.GetScout(req.QueueID) if !found { p.Warn("[scoutSearchResult] scout not found. playerID=%d, queueID = %d", p.playerID, req.QueueID) return } // 构建战报 reportGroup := battle.Service().BuildScoutReportGroup(p.playerID, req.TargetPlayerID, playerTable.LeagueID, playerTable.PlayerName, req.TargetPlayerName, curScout.Point.ToProto(), req.Assets, enum.ScoutState_Success) // 发送侦察成功战报 battle.Service().AddReport(p.playerID, reportGroup) curScout.Reset() playerTable.SetChangeData() // 通知客户端队列更新 if p.isOnline { scout.Service().ChangePush(playerTable, queueID) } } func (p *actorPlayer) notifyRebornPush(req *pb.Point) { p.Debug("[notifyRebornPush] req = %+v", req) playerTable := db.GetMapPlayerTable(p.playerID) if p.isOnline { sessions.PushPlayer(p.playerID, nameRoute.PushMapPlayer_Reborn, &pb.RebornPush{ Point: &pb.Point{ X: req.X, Y: req.Y, }, }) return } // 玩家不在线,保存一下,玩家登录时推送 playerTable.RebornInfo.NeedPush = true playerTable.RebornInfo.Point.SetValue(req.X, req.Y) playerTable.Save2Queue() } // 自动行军 func (p *actorPlayer) autoMarch(req *pb.AutoMarchRequest) { p.Debug("[autoMarch] req = %+v", req) // 寻路 arg := &pb.FindMultiPathRequest{ FindType: int32(enum.FindMultiPath_AllSuccess), // 所有的路径都要成功 } for _, auto := range req.List { arg.Requests = append(arg.Requests, &pb.FindPathRequest{ StartX: auto.Start.X, StartY: auto.Start.Y, EndX: auto.End.X, EndY: auto.End.Y, PlayerID: req.PlayerID, LeagueID: req.LeagueID, MarchType: int32(auto.MarchType), }) } path, errCode := mapCall.PathFind.FindMulti(arg) if code.IsFail(errCode) { p.Debug("[autoMarch] find path fail. errCode = %d", errCode) return } rsp := &pb.AutoMarchResult{ PlayerID: req.PlayerID, ObjectID: req.ObjectID, } for i, auto := range req.List { rsp.List = append(rsp.List, &pb.AutoMarchResultData{ MarchType: auto.MarchType, Path: path.List[i].List, }) } // 发送自动行军通知 mapCall.Logic.AutoMarchResult(rsp) } // 更新时间偏移量 func (p *actorPlayer) updateTimeOffset(req *pb.I64) { offset := req.Value ctime.AddOffsetTime(time.Duration(offset) * time.Second) p.Warn("[updateTimeOffset] playerID=%d, offset = %d, time = %d", p.playerID, offset, ctime.Now().ToSecond()) }