actor_tile.go 16 KB

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