| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- 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"
- nameActor "f1-game/internal/name/actor"
- nameRoute "f1-game/internal/name/route"
- "f1-game/internal/pb"
- "f1-game/internal/sessions"
- "f1-game/internal/types"
- 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"
- ctime "github.com/cherry-game/cherry/extend/time"
- clog "github.com/cherry-game/cherry/logger"
- "github.com/cherry-game/cherry/net/parser/pomelo"
- )
- type (
- actor struct {
- pomelo.ActorBase
- *db.LogicTables
- now ctime.CherryTime
- nowMillis int64
- updateID uint32
- processTiles types.Map[int32, *db.LogicTileTable] // 需要处理的地块对象,key: tileID
- marchCheckFuncs map[enum.MarchType]marchCheckFunc // 行军调动前检查方法,key: MarchType
- marchTypeFuncs map[enum.MarchType]marchTypeFunc // 行军指令处理方法,key: MarchType
- marchStopFuncs map[enum.MarchState]marchStopFunc // 行军中止指令处理方法,key:MarchState
- marchRetreatFuncs map[enum.MarchState]marchRetreatFunc // 各种状态下行军撤退指令的处理方法,key:MarchState
- tileBattleResultFunc map[enum.TileBattleType]tileBattleResultFunc // 地块战斗结果处理方法,key:TileBattleType
- battleTimeoutFuncs map[enum.TileBattleType]battleTimeoutFunc // 地块战斗超时处理方法,key:TileBattleType
- occupyingCastles types.Map[int64, *db.LogicCastleTable] // 占领中的系统城
- armyLines types.Map[int64, *mapTypes.ArmyLine] // 军情线
- watcherArmyLines types.Map[int64, types.Set[int64]] // wathcer关注的军情线
- currentObjectID int64 // 用于生成一些临时的对象id,不需要序列化
- bornPos mapTypes.MapBornPos
- bornLimit types.Map[int32, int32]
- }
- marchCheckFunc func(marchTable *db.LogicMarchTable, targetObjectID int64, point *pb.Point) int32
- marchTypeFunc func(*db.LogicMarchTable)
- marchStopFunc func(*db.LogicMarchTable)
- marchRetreatFunc func(*db.LogicMarchTable)
- tileBattleResultFunc func(*db.LogicTileTable, *dbLogic.TileBattle)
- battleTimeoutFunc func(tileTable *db.LogicTileTable, battleInfo *dbLogic.TileBattle)
- )
- func NewActor(mapTables *db.LogicTables, bornPos mapTypes.MapBornPos, bornLimit types.Map[int32, int32]) *actor {
- actor := &actor{
- LogicTables: mapTables,
- processTiles: types.NewMap[int32, *db.LogicTileTable](),
- occupyingCastles: types.NewMap[int64, *db.LogicCastleTable](),
- bornPos: bornPos,
- bornLimit: bornLimit,
- armyLines: types.NewMap[int64, *mapTypes.ArmyLine](),
- watcherArmyLines: types.NewMap[int64, types.Set[int64]](),
- currentObjectID: 1000,
- }
- actor.updateTime()
- actor.initMarchCheckFuncs()
- actor.initMarchTypeFuncs()
- actor.initMarchStopFuncs()
- actor.initMarchRetreatFuncs()
- actor.initTileBattleFunc()
- actor.initBattleTimeoutFuncs()
- actor.initMonster()
- actor.initTileProcess()
- actor.initOccupyingCastles()
- return actor
- }
- func (p *actor) AliasID() string {
- return nameActor.Map_Logic
- }
- func (p *actor) OnInit() {
- p.onInitRemote()
- p.onInitAOI()
- p.Timer().Add(constant.Duration_March, p.marchTimer)
- p.Timer().Add(constant.Duration_Tile, p.tileTimer)
- p.Timer().Add(constant.Duration_Res, p.resTimer)
- p.Timer().Add(constant.Duration_Build, p.buildTimer)
- p.Timer().Add(constant.Duration_Castle, p.castleTimer)
- p.Timer().Add(constant.Duration_Monster, p.monsterTimer)
- }
- // updateTime 全局时间更新
- func (p *actor) updateTime() {
- p.now = ctime.Now()
- p.nowMillis = p.now.UnixMilli()
- }
- func (p *actor) newUpdateID() uint32 {
- // 单线程保证了原子性
- p.updateID++
- return p.updateID
- }
- // checkBuildable 检查是否可以建造
- // TODO: 这个方法要重写,使 build 和 buildLeague 都可以调用
- func (p *actor) checkBuildable(playerID, leagueID int64, x, y int32, buildRow *data.MapBuildRow) int32 {
- offsetPoints := buildRow.GetCoverOffsetPoints(x, y, true)
- for _, point := range offsetPoints {
- if errCode := p.checkPointAvailiable(point.X, point.Y, playerID, leagueID, buildRow); code.IsFail(errCode) {
- return errCode
- }
- }
- return code.OK
- // 其它通用检查,是否有阻挡等
- }
- func (p *actor) checkPointAvailiable(x, y int32, playerID, leagueID int64, buildRow *data.MapBuildRow) int32 {
- if data.MapData().IsPointBlock(x, y) {
- clog.Debugf("point (%d, %d) not walkable.", x, y)
- return code.MapTile_NotWalkable
- }
- tileID, ok := data.PointToTileID(x, y)
- if !ok {
- return code.MapGetPointFail
- }
- tileTable := p.TileTables.GetValue(tileID)
- if tileTable.BuildObjectID > 0 || tileTable.CastleObjectID > 0 {
- clog.Debugf("point has block. Point = (%d, %d)", x, y)
- return code.MapTile_HasBlock
- }
- switch buildRow.BuildType {
- case enum.BuildType_Player:
- // 如果有资源,玩家可以建在自己的资源上
- if resTable, ok := p.getResTableInTile(tileTable); ok {
- if resTable.PlayerID > 0 && resTable.PlayerID != playerID {
- return code.MapTile_HasBlock
- }
- }
- case enum.BuildType_League:
- // 如果有资源,不能建在玩家的资源上
- if resTable, ok := p.getResTableInTile(tileTable); ok {
- if resTable.PlayerID > 0 {
- return code.MapTile_HasBlock
- }
- }
- // TODO: 有些同盟建筑是可以建在盟友的资源上的
- default:
- return code.MapBuild_NotBuildable
- }
- return code.OK
- }
- func (p *actor) getResTableInTile(tileTable *db.LogicTileTable) (*db.LogicResTable, bool) {
- if tileTable.ResObjectID == 0 {
- return nil, false
- }
- resTable, ok := p.ResTables.Get(tileTable.ResObjectID)
- if !ok {
- clog.Warnf("[getResTableInTile] A bug: Tile(%d) get no Res(%d)", tileTable.TileID, tileTable.ResObjectID)
- return nil, false
- }
- return resTable, true
- }
- func (p *actor) getMonsterTableInTile(tileTable *db.LogicTileTable) (*db.LogicMonsterTable, bool) {
- if tileTable.MonsterObjectID == 0 {
- return nil, false
- }
- monsterTable, ok := p.MonsterTables.Get(tileTable.MonsterObjectID)
- if !ok {
- clog.Warnf("[getMonsterTableInTile] A bug: Tile(%d) get no Monster(%d)", tileTable.TileID, tileTable.MonsterObjectID)
- return nil, false
- }
- return monsterTable, true
- }
- // getMarchSpeed 获取行军速度
- func (p *actor) getMarchSpeed(marchTable *db.LogicMarchTable, marchType enum.MarchType, marchState enum.MarchState) int64 {
- var speed int64
- row, ok := data.MapMarch.GetByMarchType(marchType)
- if !ok {
- clog.Errorf("get march speed failed, marchType = %d", marchType)
- return constant.MapMoveSpeedMin
- }
- // 不能小于最小移动速度
- speed = max(row.Speed*ctime.MillisecondsPerSecond, constant.MapMoveSpeedMin)
- // 测试用,加移速
- if value, ok := data.GameDebug.GetDebugValue(constant.GameDebug_MapMarchSpeedUp); ok {
- speed >>= value
- }
- return speed
- }
- func (p *actor) convertPath(path []*pb.Point) types.Points {
- points := make(types.Points, 0, len(path))
- for _, point := range path {
- points.Add(point.X, point.Y)
- }
- return points
- }
- // newMainCity 创建玩家主城
- func (p *actor) newMainCity(stateID int32, playerID, leagueID int64) (*db.LogicBuildTable, int32) {
- // 随机出生点
- tileID, ok := p.bornPos.Random(stateID)
- if !ok {
- clog.Debugf("[newMainCity] random born fail: stateID = %d, playerID = %d, leagueID = %d", stateID, playerID, leagueID)
- return nil, code.MapBornPoint_NotFound
- }
- buildRow, ok := data.MapBuild.GetByConfigID(constant.MapBuild_MainCity)
- if !ok {
- clog.Debugf("[newMainCity] config not found: stateID = %d, playerID = %d, leagueID = %d", stateID, playerID, leagueID)
- return nil, code.ConfigNotFound_MapBuild
- }
- x, y := data.TileIDToPoint(tileID)
- offsets := buildRow.GetCoverOffsetPoints(x, y, false)
- // 生成建筑
- mainCity := db.NewLogicBuildTable(db.NewGUID(), capp.NodeID(), x, y, constant.MapBuild_MainCity, playerID, leagueID, offsets...)
- mainCity.LoadConfig(buildRow)
- p.buildAdd(mainCity)
- siegeTable := db.NewLogicSiegeTable(mainCity.ObjectID, mainCity.NodeID, mainCity.ConfigID)
- siegeTable.SetSiegeParams(buildRow.BuildingDurability, buildRow.Durability, buildRow.GarrisonArmies, buildRow.DefendArmy)
- siegeTable.SiegeReset(false)
- p.SiegeTables.Put(siegeTable.ObjectID, siegeTable)
- p.bornPos.Remove(tileID)
- clog.Infof("new main city success: stateID = %d, playerId = %d, leagueID = %d, point = %v",
- stateID, playerID, leagueID, mainCity.Point)
- return mainCity, code.OK
- }
- // changeMainCity 改变主城(随机出生点)
- func (p *actor) changeMainCity(stateID int32, mainCityObjectID int64) (*db.LogicBuildTable, int32) {
- mainCity, ok := p.BuildTables.Get(mainCityObjectID)
- if !ok {
- return nil, code.MapObjectNotFound
- }
- // 随机出生点
- tileID, ok := p.bornPos.Random(stateID)
- if !ok {
- clog.Debugf("[changeMainCity] random born fail: stateID = %d, mainCityObjectID = %d", stateID, mainCityObjectID)
- return nil, code.MapBornPoint_NotFound
- }
- buildRow, ok := data.MapBuild.GetByConfigID(constant.MapBuild_MainCity)
- if !ok {
- clog.Debugf("[changeMainCity] config not found: stateID = %d, mainCityObjectID = %d", stateID, mainCityObjectID)
- return nil, code.ConfigNotFound_MapBuild
- }
- x, y := data.TileIDToPoint(tileID)
- offsets := buildRow.GetCoverOffsetPoints(x, y, false)
- // 先移除主城
- p.buildRemove(mainCity)
- // 刷新主城位置
- mainCity.Point.SetValue(x, y)
- mainCity.OffsetPoints = offsets
- mainCity.IsHide = false // 无论怎么都要显示
- mainCity.IsDataChanged = true
- // 重新 Load 一下配置
- mainCity.LoadConfig(buildRow)
- // 把主城血量刷新
- siegeTable := p.SiegeTables.GetValue(mainCity.ObjectID)
- siegeTable.SiegeReset(false)
- // 再添加主城
- p.buildAdd(mainCity)
- p.bornPos.Remove(tileID)
- clog.Infof("change main city success: stateID = %d, mainCityObjectID = %d, point = %v", stateID, mainCityObjectID, mainCity.Point)
- return mainCity, code.OK
- }
- func (p *actor) updateInvincibleTime(watcherTable *db.LogicWatcherTable, endTime int64) {
- watcherTable.SetInvincibleEndTime(endTime)
- watcherTable.Save2Queue()
- // 主城
- mainCity, ok := p.BuildTables.Get(watcherTable.MainCity)
- if ok {
- mainCity.SetInvincibleEndTime(endTime)
- mainCity.IsDataChanged = true
- p.UpdateObjectFull(mainCity.ObjectID, mainCity.Type)
- }
- // 行军对象
- for _, marchObjectID := range watcherTable.Marchs {
- marchTable := p.MarchTables.GetValue(marchObjectID)
- marchTable.SetInvincibleEndTime(endTime)
- marchTable.IsDataChanged = true
- p.UpdateObjectFull(marchTable.ObjectID, marchTable.Type)
- }
- }
- // TODO: 把这个封装到 LogicTables GetObjectLeagueID 方法
- func (p *actor) getMarchLeagueID(objectID int64, isLegion bool) int64 {
- if isLegion {
- if legionTable, ok := p.LegionTables.Get(objectID); ok {
- return legionTable.LeagueID
- }
- }
- if marchTable, ok := p.MarchTables.Get(objectID); ok {
- return marchTable.LeagueID
- }
- return 0
- }
- func (p *actor) generateObjectID() int64 {
- if p.currentObjectID < 1000 {
- p.currentObjectID = 1000
- }
- p.currentObjectID++
- return p.currentObjectID
- }
- func (p *actor) initOccupyingCastles() {
- for _, castleTable := range p.CastleTables {
- if castleTable.LeagueID != 0 && castleTable.CastleConfig.CastleType == pb.SysObjType_so_castle {
- p.occupyingCastles.Put(castleTable.ObjectID, castleTable)
- }
- }
- }
- // 更新对象全量信息
- func (p *actor) UpdateObjectFull(objectID int64, objectType enum.ObjectType) {
- // 更新aoi
- mapCall.AOI.UpdateAll(objectID, objectType)
- // 更新对象所有者
- switch objectType {
- case enum.ObjectType_Build:
- if buildTable, ok := p.BuildTables.Get(objectID); ok {
- p.PushOwnerMapObjectUpdate(buildTable.PlayerID, buildTable.ToOwnerMapObjectPb())
- }
- case enum.ObjectType_Res:
- if resTable, ok := p.ResTables.Get(objectID); ok {
- p.PushOwnerMapObjectUpdate(resTable.PlayerID, resTable.ToOwnerMapObjectPb())
- }
- }
- }
- func (p *actor) PushOwnerMapObjectAdd(playerID int64, object *pb.OwnerMapObject) {
- sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_OwnerMapObjectAdd, object)
- }
- func (p *actor) PushOwnerMapObjectUpdate(playerID int64, object *pb.OwnerMapObject) {
- sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_OwnerMapObjectUpdate, object)
- }
- func (p *actor) PushOwnerMapObjectDel(playerID int64, objectID int64) {
- sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_OwnerMapObjectDel, &pb.I64{Value: objectID})
- }
- func (p *actor) BroadcastCastleState(castleTable *db.LogicCastleTable) {
- rsp := &pb.MapUICastles{}
- castlePb := &pb.MapUICastle{
- ObjectID: castleTable.ObjectID,
- Point: castleTable.Point.ToProto(),
- ConfigID: castleTable.ConfigID,
- LeagueID: castleTable.LeagueID,
- }
- if castlePb.LeagueID != 0 {
- if leauge, found := pbMapLeagues.Get(castlePb.LeagueID); found {
- castlePb.LeagueName = leauge.LeagueName
- castlePb.LeagueAbbName = leauge.LeagueAbbName
- castlePb.FlagBg = leauge.FlagBg
- castlePb.FlagIcon = leauge.FlagIcon
- }
- }
- rsp.List = append(rsp.List, castlePb)
- sessions.PushMap(capp.NodeID(), nameRoute.PushMapPlayer_MapUICastlesPush, rsp)
- }
- func (p *actor) PushMapUIMarchUpsert(playerID int64, march *pb.MapUIMarch) {
- sessions.PushPlayer(playerID, nameRoute.PushMapPlayer_MapUIMarchsPush, &pb.MapUIMarchs{List: []*pb.MapUIMarch{march}})
- }
- func (p *actor) PushMapUIMarchState(playerID int64, state *pb.MapMarchState) {
- sessions.PushPlayer(playerID, nameRoute.PushMapPusher_MapUIMarchState, state)
- }
|