actor_tile.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. package mapLogic
  2. import (
  3. capp "f1-game/internal/cherry_app"
  4. "f1-game/internal/code"
  5. "f1-game/internal/constant"
  6. "f1-game/internal/data"
  7. "f1-game/internal/enum"
  8. "f1-game/internal/event"
  9. nameRemote "f1-game/internal/name/remote"
  10. "f1-game/internal/pb"
  11. mapCall "f1-game/nodes/map/internal/call"
  12. "f1-game/nodes/map/internal/db"
  13. dbLogic "f1-game/nodes/map/internal/db/data/logic"
  14. mapTypes "f1-game/nodes/map/internal/types"
  15. clog "github.com/cherry-game/cherry/logger"
  16. )
  17. func (p *actor) initTileBattleFunc() {
  18. p.tileBattleResultFunc = map[enum.TileBattleType]tileBattleResultFunc{
  19. enum.TileBattle_PVP_Meet: p.onTileMeetBattleResult,
  20. enum.TileBattle_PVP_Occupy: p.onTileOccupyBattleResult,
  21. enum.TileBattle_PVE_Occupy: p.onTileOccupyBattleResult,
  22. enum.TileBattle_PVE_SiegeGarrison: p.onTileSiegeGarrisonResult,
  23. enum.TileBattle_PVE_SiegeDurability: p.onTileSiegeDurabilityResult,
  24. enum.TileBattle_PVP_Siege: p.onTileSiegeDefendResult,
  25. enum.TileBattle_PVE_SnatchGarrison: p.onTileSnatchGarrisonResult,
  26. enum.TileBattle_PVP_Snatch: p.onTileSnatchDefendResult,
  27. enum.TileBattle_PVE_Crusade: p.onTileCrusadeBattleResult,
  28. enum.TileBattle_PVE_Snatch: p.onTileSnatchResult,
  29. enum.TileBattle_PVN_Snatch: p.onTileSnatchResult,
  30. }
  31. }
  32. func (p *actor) initTileProcess() {
  33. for _, tileTable := range p.TileTables {
  34. // 需要处理的地块
  35. if tileTable.IsNeedToProcess() {
  36. p.addTileProcess(tileTable)
  37. }
  38. }
  39. }
  40. func (p *actor) tileTimer() {
  41. p.updateTime()
  42. // 只处理需要处理的地块
  43. for _, tileTable := range p.processTiles {
  44. p.tileTicker(tileTable)
  45. }
  46. }
  47. func (p *actor) tileTicker(tileTable *db.LogicTileTable) {
  48. // 如果地块上有驻地部队,要进行范围检查
  49. p.tileGarrisonCheck(tileTable)
  50. // 处理地块
  51. p.tileProcess(tileTable)
  52. // 检查是否需要继续处理
  53. p.tileCheck(tileTable)
  54. // 保存数据
  55. p.tileSave(tileTable)
  56. }
  57. func (p *actor) tileCheck(tileTable *db.LogicTileTable) {
  58. // 不再需要处理
  59. if !tileTable.IsNeedToProcess() {
  60. tileTable.IsNeedProcess = false
  61. tileTable.IsDataChanged = true
  62. p.processTiles.Remove(tileTable.TileID)
  63. }
  64. }
  65. func (p *actor) tileSave(tileTable *db.LogicTileTable) {
  66. // 保存数据,推送通知
  67. if tileTable.IsDataChanged {
  68. tileTable.IsDataChanged = false
  69. tileTable.Save2Queue()
  70. }
  71. }
  72. func (p *actor) tileProcess(tileTable *db.LogicTileTable) {
  73. // 处理撤退的队伍
  74. if tileTable.HasRetreater() {
  75. p.tileRetreat(tileTable)
  76. }
  77. if tileTable.HasBattleQueue() {
  78. p.tileBattleQueue(tileTable)
  79. }
  80. // 处理地块的战斗
  81. if tileTable.HasBattleHandle() {
  82. p.tileBattle(tileTable)
  83. }
  84. // 处理地块的攻城
  85. if tileTable.HasSiege() {
  86. p.tileSiege(tileTable)
  87. }
  88. // 处理地块的掠夺
  89. if tileTable.HasSnatch() {
  90. p.tileSnatch(tileTable)
  91. }
  92. // 处理地块的占领
  93. if tileTable.HasOccupyWait() {
  94. p.tileOccupy(tileTable)
  95. }
  96. }
  97. // tileGarrisonCheck 地块驻守部队碰撞检测
  98. func (p *actor) tileGarrisonCheck(tileTable *db.LogicTileTable) {
  99. if !tileTable.HasGarrison() {
  100. return
  101. }
  102. checkTiles := map[int32]*db.LogicTileTable{}
  103. if tileTable.BuildObjectID > 0 {
  104. buildTable := p.BuildTables.GetValue(tileTable.BuildObjectID)
  105. checkTiles = buildTable.GetTileTableMap(p.TileTables, false)
  106. } else if tileTable.CastleObjectID > 0 {
  107. castleTable := p.CastleTables.GetValue(tileTable.CastleObjectID)
  108. checkTiles = castleTable.GetTileTableMap(p.TileTables, false)
  109. }
  110. // 驻守应该是不用是检查自己的,他会先被其它部队碰撞掉
  111. // TODO 不清楚,先看看会不会有问题
  112. if len(checkTiles) == 0 {
  113. return
  114. }
  115. marchList := tileTable.GarrisonList.NextCount(data.Const.MapMeetBattleQueue, mapTypes.ReadyFilter(p.nowMillis))
  116. for _, pair := range marchList {
  117. attackObjectID, attackMarchInfo := pair.Key, pair.Value
  118. attackLeagueID := p.getMarchLeagueID(attackObjectID, attackMarchInfo.IsLegion)
  119. for _, checkTile := range checkTiles {
  120. // 有攻击队列说明正在打遭遇战,跳过
  121. if checkTile.HasAttacker() {
  122. delete(checkTiles, checkTile.TileID)
  123. continue
  124. }
  125. // 提交了多场战斗,跳过
  126. if checkTile.BattleQueue.Size() >= int(data.Const.MapMeetBattleQueue) {
  127. delete(checkTiles, checkTile.TileID)
  128. continue
  129. }
  130. defendObjectID, defendMarchInfo, ok := checkTile.GetDefender()
  131. // 没有防守方,跳过
  132. if !ok {
  133. delete(checkTiles, checkTile.TileID)
  134. continue
  135. }
  136. // 同一阵营,那么格子里面都是自己人,跳过
  137. defendLeagueID := p.getMarchLeagueID(defendObjectID, defendMarchInfo.IsLegion)
  138. if p.isSameLeague(attackLeagueID, defendLeagueID) {
  139. delete(checkTiles, checkTile.TileID)
  140. continue
  141. }
  142. // 驻守部队是进攻方,要保存状态
  143. attacker := p.MarchTables.GetValue(attackObjectID)
  144. attacker.SaveState(p.nowMillis)
  145. p.buildTilePVPBattle(tileTable, enum.TileBattle_PVP_Meet, attackObjectID, defendObjectID, attackMarchInfo, defendMarchInfo)
  146. }
  147. }
  148. }
  149. func (p *actor) tileBattle(tileTable *db.LogicTileTable) {
  150. p.onTileBattleStart(tileTable)
  151. }
  152. func (p *actor) tileBattleQueue(tileTable *db.LogicTileTable) {
  153. // 处理等待中的战斗
  154. p.onTileBattleWait(tileTable)
  155. // 处理已经有结果的战斗
  156. p.onTileBattleResult(tileTable)
  157. }
  158. // onTileBattleStart 执行战斗开始处理
  159. // 1. 打遭遇战
  160. // 2. 打守军战
  161. func (p *actor) onTileBattleStart(tileTable *db.LogicTileTable) {
  162. if tileTable.IsMeetting && !tileTable.HasMoreMeetBattles() {
  163. tileTable.MarkMeetingBattle(false)
  164. p.onTileMeetBattleEnd(tileTable)
  165. }
  166. if tileTable.HasMoreMeetBattles() {
  167. tileTable.MarkMeetingBattle(true)
  168. p.onTileMeetBattleStart(tileTable)
  169. return
  170. }
  171. if tileTable.HasOccupy() {
  172. p.onTileOccupyBattleStart(tileTable)
  173. return
  174. }
  175. if tileTable.HasCrusade() {
  176. p.onTileCrusadeBattleStart(tileTable)
  177. return
  178. }
  179. }
  180. func (p *actor) tileSiege(tileTable *db.LogicTileTable) {
  181. p.onTileSiegeStart(tileTable)
  182. }
  183. func (p *actor) tileSnatch(tileTable *db.LogicTileTable) {
  184. buildTable, ok := tileTable.GetBuild(p.TileTables, p.BuildTables)
  185. if !ok {
  186. return
  187. }
  188. siegeTable := p.SiegeTables.GetValue(buildTable.ObjectID)
  189. if siegeTable.RemainGarrisonArmies() > 0 {
  190. // 有驻城部队,先打驻城部队
  191. p.onTileSnatchGarrisonStart(tileTable, buildTable, siegeTable)
  192. } else if p.hasAutoDefend(siegeTable) {
  193. // 看看有无攻城城防部队:调动的,征兵的,自动守城的
  194. p.onTileSnatchDefendStart(tileTable, buildTable)
  195. } else {
  196. p.onTileSnatchResStart(tileTable, buildTable, siegeTable)
  197. }
  198. tileTable.IsDataChanged = true
  199. }
  200. // onTileBattleWait 等待战斗结果
  201. // 当有战斗结束响应之后, 根据输赢执行继续战斗还是撤退
  202. func (p *actor) onTileBattleWait(tileTable *db.LogicTileTable) {
  203. for _, battleInfo := range tileTable.BattleQueue {
  204. if battleInfo.IsTimeout(p.nowMillis, 10000) {
  205. p.onTileBattleTimeout(tileTable, battleInfo)
  206. }
  207. }
  208. }
  209. // onTileBattleResult 处理战斗结果
  210. func (p *actor) onTileBattleResult(tileTable *db.LogicTileTable) {
  211. for _, battleInfo := range tileTable.BattleQueue {
  212. if battleInfo.BattleResult == nil {
  213. continue
  214. }
  215. clog.Debugf("tile battle result: TileID = %v, AttackObjectID = %v, BattleType = %v, win = %v",
  216. tileTable.TileID, battleInfo.AttackObjectID, enum.GetBattleTypeName(battleInfo.BattleType),
  217. battleInfo.BattleResult.IsWin)
  218. if f, ok := p.tileBattleResultFunc[battleInfo.BattleType]; ok {
  219. f(tileTable, battleInfo)
  220. // 战斗推送数据缓存到 map
  221. push := p.buildMapBattlePush(tileTable, battleInfo)
  222. updateID := p.newUpdateID()
  223. aoiUpdateBattleMap.Put(updateID, push)
  224. // AOI 更新
  225. mapCall.AOI.UpdateWithID(push.AttackMarchObjectID, enum.ObjectType_March, updateID, nameRemote.MapLogic_AOIUpdateBattle)
  226. } else {
  227. clog.Errorf("tile battle result func not found: TileID = %v, BattleType = %v",
  228. tileTable.TileID, battleInfo.BattleType)
  229. }
  230. tileTable.RemoveBattleQueue(battleInfo.BattleID)
  231. }
  232. }
  233. // tilePVPBattleResult 行军战斗结果处理
  234. func (p *actor) tilePVPBattleResult(winner *db.LogicMarchTable, winnerMarchInfo *mapTypes.TileMarchInfo, loser *db.LogicMarchTable, loserMarchInfo *mapTypes.TileMarchInfo, tileTable *db.LogicTileTable) {
  235. if loserMarchInfo.TileID == tileTable.TileID {
  236. p.addMarchToTileRetreat(loser, tileTable, enum.RetreatType_Rout, false)
  237. tileTable.RemoveDefendType(loser.ObjectID, loserMarchInfo.DefendType)
  238. } else {
  239. otherTile := p.TileTables.GetValue(loserMarchInfo.TileID)
  240. p.addMarchToTileRetreat(loser, otherTile, enum.RetreatType_Rout, false)
  241. otherTile.RemoveDefendType(loser.ObjectID, loserMarchInfo.DefendType)
  242. }
  243. winnerMarchInfo.LogicEndTime = p.nowMillis + constant.MapLeastBattleWaitTime
  244. winnerMarchInfo.MarkBattleWait(false)
  245. // 驻守列表和自动防守列表恢复状态
  246. if winnerMarchInfo.DefendType == enum.TileDefend_Garrison || winnerMarchInfo.DefendType == enum.TileDefend_AutoDefend {
  247. marchTable := p.MarchTables.GetValue(winner.ObjectID)
  248. p.restoreMarchState(marchTable)
  249. }
  250. }
  251. // tileRetreat 撤退
  252. func (p *actor) tileRetreat(tileTable *db.LogicTileTable) {
  253. retreatNum := tileTable.RetreatNumForOneTick()
  254. for range retreatNum {
  255. retreatInfo := tileTable.RestreatList.Remove(0)
  256. if retreatInfo == nil {
  257. break
  258. }
  259. var (
  260. marchObjectID = retreatInfo.MarchObjectID
  261. marchState = enum.GetMarchStateByRetreatType(retreatInfo.RetreatType)
  262. isRemoveRedeploy = retreatInfo.IsRemoveRedeploy
  263. )
  264. p.startMarchIDBack(marchObjectID, marchState, isRemoveRedeploy)
  265. }
  266. if retreatNum > 0 {
  267. tileTable.IsDataChanged = true
  268. }
  269. }
  270. // tileOccupy 处理地块占领事件
  271. func (p *actor) tileOccupy(tileTable *db.LogicTileTable) {
  272. // 按照占领时间来排序,拿出下一个不处于战斗等待状态的
  273. marchObjectID, tileMarchInfo, ok := tileTable.OccupyWaitList.Next(mapTypes.ReadyFilter(p.nowMillis))
  274. if !ok {
  275. return
  276. }
  277. // 时间未到
  278. if tileMarchInfo.LogicEndTime > p.nowMillis {
  279. return
  280. }
  281. // 等待处理中
  282. marchTable := p.MarchTables.GetValue(marchObjectID)
  283. var occupySuc bool
  284. resTable, ok := p.ResTables.Get(marchTable.TargetObjectID)
  285. if ok {
  286. retCode := resTable.CanOccupy(p.nowMillis, marchTable.PlayerID, marchTable.LeagueID)
  287. if code.IsOK(retCode) {
  288. occupySuc = true
  289. } else {
  290. clog.Debugf("res occupy fail: MarchObjectID = %v, ResObjectID = %v, Code = %v", marchObjectID, resTable.ObjectID, retCode)
  291. }
  292. }
  293. // 判断占领上限
  294. watcherTable := p.WatcherTables.GetValue(marchTable.PlayerID)
  295. if len(watcherTable.Res) >= data.Const.MapResOccupyLimit {
  296. clog.Debugf("Res occupy fail: num limit [MarchObjectId = %d, ResObjectID = %d]", marchObjectID, resTable.ObjectID)
  297. occupySuc = false
  298. }
  299. // 从占领队列移除
  300. tileTable.RemoveOccupyWait(marchTable.ObjectID)
  301. // 部队撤退
  302. p.addMarchToRetreat(marchTable, enum.RetreatType_Retreat, false)
  303. // 如果占领失败
  304. if !occupySuc {
  305. return
  306. }
  307. p.removeWatcherRes(resTable)
  308. // 设置拥有者
  309. resTable.SetOwner(marchTable.PlayerID, marchTable.LeagueID)
  310. // 设置免战时间
  311. resTable.SetInvincibleEndTime(p.nowMillis + data.Const.GetResOccupyInvincibleTime())
  312. // 重置战报组
  313. resTable.ResetReport()
  314. // 重置采集时间
  315. resTable.ResetGatherTime(p.nowMillis)
  316. // 推送资源点
  317. p.UpdateObjectFull(resTable.ObjectID, resTable.Type)
  318. watcherTable.AddRes(resTable.ObjectID)
  319. // 推送
  320. p.PushOwnerMapObjectAdd(resTable.PlayerID, resTable.ToOwnerMapObjectPb())
  321. clog.Debugf("march occupy wait success. MarchID = %v, ResID = %v, Point = %v",
  322. marchTable.ObjectID, resTable.ObjectID, resTable.Point)
  323. capp.PostEvent(event.NewTileOccupy(marchTable.PlayerID, resTable.PlayerID, resTable.ConfigID, resTable.ResLevel))
  324. // 推送资源点占领事件
  325. capp.PostEvent(event.NewResOccupy(resTable.PlayerID, &event.MapResOccupyData{
  326. ObjectID: resTable.ObjectID,
  327. TileID: resTable.GetTileID(),
  328. ConfigID: resTable.ConfigID,
  329. InLeagueArea: p.isSameLeague(tileTable.LeagueID, marchTable.LeagueID),
  330. }))
  331. }
  332. // isTileMeet 是否在 tile 上碰撞
  333. func (p *actor) isTileMeet(tileTable *db.LogicTileTable, playerID, leagueID int64) (isMet bool, isAttack bool) {
  334. // 碰撞顺序
  335. // 1. 驻守部队
  336. if p.checkMeetMarchList(&tileTable.GarrisonList, playerID, leagueID) {
  337. clog.Debugf("[isTileMeet] garrison: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  338. return true, true
  339. }
  340. // 2. 占领部队
  341. if p.checkMeetMarchList(&tileTable.OccupyList, playerID, leagueID) {
  342. clog.Debugf("[isTileMeet] occupy: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  343. return true, true
  344. }
  345. if p.checkMeetMarchList(&tileTable.OccupyWaitList, playerID, leagueID) {
  346. clog.Debugf("[isTileMeet] occupy wait: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  347. return true, true
  348. }
  349. // 2. 讨伐部队
  350. if p.checkMeetMarchList(&tileTable.CrusadeList, playerID, leagueID) {
  351. clog.Debugf("[isTileMeet] crusade: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  352. return true, true
  353. }
  354. // 3. 攻城部队
  355. if p.checkMeetMarchList(&tileTable.SiegeList, playerID, leagueID) {
  356. clog.Debugf("[isTileMeet] siege: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  357. return true, true
  358. }
  359. // 4. 停留部队
  360. if p.checkMeetMarchList(&tileTable.StayList, playerID, leagueID) {
  361. clog.Debugf("[isTileMeet] stay: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  362. return true, true
  363. }
  364. // 5. 行军部队
  365. if p.checkMeetMarchList(&tileTable.MarchList, playerID, leagueID) {
  366. clog.Debugf("[isTileMeet] march: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  367. return true, true
  368. }
  369. // last. 攻击部队
  370. if p.checkMeetMarchList(&tileTable.AttackList, playerID, leagueID) {
  371. clog.Debugf("[isTileMeet] attack: TileID = %v, PlayerID = %d, LeagueID = %v", tileTable.TileID, playerID, leagueID)
  372. return true, false
  373. }
  374. return false, false
  375. }
  376. func (p *actor) checkMeetMarchList(marchList *mapTypes.MarchList, playerID, leagueID int64) bool {
  377. for _, pair := range marchList.List {
  378. thisLeagueID := p.getMarchLeagueID(pair.Key, pair.Value.IsLegion)
  379. // 同联盟,不碰撞
  380. if p.isSameLeague(thisLeagueID, leagueID) {
  381. return false
  382. }
  383. // 自己不跟自己碰撞
  384. if !pair.Value.IsLegion {
  385. marchTable := p.MarchTables.GetValue(pair.Key)
  386. return marchTable.PlayerID != playerID
  387. }
  388. // TODO: 与军团的碰撞
  389. }
  390. return false
  391. }
  392. func (p *actor) addTileProcess(tileTable *db.LogicTileTable) {
  393. p.processTiles.Put(tileTable.TileID, tileTable)
  394. }
  395. func (p *actor) getChildTiles(tileTable *db.LogicTileTable) map[int32]*db.LogicTileTable {
  396. var childTiles = map[int32]*db.LogicTileTable{}
  397. for _, point := range tileTable.ChildPoints() {
  398. tileID, ok := data.PointToTileID(point.X, point.Y)
  399. if !ok {
  400. continue
  401. }
  402. childTileTable := p.TileTables.GetValue(tileID)
  403. childTiles[tileID] = childTileTable
  404. }
  405. return childTiles
  406. }
  407. func (p *actor) buildMapBattlePush(tileTable *db.LogicTileTable, battle *dbLogic.TileBattle) *pb.MapBattlePush {
  408. push := &pb.MapBattlePush{
  409. AttackMarchObjectID: battle.AttackObjectID,
  410. DefendMarchObjectID: battle.DefendObjectID,
  411. BattleType: int32(battle.BattleType),
  412. IsWin: battle.BattleResult != nil && battle.BattleResult.IsWin,
  413. Point: tileTable.Point.ToProto(),
  414. }
  415. // 可能是军团
  416. if attacker, ok := p.MarchTables.Get(battle.AttackObjectID); ok {
  417. push.AttackHeroConfigID = attacker.GetMainHeroConfigID()
  418. push.AttackMarchView = attacker.ToMarchView()
  419. }
  420. if defender, ok := p.MarchTables.Get(battle.DefendObjectID); ok {
  421. push.DefendHeroConfigID = defender.GetMainHeroConfigID()
  422. push.DefendMarchView = defender.ToMarchView()
  423. } else if battle.BattleResult != nil {
  424. if len(battle.BattleResult.Data.Tile.Monsters) > 0 {
  425. push.DefendMonsterGroupID = battle.BattleResult.Data.Tile.Monsters[0]
  426. }
  427. }
  428. return push
  429. }
  430. // tileGarrionRetreat 地块驻守部队撤退
  431. func (p *actor) tileGarrionRetreat(tileTable *db.LogicTileTable) {
  432. if tileTable.GarrisonList.Size() == 0 {
  433. return
  434. }
  435. for _, pair := range tileTable.GarrisonList.List {
  436. // TODO: 如果 MarchTable 是战斗状态,应该设置一个标记,在战斗结束后再检查了标记再撤退
  437. marchTable := p.MarchTables.GetValue(pair.Key)
  438. p.addMarchToTileRetreat(marchTable, tileTable, enum.RetreatType_Must, false)
  439. }
  440. tileTable.GarrisonList.Clear()
  441. tileTable.IsDataChanged = true
  442. }
  443. func (p *actor) isSameLeague(leagueID1, leagueID2 int64) bool {
  444. return leagueID1 == leagueID2 && leagueID1 != 0
  445. }