package mapLogic import ( capp "f1-game/internal/cherry_app" "f1-game/internal/code" "f1-game/internal/constant" "f1-game/internal/data" "f1-game/internal/enum" "f1-game/internal/event" nameRemote "f1-game/internal/name/remote" "f1-game/internal/pb" mapCall "f1-game/nodes/map/internal/call" "f1-game/nodes/map/internal/db" dbLogic "f1-game/nodes/map/internal/db/data/logic" mapTypes "f1-game/nodes/map/internal/types" clog "github.com/cherry-game/cherry/logger" ) func (p *actor) initTileBattleFunc() { p.tileBattleResultFunc = map[enum.TileBattleType]tileBattleResultFunc{ enum.TileBattle_PVP_Meet: p.onTileMeetBattleResult, enum.TileBattle_PVP_Occupy: p.onTileOccupyBattleResult, enum.TileBattle_PVE_Occupy: p.onTileOccupyBattleResult, enum.TileBattle_PVE_SiegeGarrison: p.onTileSiegeGarrisonResult, enum.TileBattle_PVE_SiegeDurability: p.onTileSiegeDurabilityResult, enum.TileBattle_PVP_Siege: p.onTileSiegeDefendResult, enum.TileBattle_PVE_SnatchGarrison: p.onTileSnatchGarrisonResult, enum.TileBattle_PVP_Snatch: p.onTileSnatchDefendResult, enum.TileBattle_PVE_Crusade: p.onTileCrusadeBattleResult, enum.TileBattle_PVE_Snatch: p.onTileSnatchResult, enum.TileBattle_PVN_Snatch: p.onTileSnatchResult, } } func (p *actor) initTileProcess() { for _, tileTable := range p.TileTables { // 需要处理的地块 if tileTable.IsNeedToProcess() { p.addTileProcess(tileTable) } } } func (p *actor) tileTimer() { p.updateTime() // 只处理需要处理的地块 for _, tileTable := range p.processTiles { p.tileTicker(tileTable) } } func (p *actor) tileTicker(tileTable *db.LogicTileTable) { // 如果地块上有驻地部队,要进行范围检查 p.tileGarrisonCheck(tileTable) // 处理地块 p.tileProcess(tileTable) // 检查是否需要继续处理 p.tileCheck(tileTable) // 保存数据 p.tileSave(tileTable) } func (p *actor) tileCheck(tileTable *db.LogicTileTable) { // 不再需要处理 if !tileTable.IsNeedToProcess() { tileTable.IsNeedProcess = false tileTable.IsDataChanged = true p.processTiles.Remove(tileTable.TileID) } } func (p *actor) tileSave(tileTable *db.LogicTileTable) { // 保存数据,推送通知 if tileTable.IsDataChanged { tileTable.IsDataChanged = false tileTable.Save2Queue() } } func (p *actor) tileProcess(tileTable *db.LogicTileTable) { // 处理撤退的队伍 if tileTable.HasRetreater() { p.tileRetreat(tileTable) } if tileTable.HasBattleQueue() { p.tileBattleQueue(tileTable) } // 处理地块的战斗 if tileTable.HasBattleHandle() { p.tileBattle(tileTable) } // 处理地块的攻城 if tileTable.HasSiege() { p.tileSiege(tileTable) } // 处理地块的掠夺 if tileTable.HasSnatch() { p.tileSnatch(tileTable) } // 处理地块的占领 if tileTable.HasOccupyWait() { p.tileOccupy(tileTable) } } // tileGarrisonCheck 地块驻守部队碰撞检测 func (p *actor) tileGarrisonCheck(tileTable *db.LogicTileTable) { if !tileTable.HasGarrison() { return } checkTiles := map[int32]*db.LogicTileTable{} if tileTable.BuildObjectID > 0 { buildTable := p.BuildTables.GetValue(tileTable.BuildObjectID) checkTiles = buildTable.GetTileTableMap(p.TileTables, false) } else if tileTable.CastleObjectID > 0 { castleTable := p.CastleTables.GetValue(tileTable.CastleObjectID) checkTiles = castleTable.GetTileTableMap(p.TileTables, false) } // 驻守应该是不用是检查自己的,他会先被其它部队碰撞掉 // TODO 不清楚,先看看会不会有问题 if len(checkTiles) == 0 { return } marchList := tileTable.GarrisonList.NextCount(data.Const.MapMeetBattleQueue, mapTypes.ReadyFilter(p.nowMillis)) for _, pair := range marchList { attackObjectID, attackMarchInfo := pair.Key, pair.Value attackLeagueID := p.getMarchLeagueID(attackObjectID, attackMarchInfo.IsLegion) for _, checkTile := range checkTiles { // 有攻击队列说明正在打遭遇战,跳过 if checkTile.HasAttacker() { delete(checkTiles, checkTile.TileID) continue } // 提交了多场战斗,跳过 if checkTile.BattleQueue.Size() >= int(data.Const.MapMeetBattleQueue) { delete(checkTiles, checkTile.TileID) continue } defendObjectID, defendMarchInfo, ok := checkTile.GetDefender() // 没有防守方,跳过 if !ok { delete(checkTiles, checkTile.TileID) continue } // 同一阵营,那么格子里面都是自己人,跳过 defendLeagueID := p.getMarchLeagueID(defendObjectID, defendMarchInfo.IsLegion) if p.isSameLeague(attackLeagueID, defendLeagueID) { delete(checkTiles, checkTile.TileID) continue } // 驻守部队是进攻方,要保存状态 attacker := p.MarchTables.GetValue(attackObjectID) attacker.SaveState(p.nowMillis) p.buildTilePVPBattle(tileTable, enum.TileBattle_PVP_Meet, attackObjectID, defendObjectID, attackMarchInfo, defendMarchInfo) } } } func (p *actor) tileBattle(tileTable *db.LogicTileTable) { p.onTileBattleStart(tileTable) } func (p *actor) tileBattleQueue(tileTable *db.LogicTileTable) { // 处理等待中的战斗 p.onTileBattleWait(tileTable) // 处理已经有结果的战斗 p.onTileBattleResult(tileTable) } // onTileBattleStart 执行战斗开始处理 // 1. 打遭遇战 // 2. 打守军战 func (p *actor) onTileBattleStart(tileTable *db.LogicTileTable) { if tileTable.IsMeetting && !tileTable.HasMoreMeetBattles() { tileTable.MarkMeetingBattle(false) p.onTileMeetBattleEnd(tileTable) } if tileTable.HasMoreMeetBattles() { tileTable.MarkMeetingBattle(true) p.onTileMeetBattleStart(tileTable) return } if tileTable.HasOccupy() { p.onTileOccupyBattleStart(tileTable) return } if tileTable.HasCrusade() { p.onTileCrusadeBattleStart(tileTable) return } } func (p *actor) tileSiege(tileTable *db.LogicTileTable) { p.onTileSiegeStart(tileTable) } func (p *actor) tileSnatch(tileTable *db.LogicTileTable) { buildTable, ok := tileTable.GetBuild(p.TileTables, p.BuildTables) if !ok { return } siegeTable := p.SiegeTables.GetValue(buildTable.ObjectID) if siegeTable.RemainGarrisonArmies() > 0 { // 有驻城部队,先打驻城部队 p.onTileSnatchGarrisonStart(tileTable, buildTable, siegeTable) } else if p.hasAutoDefend(siegeTable) { // 看看有无攻城城防部队:调动的,征兵的,自动守城的 p.onTileSnatchDefendStart(tileTable, buildTable) } else { p.onTileSnatchResStart(tileTable, buildTable, siegeTable) } tileTable.IsDataChanged = true } // onTileBattleWait 等待战斗结果 // 当有战斗结束响应之后, 根据输赢执行继续战斗还是撤退 func (p *actor) onTileBattleWait(tileTable *db.LogicTileTable) { for _, battleInfo := range tileTable.BattleQueue { if battleInfo.IsTimeout(p.nowMillis, 10000) { p.onTileBattleTimeout(tileTable, battleInfo) } } } // onTileBattleResult 处理战斗结果 func (p *actor) onTileBattleResult(tileTable *db.LogicTileTable) { for _, battleInfo := range tileTable.BattleQueue { if battleInfo.BattleResult == nil { continue } clog.Debugf("tile battle result: TileID = %v, AttackObjectID = %v, BattleType = %v, win = %v", tileTable.TileID, battleInfo.AttackObjectID, enum.GetBattleTypeName(battleInfo.BattleType), battleInfo.BattleResult.IsWin) if f, ok := p.tileBattleResultFunc[battleInfo.BattleType]; ok { f(tileTable, battleInfo) // 战斗推送数据缓存到 map push := p.buildMapBattlePush(tileTable, battleInfo) updateID := p.newUpdateID() aoiUpdateBattleMap.Put(updateID, push) // AOI 更新 mapCall.AOI.UpdateWithID(push.AttackMarchObjectID, enum.ObjectType_March, updateID, nameRemote.MapLogic_AOIUpdateBattle) } else { clog.Errorf("tile battle result func not found: TileID = %v, BattleType = %v", tileTable.TileID, battleInfo.BattleType) } tileTable.RemoveBattleQueue(battleInfo.BattleID) } } // tilePVPBattleResult 行军战斗结果处理 func (p *actor) tilePVPBattleResult(winner *db.LogicMarchTable, winnerMarchInfo *mapTypes.TileMarchInfo, loser *db.LogicMarchTable, loserMarchInfo *mapTypes.TileMarchInfo, tileTable *db.LogicTileTable) { if loserMarchInfo.TileID == tileTable.TileID { p.addMarchToTileRetreat(loser, tileTable, enum.RetreatType_Rout, false) tileTable.RemoveDefendType(loser.ObjectID, loserMarchInfo.DefendType) } else { otherTile := p.TileTables.GetValue(loserMarchInfo.TileID) p.addMarchToTileRetreat(loser, otherTile, enum.RetreatType_Rout, false) otherTile.RemoveDefendType(loser.ObjectID, loserMarchInfo.DefendType) } winnerMarchInfo.LogicEndTime = p.nowMillis + constant.MapLeastBattleWaitTime winnerMarchInfo.MarkBattleWait(false) // 驻守列表和自动防守列表恢复状态 if winnerMarchInfo.DefendType == enum.TileDefend_Garrison || winnerMarchInfo.DefendType == enum.TileDefend_AutoDefend { marchTable := p.MarchTables.GetValue(winner.ObjectID) p.restoreMarchState(marchTable) } } // tileRetreat 撤退 func (p *actor) tileRetreat(tileTable *db.LogicTileTable) { retreatNum := tileTable.RetreatNumForOneTick() for range retreatNum { retreatInfo := tileTable.RestreatList.Remove(0) if retreatInfo == nil { break } var ( marchObjectID = retreatInfo.MarchObjectID marchState = enum.GetMarchStateByRetreatType(retreatInfo.RetreatType) isRemoveRedeploy = retreatInfo.IsRemoveRedeploy ) p.startMarchIDBack(marchObjectID, marchState, isRemoveRedeploy) } if retreatNum > 0 { tileTable.IsDataChanged = true } } // tileOccupy 处理地块占领事件 func (p *actor) tileOccupy(tileTable *db.LogicTileTable) { // 按照占领时间来排序,拿出下一个不处于战斗等待状态的 marchObjectID, tileMarchInfo, ok := tileTable.OccupyWaitList.Next(mapTypes.ReadyFilter(p.nowMillis)) if !ok { return } // 时间未到 if tileMarchInfo.LogicEndTime > p.nowMillis { return } // 等待处理中 marchTable := p.MarchTables.GetValue(marchObjectID) var occupySuc bool resTable, ok := p.ResTables.Get(marchTable.TargetObjectID) if ok { retCode := resTable.CanOccupy(p.nowMillis, marchTable.PlayerID, marchTable.LeagueID) if code.IsOK(retCode) { occupySuc = true } else { clog.Debugf("res occupy fail: MarchObjectID = %v, ResObjectID = %v, Code = %v", marchObjectID, resTable.ObjectID, retCode) } } // 判断占领上限 watcherTable := p.WatcherTables.GetValue(marchTable.PlayerID) if len(watcherTable.Res) >= data.Const.MapResOccupyLimit { clog.Debugf("Res occupy fail: num limit [MarchObjectId = %d, ResObjectID = %d]", marchObjectID, resTable.ObjectID) occupySuc = false } // 从占领队列移除 tileTable.RemoveOccupyWait(marchTable.ObjectID) // 部队撤退 p.addMarchToRetreat(marchTable, enum.RetreatType_Retreat, false) // 如果占领失败 if !occupySuc { return } p.removeWatcherRes(resTable) // 设置拥有者 resTable.SetOwner(marchTable.PlayerID, marchTable.LeagueID) // 设置免战时间 resTable.SetInvincibleEndTime(p.nowMillis + data.Const.GetResOccupyInvincibleTime()) // 重置战报组 resTable.ResetReport() // 重置采集时间 resTable.ResetGatherTime(p.nowMillis) // 推送资源点 p.UpdateObjectFull(resTable.ObjectID, resTable.Type) watcherTable.AddRes(resTable.ObjectID) // 推送 p.PushOwnerMapObjectAdd(resTable.PlayerID, resTable.ToOwnerMapObjectPb()) clog.Debugf("march occupy wait success. MarchID = %v, ResID = %v, Point = %v", marchTable.ObjectID, resTable.ObjectID, resTable.Point) capp.PostEvent(event.NewTileOccupy(marchTable.PlayerID, resTable.PlayerID, resTable.ConfigID, resTable.ResLevel)) // 推送资源点占领事件 capp.PostEvent(event.NewResOccupy(resTable.PlayerID, &event.MapResOccupyData{ ObjectID: resTable.ObjectID, TileID: resTable.GetTileID(), ConfigID: resTable.ConfigID, InLeagueArea: p.isSameLeague(tileTable.LeagueID, marchTable.LeagueID), })) } // isTileMeet 是否在 tile 上碰撞 func (p *actor) isTileMeet(tileTable *db.LogicTileTable, playerID, leagueID int64) (isMet bool, isAttack bool) { // 碰撞顺序 // 1. 驻守部队 if p.checkMeetMarchList(&tileTable.GarrisonList, playerID, leagueID) { clog.Debugf("[isTileMeet] garrison: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } // 2. 占领部队 if p.checkMeetMarchList(&tileTable.OccupyList, playerID, leagueID) { clog.Debugf("[isTileMeet] occupy: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } if p.checkMeetMarchList(&tileTable.OccupyWaitList, playerID, leagueID) { clog.Debugf("[isTileMeet] occupy wait: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } // 2. 讨伐部队 if p.checkMeetMarchList(&tileTable.CrusadeList, playerID, leagueID) { clog.Debugf("[isTileMeet] crusade: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } // 3. 攻城部队 if p.checkMeetMarchList(&tileTable.SiegeList, playerID, leagueID) { clog.Debugf("[isTileMeet] siege: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } // 4. 停留部队 if p.checkMeetMarchList(&tileTable.StayList, playerID, leagueID) { clog.Debugf("[isTileMeet] stay: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } // 5. 行军部队 if p.checkMeetMarchList(&tileTable.MarchList, playerID, leagueID) { clog.Debugf("[isTileMeet] march: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, true } // last. 攻击部队 if p.checkMeetMarchList(&tileTable.AttackList, playerID, leagueID) { clog.Debugf("[isTileMeet] attack: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID) return true, false } return false, false } func (p *actor) checkMeetMarchList(marchList *mapTypes.MarchList, playerID, leagueID int64) bool { for _, pair := range marchList.List { thisLeagueID := p.getMarchLeagueID(pair.Key, pair.Value.IsLegion) // 同联盟,不碰撞 if p.isSameLeague(thisLeagueID, leagueID) { return false } // 自己不跟自己碰撞 if !pair.Value.IsLegion { marchTable := p.MarchTables.GetValue(pair.Key) return marchTable.PlayerID != playerID } // TODO: 与军团的碰撞 } return false } func (p *actor) addTileProcess(tileTable *db.LogicTileTable) { p.processTiles.Put(tileTable.TileID, tileTable) } func (p *actor) getChildTiles(tileTable *db.LogicTileTable) map[int32]*db.LogicTileTable { var childTiles = map[int32]*db.LogicTileTable{} for _, point := range tileTable.ChildPoints() { tileID, ok := data.PointToTileID(point.X, point.Y) if !ok { continue } childTileTable := p.TileTables.GetValue(tileID) childTiles[tileID] = childTileTable } return childTiles } func (p *actor) buildMapBattlePush(tileTable *db.LogicTileTable, battle *dbLogic.TileBattle) *pb.MapBattlePush { push := &pb.MapBattlePush{ AttackMarchObjectID: battle.AttackObjectID, DefendMarchObjectID: battle.DefendObjectID, BattleType: int32(battle.BattleType), IsWin: battle.BattleResult != nil && battle.BattleResult.IsWin, Point: tileTable.Point.ToProto(), } // 可能是军团 if attacker, ok := p.MarchTables.Get(battle.AttackObjectID); ok { push.AttackHeroConfigID = attacker.GetMainHeroConfigID() push.AttackMarchView = attacker.ToMarchView() } if defender, ok := p.MarchTables.Get(battle.DefendObjectID); ok { push.DefendHeroConfigID = defender.GetMainHeroConfigID() push.DefendMarchView = defender.ToMarchView() } else if battle.BattleResult != nil { if len(battle.BattleResult.Data.Tile.Monsters) > 0 { push.DefendMonsterGroupID = battle.BattleResult.Data.Tile.Monsters[0] } } return push } // tileGarrionRetreat 地块驻守部队撤退 func (p *actor) tileGarrionRetreat(tileTable *db.LogicTileTable) { if tileTable.GarrisonList.Size() == 0 { return } for _, pair := range tileTable.GarrisonList.List { // TODO: 如果 MarchTable 是战斗状态,应该设置一个标记,在战斗结束后再检查了标记再撤退 marchTable := p.MarchTables.GetValue(pair.Key) p.addMarchToTileRetreat(marchTable, tileTable, enum.RetreatType_Must, false) } tileTable.GarrisonList.Clear() tileTable.IsDataChanged = true } func (p *actor) isSameLeague(leagueID1, leagueID2 int64) bool { return leagueID1 == leagueID2 && leagueID1 != 0 }