package mapLogic import ( "f1-game/internal/constant" "f1-game/internal/data" "f1-game/internal/enum" "f1-game/internal/event" "f1-game/internal/extend/times" "f1-game/internal/pb" "f1-game/internal/types" mapCall "f1-game/nodes/map/internal/call" "f1-game/nodes/map/internal/db" mapTypes "f1-game/nodes/map/internal/types" clog "github.com/cherry-game/cherry/logger" ) func (p *actor) addBuildAOI(buildTable *db.LogicBuildTable) { marker := buildTable.ToAOIMarker() mapCall.AOI.Enter(marker) } func (p *actor) buildTimer() { p.updateTime() for _, buildTable := range p.BuildTables { // 不处理隐藏建筑 if !buildTable.IsHide { p.buildTicker(buildTable) } if buildTable.IsDataChanged { buildTable.IsDataChanged = false buildTable.Save2Queue() } if buildTable.IsDelete { p.BuildTables.Remove(buildTable.ObjectID) } } } func (p *actor) buildTicker(buildTable *db.LogicBuildTable) { // 正在建造中 if buildTable.IsBuilding { if buildTable.CheckFinish(p.nowMillis) { p.buildFinish(buildTable) } } else if buildTable.IsUpgrading { // 正在升级中 if buildTable.CheckUpgradeFinish(p.nowMillis) { p.buildUpgradeFinish(buildTable) } } // 放弃中 if buildTable.IsDiscard() { p.doBuildDiscard(buildTable) } // 攻城判断 if buildTable.IsBeingSiege() { p.buildSiegeCheck(buildTable) } // 加固开启 if buildTable.IsReinforceOpen() { p.buildReinforce(buildTable) } } // 加固处理 func (p *actor) buildReinforce(buildTable *db.LogicBuildTable) { if p.nowMillis >= buildTable.Reinforce.EndTime { if buildTable.IsReinforcePreparing() { buildTable.Reinforce.ChangeStage(enum.ReinforceStage_Effect, p.nowMillis+data.Const.GetMapReinforceEffectTime()) clog.Debugf("change reinforce: playerID = %v, stage = %s, endTime = %s", buildTable.PlayerID, enum.GetReinforceStageName(buildTable.Reinforce.Stage), times.MillisToDatetimeFormat(buildTable.Reinforce.EndTime)) // 在 watcher 上记录冷却时间 watcherTable := p.WatcherTables.GetValue(buildTable.PlayerID) watcherTable.ReinforceCooldown = p.nowMillis + data.Const.GetMapReinforceCooldown() watcherTable.Save2Queue() // AOI 通知 mapCall.AOI.UpdateAll(buildTable.ObjectID, buildTable.Type) } else if buildTable.IsReinforceEffecting() { buildTable.Reinforce.ChangeStage(enum.ReinforceStage_None, 0) clog.Debugf("change reinforce: playerID = %v, stage = %ss", buildTable.PlayerID, enum.GetReinforceStageName(buildTable.Reinforce.Stage)) } buildTable.IsDataChanged = true } } // 检查建筑的攻城状态 func (p *actor) buildSiegeCheck(buildTable *db.LogicBuildTable) { for tileID := range buildTable.SiegeTiles { tileTable := p.TileTables.GetValue(tileID) // 判断是否有攻城的和掠夺的 if !tileTable.HasSiege() && !tileTable.HasSnatch() { buildTable.RemoveSiegeTile(tileID) } } // 如果没有再被攻城了,通知自动防守部队解散 if !buildTable.IsBeingSiege() { playerTeams := map[int64][]int32{} buildTable.AutoDefendList.Range(func(marchObjectID int64, _ *mapTypes.TileMarchInfo) bool { marchTable := p.MarchTables.GetValue(marchObjectID) playerTeams[marchTable.PlayerID] = append(playerTeams[marchTable.PlayerID], marchTable.TeamID) return true }) buildTable.AutoDefendList.Clear() buildTable.IsDataChanged = true if len(playerTeams) > 0 { for playerID, teamIDs := range playerTeams { mapCall.Player.AutoDefendQuit(playerID, teamIDs...) } } } } func (p *actor) doBuildDiscard(buildTable *db.LogicBuildTable) { if buildTable.DiscardEndTime > p.nowMillis { return } // 驻守部队修改状态为停留中 tileTable := p.TileTables.GetValue(buildTable.GetTileID()) if tileTable != nil { tileTable.GarrisonList.Range(func(marchObjectID int64, tileMarchInfo *mapTypes.TileMarchInfo) bool { marchTable := p.MarchTables.GetValue(marchObjectID) if marchTable != nil { // 修改状态为 停留中 p.updateMarchState(marchTable, enum.MarchState_Stay, 0) // 加入停留队列 tileTable := p.TileTables.GetValue(marchTable.GetTileID()) tileTable.AddStay(marchTable.ObjectID, mapTypes.NewTileMarchInfo( tileTable.TileID, marchTable.ObjectID, p.nowMillis, )) p.addTileProcess(tileTable) } return true }) tileTable.GarrisonList.Clear() tileTable.IsDataChanged = true } // 删除建筑 p.buildRemove(buildTable) } func (p *actor) buildFinish(buildTable *db.LogicBuildTable) { clog.Debugf("[buildFinish] ObjectID = %d, ConfigID = %d", buildTable.ObjectID, buildTable.ConfigID) buildTable.IsBuilding = false buildTable.IsDataChanged = true // 重置攻城属性 siegeTable := p.SiegeTables.GetValue(buildTable.ObjectID) siegeTable.SiegeReset(false) // 通知客户端 p.UpdateObjectFull(buildTable.ObjectID, buildTable.Type) // 通知联盟建筑等级更新 if buildTable.BuildType == enum.BuildType_League { // 发送建筑完成事件 p.PostEvent(event.NewLeagueBuildFinish(buildTable.LeagueID, buildTable.ObjectID, buildTable.Level, false)) } // 联盟旗帜和联盟驻地要检查领土内的资源点,通知更新 // TODO 一个一个发,是否可以一组发 if buildTable.ConfigID == constant.MapBuild_LeaguePoint || buildTable.ConfigID == constant.MapBuild_LeagueFlag { buildRow, ok := data.MapBuild.GetByConfigID(buildTable.ConfigID) if ok { for _, point := range buildRow.GetAffectOffsetPoints(buildTable.X(), buildTable.Y(), false) { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable, ok := p.TileTables.Get(tileID) if !ok || tileTable.ResObjectID == 0 { continue } resTable, ok := p.ResTables.Get(tileTable.ResObjectID) if !ok || !resTable.HasPlayer() || !p.isSameLeague(resTable.LeagueID, tileTable.LeagueID) { continue } // 抛事件 p.PostEvent(event.NewResUpdate(resTable.PlayerID, &event.MapResUpdateData{ ObjectID: resTable.ObjectID, UpdateType: enum.ResUpdate_InLeagueArea, InLeagueArea: true, })) } } } } func (p *actor) buildUpgradeFinish(buildTable *db.LogicBuildTable) { clog.Debugf("[buildUpgradeFinish] ObjectID = %d, ConfigID = %d", buildTable.ObjectID, buildTable.ConfigID) buildTable.IsUpgrading = false buildTable.Level = buildTable.Level + 1 buildTable.IsDataChanged = true // 通知客户端 p.UpdateObjectFull(buildTable.ObjectID, buildTable.Type) // 通知联盟建筑等级更新 if buildTable.BuildType == enum.BuildType_League { // 发送联盟建筑升级事件 p.PostEvent(event.NewLeagueBuildFinish(buildTable.LeagueID, buildTable.ObjectID, buildTable.Level, true)) } } // buildAdd 增加建筑 func (p *actor) buildAdd(buildTable *db.LogicBuildTable) { clog.Debugf("[buildAdd] ObjectID = %v, ConfigID = %v", buildTable.ObjectID, buildTable.ConfigID) buildTable.IsDataChanged = true // 放入缓存 p.BuildTables.Put(buildTable.ObjectID, buildTable) // 移除建筑覆盖范围内的资源点,怪物等 for _, point := range buildTable.GetPoints() { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) if resTable, ok := p.getResTableInTile(tileTable); ok { p.resDead(resTable) } if monsterTable, ok := p.getMonsterTableInTile(tileTable); ok { p.monsterDead(monsterTable) tileTable.SetMonsterObjectID(0) } } for _, point := range buildTable.GetPoints() { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) tileTable.SetBuildObjectID(buildTable.ObjectID) p.addTileProcess(tileTable) } // 剔除被阻挡的出生点 p.bornPos.Remove(buildTable.GetTiles()...) switch buildTable.ConfigID { // case constant.MapBuild_SiegeBase: // // 如果是攻城大营,初始化 // buildTable.SiegeBase = &dbLogic.SiegeBase{} // buildTable.IsDataChanged = true case constant.MapBuild_LeagueFlag: // 如果是联盟旗帜,设置领地 buildRow, _ := data.MapBuild.GetByConfigID(buildTable.ConfigID) affectPoints := buildRow.GetAffectOffsetPoints(buildTable.X(), buildTable.Y(), true) for _, point := range affectPoints { // 跳过阻挡点 if data.MapData().IsPointBlock(point.X, point.Y) { continue } tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) tileTable.AddAreaFlagObjectID(buildTable.ObjectID, buildTable.LeagueID) p.addTileProcess(tileTable) } clog.Debugf("cover tile areas: BuildObjectID = %d, BuildConfigID = %d Points = %+v", buildTable.ObjectID, buildTable.ConfigID, affectPoints) case constant.MapBuild_LeaguePoint: // 如果是联盟驻地,设置领地 buildRow, _ := data.MapBuild.GetByConfigID(buildTable.ConfigID) affectPoints := buildRow.GetAffectOffsetPoints(buildTable.X(), buildTable.Y(), true) for _, point := range affectPoints { // 跳过阻挡点 if data.MapData().IsPointBlock(point.X, point.Y) { continue } tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) tileTable.SetAreaStrongholdObjectID(buildTable.ObjectID, buildTable.LeagueID) p.addTileProcess(tileTable) } clog.Debugf("cover tile areas: BuildObjectID = %d, BuildConfigID = %d Points = %+v", buildTable.ObjectID, buildTable.ConfigID, affectPoints) } if buildTable.BuildType == enum.BuildType_League { mapCall.League.BuildUpdate(buildTable.LeagueID, &pb.BuildUpdate{ ConfigID: buildTable.ConfigID, ObjectID: buildTable.ObjectID, X: buildTable.X(), Y: buildTable.Y(), IsAdd: true, }) } p.addBuildAOI(buildTable) // 设置动态障碍点 mapCall.PathFind.SetBarriers(buildTable.ToBarrierRequest()) if buildTable.PlayerID != 0 { p.PushOwnerMapObjectAdd(buildTable.PlayerID, buildTable.ToOwnerMapObjectPb()) } } // 移除建筑,isDelete 为true表示真的删除建筑 func (p *actor) buildRemove(buildTable *db.LogicBuildTable) { // 移除地块建筑并更新关联坐标 for _, point := range buildTable.GetPoints() { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) tileTable.SetBuildObjectID(0) p.addTileProcess(tileTable) } switch buildTable.ConfigID { // 联盟旗帜 case constant.MapBuild_LeagueFlag, constant.MapBuild_LeaguePoint: { buildRow, _ := data.MapBuild.GetByConfigID(buildTable.ConfigID) affectPoints := buildRow.GetAffectOffsetPoints(buildTable.X(), buildTable.Y(), true) for _, point := range affectPoints { // 跳过阻挡点 if data.MapData().IsPointBlock(point.X, point.Y) { continue } tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) if buildTable.ConfigID == constant.MapBuild_LeagueFlag { // 旗帜移除地块标记 tileTable.RemoveAreaFlagObjectID(buildTable.ObjectID) } else { // 领地移除地块标记 tileTable.ClearAreaStrongholdObjectID() } // 抛出资源点更新事件 if tileTable.ResObjectID > 0 { resTable, ok := p.ResTables.Get(tileTable.ResObjectID) if ok && resTable.HasPlayer() && p.isSameLeague(resTable.LeagueID, buildTable.LeagueID) { p.PostEvent(event.NewResUpdate(resTable.PlayerID, &event.MapResUpdateData{ ObjectID: resTable.ObjectID, UpdateType: enum.ResUpdate_InLeagueArea, InLeagueArea: false, })) } } p.addTileProcess(tileTable) } clog.Debugf("remove cover tile areas: BuildObjectID = %d, BuildConfigID = %d Points = %+v", buildTable.ObjectID, buildTable.ConfigID, affectPoints) if buildTable.ConfigID == constant.MapBuild_LeaguePoint { // 抛出联盟驻地被摧毁事件 p.PostEvent(event.NewLeaguePointDestroy(buildTable.LeagueID, buildTable.ObjectID)) // 设置 WatcherTable 的联盟驻地为 0 // TODO: 如何不遍历所有的 WatcherTable 就知道公会有哪些玩家 for _, watcherTable := range p.WatcherTables { if watcherTable.LeagueID == buildTable.LeagueID { watcherTable.SetLeaguePoint(0) clog.Debugf("[buildRemove] SetLeaguePoint. PlayerID = %v, buildID = %v", watcherTable.PlayerID, 0) } } } } } // 只有不是玩家主城的建筑才会删除 if buildTable.ConfigID != constant.MapBuild_MainCity { buildTable.IsDelete = true buildTable.IsDataChanged = true // 移除对象的攻城表 siegeTable := p.SiegeTables.GetValue(buildTable.ObjectID) siegeTable.IsDelete = true siegeTable.Save2Queue() p.SiegeTables.Remove(siegeTable.ObjectID) } else { buildTable.IsHide = true buildTable.IsDataChanged = true } if buildTable.PlayerID > 0 { // 玩家的建筑 watcherTable := p.WatcherTables.GetValue(buildTable.PlayerID) watcherTable.RemoveBuild(buildTable.ObjectID) } else if buildTable.LeagueID > 0 { // 联盟的建筑 mapCall.League.BuildUpdate(buildTable.LeagueID, &pb.BuildUpdate{ ConfigID: buildTable.ConfigID, ObjectID: buildTable.ObjectID, X: buildTable.X(), Y: buildTable.Y(), IsAdd: false, }) } if buildTable.PlayerID != 0 { p.PushOwnerMapObjectDel(buildTable.PlayerID, buildTable.ObjectID) } // 移出 AOI mapCall.AOI.Leave(buildTable.ObjectID, buildTable.Type) // 移除动态障碍点 mapCall.PathFind.RemoveBarriers(buildTable.OffsetPoints) // 移除建筑补充被覆盖的资源点 p.resReplenish(buildTable.GetPoints()) // 回收出生点 p.bornPos.Recycle(buildTable.GetTiles()...) } func (p *actor) getBuild(playerID int64, configID int32) (*db.LogicBuildTable, bool) { for _, build := range p.BuildTables { if build.PlayerID == playerID && build.ConfigID == configID { return build, true } } return nil, false } // 获取联盟驻地 // TODO: 优化掉这个方法,看看哪些地方调用了这个方法,看看能否通过其它方式传递 leaguePoint 的 ObjectID 进来直接查 func (p *actor) getLeaguePoint(leagueID int64) (*db.LogicBuildTable, bool) { for _, build := range p.BuildTables { if build.ConfigID == constant.MapBuild_LeaguePoint && build.LeagueID == leagueID { return build, true } } return nil, false } // isBuildSiege 建筑是否被攻城 func (p *actor) isBuildSiege(buildTable *db.LogicBuildTable) bool { tileTable := p.TileTables.GetValue(buildTable.GetTileID()) if tileTable.HasSiege() { return true } if tileTable.HasChildren() { for _, point := range tileTable.ChildPoints() { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } childTile := p.TileTables.GetValue(tileID) if childTile.HasSiege() { return true } } } return false } // isPointInBuildingRange 判断一个点是否在建筑范围内 // TODO: 优化掉这个方法 func (p *actor) isPointInBuildingRange(buildTable *db.LogicBuildTable, point types.Point) bool { if buildTable.Point.X == point.X && buildTable.Point.Y == point.Y { return true } buildRow, ok := data.MapBuild.GetByConfigID(buildTable.ConfigID) if ok { affectOffsetPoints := buildRow.GetAffectOffsetPoints(buildTable.Point.X, buildTable.Point.Y, true) for _, effectPoint := range affectOffsetPoints { if effectPoint.X == point.X && effectPoint.Y == point.Y { return true } } } return false } // isCoordInLeagueTerritory 判断一个坐标是否是处于某个联盟的领土 // TODO: 优化掉这个方法 func (p *actor) isCoordInLeagueTerritory(coord types.Point, league int64) bool { for _, build := range p.BuildTables { if build.LeagueID != league || build.IsBuilding { continue } switch build.ConfigID { case constant.MapBuild_LeaguePoint, constant.MapBuild_LeagueFlag: if p.isPointInBuildingRange(build, coord) { return true } } } return false } // isTileInLeagueTerritoryAndBorderingOtherLeague 判断一个坐标是否是处于某个联盟的领土并且与另一个联盟接壤 func (p *actor) isTileInLeagueTerritoryAndBorderingOtherLeague(targetTileID int32, targetLeague int64, srcLeague int64) bool { if srcLeague == 0 { return false } tileTable, ok := p.TileTables.Get(targetTileID) if !ok || !p.isSameLeague(tileTable.LeagueID, targetLeague) { return false } // 依次判断是否有旗帜、驻地、系统城的领土覆盖 for flagObjectID := range tileTable.AreaFlagObjectIDs { buildTable, ok := p.BuildTables.Get(flagObjectID) if !ok || buildTable.IsBuilding { continue } buildRow, ok := data.MapBuild.GetByConfigID(buildTable.ConfigID) if !ok { continue } for _, point := range buildRow.GetAffectEdgePoints(buildTable.Point.X, buildTable.Point.Y) { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) if tileTable != nil && p.isLeagueTerritoryBuildingBuiltOnTile(tileTable, srcLeague) { return true } } } // 驻地 if tileTable.AreaStrongholdObjectID > 0 { buildTable, ok := p.BuildTables.Get(tileTable.AreaStrongholdObjectID) if ok && !buildTable.IsBuilding { if buildRow, ok := data.MapBuild.GetByConfigID(buildTable.ConfigID); ok { for _, point := range buildRow.GetAffectEdgePoints(buildTable.Point.X, buildTable.Point.Y) { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) if tileTable != nil && p.isLeagueTerritoryBuildingBuiltOnTile(tileTable, srcLeague) { return true } } } } } // 系统城 if tileTable.AreaCastleObjectID > 0 { castleTable, ok := p.CastleTables.Get(tileTable.AreaCastleObjectID) if ok && !p.isSameLeague(castleTable.LeagueID, srcLeague) { if castleRow, ok := data.MapCastle.GetByConfigID(castleTable.ConfigID); ok { for _, point := range castleRow.GetAffectEdgePoints(castleTable.Point.X, castleTable.Point.Y) { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) if tileTable != nil && p.isLeagueTerritoryBuildingBuiltOnTile(tileTable, srcLeague) { return true } } } } } return false } // isPointsConnectedToLeagueTerritory 点是否与目标联盟连接 func (p *actor) isPointsConnectedToLeagueTerritory(points types.Points, leagueID int64) bool { for _, point := range points { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } tileTable := p.TileTables.GetValue(tileID) if tileTable != nil && p.isLeagueTerritoryBuildingBuiltOnTile(tileTable, leagueID) { return true } } return false } // isLeagueTerritoryBuildingBuiltOnTile 地块上是否有已经建成的联盟领地建筑 func (p *actor) isLeagueTerritoryBuildingBuiltOnTile(tileTable *db.LogicTileTable, leagueID int64) bool { if tileTable != nil && tileTable.LeagueID == leagueID { // 旗帜 for objectId := range tileTable.AreaFlagObjectIDs { tileBuildObj := p.BuildTables.GetValue(objectId) if tileBuildObj != nil && !tileBuildObj.IsBuilding { return true } } // 驻地 tileBuildObj := p.BuildTables.GetValue(tileTable.AreaStrongholdObjectID) if tileBuildObj != nil && !tileBuildObj.IsBuilding { return true } // 系统城 castleTable, ok := p.CastleTables.Get(tileTable.AreaCastleObjectID) if ok && !p.isSameLeague(castleTable.LeagueID, leagueID) { return true } } return false }