actor.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  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. nameActor "f1-game/internal/name/actor"
  9. nameRoute "f1-game/internal/name/route"
  10. "f1-game/internal/pb"
  11. "f1-game/internal/sessions"
  12. "f1-game/internal/types"
  13. "f1-game/nodes/map/internal/db"
  14. dbLogic "f1-game/nodes/map/internal/db/data/logic"
  15. mapTypes "f1-game/nodes/map/internal/types"
  16. ctime "github.com/cherry-game/cherry/extend/time"
  17. clog "github.com/cherry-game/cherry/logger"
  18. "github.com/cherry-game/cherry/net/parser/pomelo"
  19. )
  20. type (
  21. actor struct {
  22. pomelo.ActorBase
  23. *db.LogicTables
  24. now ctime.CherryTime
  25. nowMillis int64
  26. updateID uint32
  27. processTiles types.Map[int32, *db.LogicTileTable] // 需要处理的地块对象,key: tileID
  28. marchCheckFuncs map[enum.MarchType]marchCheckFunc // 行军调动前检查方法,key: MarchType
  29. marchTypeFuncs map[enum.MarchType]marchTypeFunc // 行军指令处理方法,key: MarchType
  30. marchStopFuncs map[enum.MarchState]marchStopFunc // 行军中止指令处理方法,key:MarchState
  31. marchRetreatFuncs map[enum.MarchState]marchRetreatFunc // 各种状态下行军撤退指令的处理方法,key:MarchState
  32. tileBattleResultFunc map[enum.TileBattleType]tileBattleResultFunc // 地块战斗结果处理方法,key:TileBattleType
  33. battleTimeoutFuncs map[enum.TileBattleType]battleTimeoutFunc // 地块战斗超时处理方法,key:TileBattleType
  34. occupyingCastles types.Map[int64, *db.LogicCastleTable] // 占领中的系统城
  35. bornPos mapTypes.MapBornPos
  36. bornLimit types.Map[int32, int32]
  37. }
  38. marchCheckFunc func(marchTable *db.LogicMarchTable, targetObjectID int64, point *pb.Point) int32
  39. marchTypeFunc func(*db.LogicMarchTable)
  40. marchStopFunc func(*db.LogicMarchTable)
  41. marchRetreatFunc func(*db.LogicMarchTable)
  42. tileBattleResultFunc func(*db.LogicTileTable, *dbLogic.TileBattle)
  43. battleTimeoutFunc func(tileTable *db.LogicTileTable, battleInfo *dbLogic.TileBattle)
  44. )
  45. func NewActor(mapTables *db.LogicTables, bornPos mapTypes.MapBornPos, bornLimit types.Map[int32, int32]) *actor {
  46. actor := &actor{
  47. LogicTables: mapTables,
  48. processTiles: types.NewMap[int32, *db.LogicTileTable](),
  49. occupyingCastles: types.NewMap[int64, *db.LogicCastleTable](),
  50. bornPos: bornPos,
  51. bornLimit: bornLimit,
  52. }
  53. actor.updateTime()
  54. actor.initMarchCheckFuncs()
  55. actor.initMarchTypeFuncs()
  56. actor.initMarchStopFuncs()
  57. actor.initMarchRetreatFuncs()
  58. actor.initTileBattleFunc()
  59. actor.initBattleTimeoutFuncs()
  60. actor.initMonster()
  61. actor.initTileProcess()
  62. actor.initOccupyingCastles()
  63. return actor
  64. }
  65. func (p *actor) AliasID() string {
  66. return nameActor.Map_Logic
  67. }
  68. func (p *actor) OnInit() {
  69. p.onInitRemote()
  70. p.onInitAOI()
  71. p.Timer().Add(constant.Duration_March, p.marchTimer)
  72. p.Timer().Add(constant.Duration_Tile, p.tileTimer)
  73. p.Timer().Add(constant.Duration_Res, p.resTimer)
  74. p.Timer().Add(constant.Duration_Build, p.buildTimer)
  75. p.Timer().Add(constant.Duration_Castle, p.castleTimer)
  76. p.Timer().Add(constant.Duration_Monster, p.monsterTimer)
  77. }
  78. // updateTime 全局时间更新
  79. func (p *actor) updateTime() {
  80. p.now = ctime.Now()
  81. p.nowMillis = p.now.UnixMilli()
  82. }
  83. func (p *actor) newUpdateID() uint32 {
  84. // 单线程保证了原子性
  85. p.updateID++
  86. return p.updateID
  87. }
  88. // checkBuildable 检查是否可以建造
  89. // TODO: 这个方法要重写,使 build 和 buildLeague 都可以调用
  90. func (p *actor) checkBuildable(playerID, leagueID int64, x, y int32, buildRow *data.MapBuildRow) int32 {
  91. offsetPoints := buildRow.GetCoverOffsetPoints(x, y, true)
  92. for _, point := range offsetPoints {
  93. if errCode := p.checkPointAvailiable(point.X, point.Y, playerID, leagueID, buildRow); code.IsFail(errCode) {
  94. return errCode
  95. }
  96. }
  97. return code.OK
  98. // 其它通用检查,是否有阻挡等
  99. }
  100. func (p *actor) checkPointAvailiable(x, y int32, playerID, leagueID int64, buildRow *data.MapBuildRow) int32 {
  101. if data.MapData().IsPointBlock(x, y) {
  102. clog.Debugf("point (%d, %d) not walkable.", x, y)
  103. return code.MapTile_NotWalkable
  104. }
  105. tileID, ok := data.PointToTileID(x, y)
  106. if !ok {
  107. return code.MapGetPointFail
  108. }
  109. tileTable := p.TileTables.GetValue(tileID)
  110. if tileTable.BuildObjectID > 0 || tileTable.CastleObjectID > 0 {
  111. clog.Debugf("point has block. Point = (%d, %d)", x, y)
  112. return code.MapTile_HasBlock
  113. }
  114. switch buildRow.BuildType {
  115. case enum.BuildType_Player:
  116. // 如果有资源,玩家可以建在自己的资源上
  117. if resTable, ok := p.getResTableInTile(tileTable); ok {
  118. if resTable.PlayerID > 0 && resTable.PlayerID != playerID {
  119. return code.MapTile_HasBlock
  120. }
  121. }
  122. case enum.BuildType_League:
  123. // 如果有资源,不能建在玩家的资源上
  124. if resTable, ok := p.getResTableInTile(tileTable); ok {
  125. if resTable.PlayerID > 0 {
  126. return code.MapTile_HasBlock
  127. }
  128. }
  129. // TODO: 有些同盟建筑是可以建在盟友的资源上的
  130. default:
  131. return code.MapBuild_NotBuildable
  132. }
  133. return code.OK
  134. }
  135. func (p *actor) getResTableInTile(tileTable *db.LogicTileTable) (*db.LogicResTable, bool) {
  136. if tileTable.ResObjectID == 0 {
  137. return nil, false
  138. }
  139. resTable, ok := p.ResTables.Get(tileTable.ResObjectID)
  140. if !ok {
  141. clog.Warnf("[getResTableInTile] A bug: Tile(%d) get no Res(%d)", tileTable.TileID, tileTable.ResObjectID)
  142. return nil, false
  143. }
  144. return resTable, true
  145. }
  146. func (p *actor) getMonsterTableInTile(tileTable *db.LogicTileTable) (*db.LogicMonsterTable, bool) {
  147. if tileTable.MonsterObjectID == 0 {
  148. return nil, false
  149. }
  150. monsterTable, ok := p.MonsterTables.Get(tileTable.MonsterObjectID)
  151. if !ok {
  152. clog.Warnf("[getMonsterTableInTile] A bug: Tile(%d) get no Monster(%d)", tileTable.TileID, tileTable.MonsterObjectID)
  153. return nil, false
  154. }
  155. return monsterTable, true
  156. }
  157. // getMarchSpeed 获取行军速度
  158. func (p *actor) getMarchSpeed(marchTable *db.LogicMarchTable, marchType enum.MarchType, marchState enum.MarchState) int64 {
  159. var speed int64
  160. row, ok := data.MapMarch.GetByMarchType(marchType)
  161. if !ok {
  162. clog.Errorf("get march speed failed, marchType = %d", marchType)
  163. return constant.MapMoveSpeedMin
  164. }
  165. // 不能小于最小移动速度
  166. speed = max(row.Speed*ctime.MillisecondsPerSecond, constant.MapMoveSpeedMin)
  167. // 测试用,加移速
  168. if value, ok := data.GameDebug.GetDebugValue(constant.GameDebug_MapMarchSpeedUp); ok {
  169. speed >>= value
  170. }
  171. return speed
  172. }
  173. func (p *actor) convertPath(path []*pb.Point) types.Points {
  174. points := make(types.Points, 0, len(path))
  175. for _, point := range path {
  176. points.Add(point.X, point.Y)
  177. }
  178. return points
  179. }
  180. // newMainCity 创建玩家主城
  181. func (p *actor) newMainCity(stateID int32, playerID, leagueID int64) (*db.LogicBuildTable, int32) {
  182. // 随机出生点
  183. tileID, ok := p.bornPos.Random(stateID)
  184. if !ok {
  185. clog.Debugf("[newMainCity] random born fail: stateID = %d, playerID = %d, leagueID = %d", stateID, playerID, leagueID)
  186. return nil, code.MapBornPoint_NotFound
  187. }
  188. buildRow, ok := data.MapBuild.GetByConfigID(constant.MapBuild_MainCity)
  189. if !ok {
  190. clog.Debugf("[newMainCity] config not found: stateID = %d, playerID = %d, leagueID = %d", stateID, playerID, leagueID)
  191. return nil, code.ConfigNotFound_MapBuild
  192. }
  193. x, y := data.TileIDToPoint(tileID)
  194. offsets := buildRow.GetCoverOffsetPoints(x, y, false)
  195. // 生成建筑
  196. mainCity := db.NewLogicBuildTable(db.NewGUID(), capp.NodeID(), x, y, constant.MapBuild_MainCity, playerID, leagueID, offsets...)
  197. mainCity.LoadConfig(buildRow)
  198. p.buildAdd(mainCity)
  199. siegeTable := db.NewLogicSiegeTable(mainCity.ObjectID, mainCity.NodeID, mainCity.ConfigID)
  200. siegeTable.SetSiegeParams(buildRow.BuildingDurability, buildRow.Durability, buildRow.GarrisonArmies, buildRow.DefendArmy)
  201. siegeTable.SiegeReset(false)
  202. p.SiegeTables.Put(siegeTable.ObjectID, siegeTable)
  203. p.bornPos.Remove(tileID)
  204. clog.Infof("new main city success: stateID = %d, playerId = %d, leagueID = %d, point = %v",
  205. stateID, playerID, leagueID, mainCity.Point)
  206. return mainCity, code.OK
  207. }
  208. // changeMainCity 改变主城(随机出生点)
  209. func (p *actor) changeMainCity(stateID int32, mainCityObjectID int64) (*db.LogicBuildTable, int32) {
  210. mainCity, ok := p.BuildTables.Get(mainCityObjectID)
  211. if !ok {
  212. return nil, code.MapObjectNotFound
  213. }
  214. // 随机出生点
  215. tileID, ok := p.bornPos.Random(stateID)
  216. if !ok {
  217. clog.Debugf("[changeMainCity] random born fail: stateID = %d, mainCityObjectID = %d", stateID, mainCityObjectID)
  218. return nil, code.MapBornPoint_NotFound
  219. }
  220. buildRow, ok := data.MapBuild.GetByConfigID(constant.MapBuild_MainCity)
  221. if !ok {
  222. clog.Debugf("[changeMainCity] config not found: stateID = %d, mainCityObjectID = %d", stateID, mainCityObjectID)
  223. return nil, code.ConfigNotFound_MapBuild
  224. }
  225. x, y := data.TileIDToPoint(tileID)
  226. offsets := buildRow.GetCoverOffsetPoints(x, y, false)
  227. // 先移除主城
  228. p.buildRemove(mainCity)
  229. // 刷新主城位置
  230. mainCity.Point.SetValue(x, y)
  231. mainCity.OffsetPoints = offsets
  232. mainCity.IsHide = false // 无论怎么都要显示
  233. mainCity.IsDataChanged = true
  234. // 把主城血量刷新
  235. siegeTable := p.SiegeTables.GetValue(mainCity.ObjectID)
  236. siegeTable.SiegeReset(false)
  237. // 再添加主城
  238. p.buildAdd(mainCity)
  239. p.bornPos.Remove(tileID)
  240. clog.Infof("change main city success: stateID = %d, mainCityObjectID = %d, point = %v", stateID, mainCityObjectID, mainCity.Point)
  241. return mainCity, code.OK
  242. }
  243. // TODO: 把这个封装到 LogicTables GetObjectLeagueID 方法
  244. func (p *actor) getMarchLeagueID(objectID int64, isLegion bool) int64 {
  245. if isLegion {
  246. if legionTable, ok := p.LegionTables.Get(objectID); ok {
  247. return legionTable.LeagueID
  248. }
  249. }
  250. if marchTable, ok := p.MarchTables.Get(objectID); ok {
  251. return marchTable.LeagueID
  252. }
  253. return 0
  254. }
  255. func (p *actor) initOccupyingCastles() {
  256. for _, castleTable := range p.CastleTables {
  257. if castleTable.LeagueID != 0 && castleTable.CastleConfig.CastleType == pb.SysObjType_so_castle {
  258. p.occupyingCastles.Put(castleTable.ObjectID, castleTable)
  259. }
  260. }
  261. }
  262. func (p *actor) PushOwnerMapObjectAdd(playerID int64, object *pb.OwnerMapObject) {
  263. sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_OwnerMapObjectAdd, object)
  264. }
  265. func (p *actor) PushOwnerMapObjectDel(playerID int64, objectID int64) {
  266. sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_OwnerMapObjectDel, &pb.I64{Value: objectID})
  267. }
  268. func (p *actor) BroadcastCastleState(castleTable *db.LogicCastleTable) {
  269. rsp := &pb.MapUICastles{}
  270. castlePb := &pb.MapUICastle{
  271. ObjectID: castleTable.ObjectID,
  272. Point: castleTable.Point.ToProto(),
  273. ConfigID: castleTable.ConfigID,
  274. LeagueID: castleTable.LeagueID,
  275. }
  276. if castlePb.LeagueID != 0 {
  277. if leauge, found := pbMapLeagues.Get(castlePb.LeagueID); found {
  278. castlePb.LeagueName = leauge.LeagueName
  279. castlePb.LeagueAbbName = leauge.LeagueAbbName
  280. castlePb.FlagBg = leauge.FlagBg
  281. castlePb.FlagIcon = leauge.FlagIcon
  282. }
  283. }
  284. rsp.List = append(rsp.List, castlePb)
  285. sessions.PushMap(capp.NodeID(), nameRoute.PushMapPlayer_MapUICastlesPush, rsp)
  286. }