actor_tile.go 16 KB

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