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" "f1-game/nodes/map/player/player" "f1-game/nodes/map/player/scout" "f1-game/nodes/map/player/storage" "f1-game/nodes/map/player/team" 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.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) } // 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) // 登录前事件处理 mapFacade.ServiceOnLogin(playerTable, session) // 推送各个模块数据 mapFacade.ServicePush(playerTable, session) // 设置actor为登录状态 p.isOnline = true // 返回玩家地图的信息 p.Response(session, playerTable.ToMapLoginResponse()) // 在线状态设置后,再启动定时器 p.startTimer() // 登录后事件处理 mapFacade.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) } // 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 := mapCall.PathFind.Find(startPoint.X, startPoint.Y, req.Point.X, req.Point.Y, p.playerID, playerTable.LeagueID, 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) } // 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 ok := mapFacade.Storage.IsAllEnough(p.playerID, row.BuildCost); !ok { 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 ok := mapFacade.Storage.IsAllEnough(p.playerID, mapBuildUpgradeRow.UpgradeCost); !ok { 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, } 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.IsRebornFailed = true playerTable.Save2Queue() // 推送通知 if p.isOnline { sessions.PushPlayer(p.playerID, nameRoute.PushMapPlayer_Reborn, playerTable.ToRebornPush()) } return } // 更新坐标 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 = dbMapPlayer.NewRebornInfo(point.X, point.Y) playerTable.Save2Queue() } } // reborn 重新随机出生点 func (p *actorPlayer) reborn(session *cproto.Session, _ *pb.None) { playerTable := db.GetMapPlayerTable(p.playerID) if !playerTable.IsRebornFailed { 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.ClearRebornInfo() 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 ok := data.Const.CheckMapMarkTitle(title); !ok { 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 := mapCall.PathFind.Find(startPoint.X, startPoint.Y, req.Point.X, req.Point.Y, p.playerID, playerTable.LeagueID, 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.I32List) { p.Debug("[autoDefendQuit] req = %+v", req) playerTable := db.GetMapPlayerTable(p.playerID) // 自动防守退出 for _, teamID := range req.List { dbTeam, ok := playerTable.Teams.GetTeam(teamID) if !ok { continue } dbTeam.IsAutoDefend = false } playerTable.Save2Queue() } // 玩家主城被攻打 func (p *actorPlayer) mainCitySiege(req *pb.I64) { p.Debug("[mainCitySiege] ObjectID = %v", req.Value) playerTable := db.GetMapPlayerTable(p.playerID) // 获取所有在城内并且非重伤的部队去自动防守 arg := &pb.AutoDefend{ BuildObjectID: req.Value, } for _, dbTeam := range playerTable.Teams { if dbTeam.IsNotStandby() || code.IsFail(dbTeam.IsBusy()) || team.Service().IsTeamTroopsLack(playerTable, dbTeam) { continue } member := &pb.AutoDefendMember{ MarchObjectID: dbTeam.MarchObjectID, } if dbTeam.IsNeedSync() { member.MarchSync = team.Service().GetTeamMarchSyncProto(playerTable, dbTeam) } arg.Members = append(arg.Members, member) } p.Debug("[mainCitySiege] ObjectID = %v, March size = %v", req.Value, len(arg.Members)) // 通知加入自动防守 if len(arg.Members) > 0 { rsp, errCode := mapCall.Logic.AutoDefend(arg) if code.IsFail(errCode) { p.Warn("[mainCitySiege] auto defend error: req = %+v, code = %v", arg, errCode) return } for _, pair := range rsp.List { if dbTeam, ok := playerTable.Teams.GetTeam(pair.Value); ok { if dbTeam.MarchObjectID == 0 { dbTeam.MarchObjectID = pair.Key } dbTeam.IsAutoDefend = true } } playerTable.Save2Queue() } } 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) // 获取所有在城内并且非重伤的部队去自动防守 arg := &pb.AutoDefend{ BuildObjectID: req.BuildObjectID, } for _, teamID := range req.TeamIDs { dbTeam, ok := playerTable.Teams.GetTeam(teamID) if !ok { continue } if dbTeam.IsAutoDefend || code.IsFail(dbTeam.IsBusy()) || team.Service().IsTeamTroopsLack(playerTable, dbTeam) { continue } member := &pb.AutoDefendMember{ MarchObjectID: dbTeam.MarchObjectID, } arg.Members = append(arg.Members, member) } p.Debug("[autoDefendRequest] ObjectID = %v, Arg = %+v", req.BuildObjectID, arg) // 通知加入自动防守 if len(arg.Members) > 0 { rsp, errCode := mapCall.Logic.AutoDefend(arg) if code.IsFail(errCode) { p.Warn("[autoDefendRequest] auto defend error: req = %+v, code = %v", arg, errCode) return } for _, pair := range rsp.List { if dbTeam, ok := playerTable.Teams.GetTeam(pair.Value); ok { dbTeam.IsAutoDefend = true } } playerTable.Save2Queue() } } 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) 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 } // 去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.TargetPlayerID = resp.TargetPlayerID 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) { continue } // 更新侦察结束时间 curScout.EndTime = data.Const.GetMapScoutTime() errCode = mapCall.Scout.ScoutUpdateStatus(queueID, enum.ScoutType_Start, p.playerID, curScout.EndTime) if code.IsFail(errCode) { p.Warn("[scoutUpdateNotice] start fail, update status fail. queueID = %d", queueID) continue } 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) { continue } // 发送call到目标玩家获取目标玩家的木石粮铁 mapCall.Player.ScoutSearch(queueID, p.playerID, curScout.TargetPlayerID) 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.GetCurrencys(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 = &dbMapPlayer.RebornInfo{ Point: types.NewPoint(req.X, req.Y), } playerTable.Save2Queue() }