league_map.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. package mapLeague
  2. import (
  3. "f1-game/internal/code"
  4. "f1-game/internal/constant"
  5. "f1-game/internal/data"
  6. "f1-game/internal/enum"
  7. "f1-game/internal/event"
  8. "f1-game/internal/extend/utils"
  9. nameEvent "f1-game/internal/name/event"
  10. nameLocal "f1-game/internal/name/local"
  11. nameRemote "f1-game/internal/name/remote"
  12. nameRoute "f1-game/internal/name/route"
  13. "f1-game/internal/pb"
  14. "f1-game/internal/sessions"
  15. mapCall "f1-game/nodes/map/internal/call"
  16. "f1-game/nodes/map/internal/db"
  17. leagueAttr "f1-game/nodes/map/league/attr"
  18. leagueBase "f1-game/nodes/map/league/base"
  19. leagueMail "f1-game/nodes/map/league/mail"
  20. ctime "github.com/cherry-game/cherry/extend/time"
  21. cfacade "github.com/cherry-game/cherry/facade"
  22. cproto "github.com/cherry-game/cherry/net/proto"
  23. )
  24. func (p *actorLeague) initMap() {
  25. p.Local().Register(nameLocal.MapLeague_Build, p.build)
  26. p.Local().Register(nameLocal.MapLeague_BuildUpgrade, p.buildUpgrade)
  27. p.Local().Register(nameLocal.MapLeague_BuildDiscard, p.buildDiscard)
  28. p.Local().Register(nameLocal.MapLeague_DiscardCancel, p.discardCancel)
  29. p.Remote().Register(nameRemote.MapLeague_BuildUpdate, p.buildUpdate)
  30. p.EventRegister(nameEvent.League_BuildFinish, p.buildFinish)
  31. p.EventRegister(nameEvent.League_PointDestroy, p.pointDestroy)
  32. }
  33. func (p *actorLeague) build(session *cproto.Session, req *pb.MapBuildRequest) {
  34. p.Debug("[build] req = %v", req)
  35. opPlayerID := sessions.GetPlayerID(session)
  36. leagueMemberTable, found := db.GetMapLeagueMemberTable(p.leagueID)
  37. if !found {
  38. p.ResponseCode(session, code.LeagueMembersEmpty)
  39. return
  40. }
  41. // 权限检查 需要同盟建设权限
  42. ok := leagueMemberTable.CheckPermission(opPlayerID, enum.League_Permission_Construction)
  43. if !ok {
  44. p.ResponseCode(session, code.LeaguePermissionDenied)
  45. return
  46. }
  47. row, ok := data.MapBuild.GetByConfigID(req.ConfigID)
  48. if !ok {
  49. p.ResponseCode(session, code.ConfigNotFound_MapBuild)
  50. return
  51. }
  52. // 不是联盟建筑
  53. if row.BuildType != enum.BuildType_League {
  54. p.ResponseCode(session, code.MapBuild_NotLeagueBuild)
  55. return
  56. }
  57. var (
  58. buildCount = p.leagueTable.Builds.GetBuildCount(req.ConfigID)
  59. buildLimit int32
  60. )
  61. // 获取当前联盟建筑建造上限
  62. attrID, found := constant.LeagueMapBuildCountLimitAttrIDMap[req.ConfigID]
  63. if found {
  64. buildLimit = p.leagueTable.LeagueInfo.Attrs.GetI32(attrID)
  65. }
  66. // 建造数量已达上限
  67. if buildCount >= buildLimit {
  68. p.ResponseCode(session, code.MapBuild_NumLimit)
  69. return
  70. }
  71. // 资源不足
  72. if len(row.BuildCost) > 0 && !p.isEnough(row.BuildCost) {
  73. p.ResponseCode(session, code.ItemNotEnough)
  74. return
  75. }
  76. // 计算建造时间
  77. buildTime := row.BuildTime
  78. // 首次建造
  79. if buildCount == 0 {
  80. if time, ok := data.Const.MapBuildFirstTimeElapsed[req.ConfigID]; ok {
  81. buildTime = time
  82. }
  83. }
  84. level := int32(1)
  85. if req.ConfigID == constant.MapBuild_LeaguePoint {
  86. level = p.leagueTable.LeagueInfo.Level
  87. }
  88. arg := pb.BuildLeagueRequest{
  89. ConfigID: req.ConfigID,
  90. LeagueID: p.leagueID,
  91. Level: level,
  92. X: req.Point.X,
  93. Y: req.Point.Y,
  94. BuildEndTime: ctime.Now().ToMillisecond() + buildTime*ctime.MillisecondsPerSecond,
  95. }
  96. rsp, errCode := mapCall.Logic.BuildLeague(&arg)
  97. if code.IsFail(errCode) {
  98. p.Debug("build fail: errCode = %d", errCode)
  99. p.ResponseCode(session, errCode)
  100. return
  101. }
  102. // 扣除资源
  103. if len(row.BuildCost) > 0 {
  104. p.subAssets(row.BuildCost)
  105. }
  106. newObjectID, removeObjectID := rsp.Key, rsp.Value
  107. newBuild := p.leagueTable.Builds.AddBuild(req.ConfigID, newObjectID, req.Point.X, req.Point.Y, arg.BuildEndTime)
  108. buildsUpdatePushRsp := &pb.MapObjectsUpdate{}
  109. if removeObjectID != 0 {
  110. p.leagueTable.Builds.RemoveBuild(removeObjectID)
  111. // 给所有联盟成员推送建筑更新
  112. buildsUpdatePushRsp.List = append(buildsUpdatePushRsp.List, &pb.MapObjectUpdate{
  113. IsDelete: true,
  114. ObjectID: removeObjectID,
  115. ObjectType: int32(enum.ObjectType_Build),
  116. })
  117. }
  118. p.leagueTable.Save2Queue()
  119. p.ResponseCode(session, code.OK)
  120. p.PostEvent(event.NewLeagueBuild(p.leagueID, req.ConfigID, req.Point))
  121. // 给所有联盟成员推送建筑更新
  122. buildsUpdatePushRsp.List = append(buildsUpdatePushRsp.List, &pb.MapObjectUpdate{
  123. ObjectID: newObjectID,
  124. ConfigID: newBuild.ConfigID,
  125. ObjectType: int32(enum.ObjectType_Build),
  126. Point: newBuild.Point.ToProto(),
  127. Time: newBuild.BuildEndTime,
  128. })
  129. p.pushToMembers(nameRoute.PushMapLeague_BuildUpdate, buildsUpdatePushRsp)
  130. // 添加日志
  131. opPlayerName := leagueBase.Service().GetPlayerName(opPlayerID)
  132. jobID := leagueMemberTable.GetLeagueMemberJob(opPlayerID)
  133. leagueLogTable, _ := db.GetMapLeagueLogTable(p.leagueID)
  134. leagueLogTable.AddLeagueLog(enum.Log_LeagueBuildBuilding, utils.ToStringSlice(jobID, opPlayerName, req.ConfigID)...)
  135. }
  136. // 联盟建筑升级
  137. func (p *actorLeague) buildUpgrade(session *cproto.Session, req *pb.I64) {
  138. opPlayerID := sessions.GetPlayerID(session)
  139. leagueMemberTable, found := db.GetMapLeagueMemberTable(p.leagueID)
  140. if !found {
  141. p.ResponseCode(session, code.LeagueMembersEmpty)
  142. return
  143. }
  144. // 权限检查 需要同盟建设权限
  145. ok := leagueMemberTable.CheckPermission(opPlayerID, enum.League_Permission_Construction)
  146. if !ok {
  147. p.ResponseCode(session, code.LeaguePermissionDenied)
  148. return
  149. }
  150. build, found := p.leagueTable.Builds.GetBuild(req.Value)
  151. if !found {
  152. p.ResponseCode(session, code.MapBuild_NotFound)
  153. return
  154. }
  155. // 联盟驻地不支持手动升级 跟随联盟等级
  156. if build.ConfigID == constant.MapBuild_LeaguePoint {
  157. p.ResponseCode(session, code.MapBuild_NotUpgrade)
  158. return
  159. }
  160. if build.IsBuilding() {
  161. p.ResponseCode(session, code.MapBuild_IsBuilding)
  162. return
  163. }
  164. if build.IsUpgrading() {
  165. p.ResponseCode(session, code.MapBuild_IsUpgrading)
  166. return
  167. }
  168. if build.DiscardTime > 0 {
  169. p.ResponseCode(session, code.MapBuild_AlreadyDiscarding)
  170. return
  171. }
  172. curMapBuildUpgradeRow, found := data.MapBuildUpgrade.GetByConfigIDLevel(build.ConfigID, build.Level)
  173. if !found {
  174. p.ResponseCode(session, code.MapBuildUpgrade_NotFound)
  175. return
  176. }
  177. var (
  178. nextLevel = build.Level + 1
  179. levelLimit = int32(0)
  180. )
  181. // 获取当前建筑等级上限
  182. attrID, found := constant.LeagueMapBuildLevelLimitAttrIDMap[build.ConfigID]
  183. if found {
  184. levelLimit = p.leagueTable.LeagueInfo.Attrs.GetI32(attrID)
  185. }
  186. // 判断等级是否已达当前上限
  187. if nextLevel >= levelLimit {
  188. p.ResponseCode(session, code.LevelLimit)
  189. return
  190. }
  191. // 判断下一等级的配置是否存在
  192. _, found = data.MapBuildUpgrade.GetByConfigIDLevel(build.ConfigID, nextLevel)
  193. if !found {
  194. p.ResponseCode(session, code.ConfigNotFound_MapBuildUpgrade)
  195. return
  196. }
  197. // 升级消耗不存在
  198. if curMapBuildUpgradeRow.UpgradeCost.IsEmpty() {
  199. p.ResponseCode(session, code.MapBuildUpgradeCost_NotExist)
  200. return
  201. }
  202. if !p.isEnough(curMapBuildUpgradeRow.UpgradeCost) {
  203. p.ResponseCode(session, code.ItemNotEnough)
  204. return
  205. }
  206. // 升级结束时间戳
  207. upgradeEndTime := ctime.Now().ToMillisecond() + curMapBuildUpgradeRow.UpgradeTime*ctime.MillisecondsPerSecond
  208. arg := &pb.BuildUpgradeRequest{
  209. ObjectID: build.ObjectID,
  210. ConfigID: build.ConfigID,
  211. LeagueID: p.leagueID,
  212. Level: build.Level,
  213. UpgradeEndTime: upgradeEndTime,
  214. }
  215. errCode := mapCall.Logic.BuildUpgrade(arg)
  216. if code.IsFail(errCode) {
  217. p.ResponseCode(session, errCode)
  218. return
  219. }
  220. // 扣除资源
  221. p.subAssets(curMapBuildUpgradeRow.UpgradeCost)
  222. // TODO 是否单独字段保存升级结束时间戳 现在先放建造结束时间
  223. build.UpgradeEndTime = upgradeEndTime
  224. p.leagueTable.Save2Queue()
  225. p.ResponseCode(session, code.OK)
  226. // 给所有联盟成员推送建筑更新
  227. buildsUpdatePushRsp := &pb.MapObjectsUpdate{}
  228. buildsUpdatePushRsp.List = append(buildsUpdatePushRsp.List, &pb.MapObjectUpdate{
  229. ObjectID: build.ObjectID,
  230. ConfigID: build.ConfigID,
  231. ObjectType: int32(enum.ObjectType_Build),
  232. Point: build.Point.ToProto(),
  233. Time: build.BuildEndTime,
  234. })
  235. p.pushToMembers(nameRoute.PushMapLeague_BuildUpdate, buildsUpdatePushRsp)
  236. }
  237. // buildDiscard 摧毁联盟建筑
  238. func (p *actorLeague) buildDiscard(session *cproto.Session, req *pb.I64) {
  239. objectID := req.Value
  240. p.Debug("[buildDiscard] objectID = %d", objectID)
  241. build, found := p.leagueTable.Builds.GetBuild(objectID)
  242. if !found {
  243. p.ResponseCode(session, code.MapLeague_BuildNotFound)
  244. return
  245. }
  246. mapBuildRow, found := data.MapBuild.GetByConfigID(build.ConfigID)
  247. if !found {
  248. p.ResponseCode(session, code.ConfigNotFound_MapBuild)
  249. return
  250. }
  251. // 当前建筑不可摧毁
  252. if !mapBuildRow.Discardable {
  253. p.ResponseCode(session, code.MapLeague_NotCanDiscard)
  254. return
  255. }
  256. // TODO 建筑建造中是否能摧毁
  257. // 已经在摧毁中
  258. if build.DiscardTime > 0 {
  259. p.ResponseCode(session, code.MapBuild_AlreadyDiscarding)
  260. return
  261. }
  262. opPlayerID := sessions.GetPlayerID(session)
  263. mapLeagueMemberTable, _ := db.GetMapLeagueMemberTable(p.leagueID)
  264. // 检查权限
  265. if !mapLeagueMemberTable.CheckPermission(opPlayerID, enum.League_Permission_Construction) {
  266. p.ResponseCode(session, code.LeaguePermissionDenied)
  267. return
  268. }
  269. discardTime, errCode := mapCall.Logic.BuildDiscard(p.leagueID, objectID)
  270. if code.IsFail(errCode) {
  271. p.ResponseCode(session, errCode)
  272. return
  273. }
  274. build.PlayerID = sessions.GetPlayerID(session)
  275. build.JobID = mapLeagueMemberTable.GetLeagueMemberJob(opPlayerID)
  276. build.DiscardTime = discardTime
  277. p.leagueTable.Save2Queue()
  278. p.ResponseCode(session, code.OK)
  279. // 给所有联盟成员推送建筑更新
  280. p.pushToMembers(nameRoute.PushMapLeague_BuildUpdate, &pb.MapObjectsUpdate{
  281. List: []*pb.MapObjectUpdate{
  282. {
  283. IsDelete: true,
  284. ObjectID: objectID,
  285. ConfigID: build.ConfigID,
  286. ObjectType: int32(enum.ObjectType_Build),
  287. Point: build.Point.ToProto(),
  288. Time: discardTime,
  289. },
  290. },
  291. })
  292. }
  293. // buildDiscard 摧毁联盟建筑
  294. func (p *actorLeague) discardCancel(session *cproto.Session, req *pb.I64I32) {
  295. var (
  296. objectID = req.Key
  297. objectType = enum.ObjectType(req.Value)
  298. )
  299. p.Debug("[discardCancel] objectID = %d, objectType = %s", objectID, enum.GetObjectTypeName(objectType))
  300. opPlayerID := sessions.GetPlayerID(session)
  301. mapLeagueMemberTable, _ := db.GetMapLeagueMemberTable(p.leagueID)
  302. // 检查权限
  303. if !mapLeagueMemberTable.CheckPermission(opPlayerID, enum.League_Permission_Construction) {
  304. p.ResponseCode(session, code.LeaguePermissionDenied)
  305. return
  306. }
  307. errCode := mapCall.Logic.DiscardCancelLeague(p.leagueID, objectID, int32(objectType))
  308. if code.IsFail(errCode) {
  309. p.ResponseCode(session, errCode)
  310. return
  311. }
  312. switch objectType {
  313. case enum.ObjectType_Build:
  314. build, found := p.leagueTable.Builds.GetBuild(objectID)
  315. if !found {
  316. p.ResponseCode(session, code.MapLeague_BuildNotFound)
  317. return
  318. }
  319. build.PlayerID = 0
  320. build.JobID = 0
  321. build.DiscardTime = 0
  322. p.leagueTable.Save2Queue()
  323. // 给所有联盟成员推送建筑更新
  324. p.pushToMembers(nameRoute.PushMapLeague_BuildUpdate, &pb.MapObjectsUpdate{
  325. List: []*pb.MapObjectUpdate{
  326. {
  327. ObjectID: objectID,
  328. ConfigID: build.ConfigID,
  329. ObjectType: int32(enum.ObjectType_Build),
  330. Point: build.Point.ToProto(),
  331. Time: 0,
  332. },
  333. },
  334. })
  335. }
  336. p.ResponseCode(session, code.OK)
  337. }
  338. func (p *actorLeague) buildUpdate(req *pb.BuildUpdate) {
  339. p.Debug("[buildUpdate] req = %+v", req)
  340. var (
  341. point = &pb.Point{
  342. X: req.X,
  343. Y: req.Y,
  344. }
  345. mapObjectUpdate = &pb.MapObjectUpdate{
  346. ObjectID: req.ObjectID,
  347. ConfigID: req.ConfigID,
  348. ObjectType: int32(enum.ObjectType_Build),
  349. Point: point,
  350. }
  351. )
  352. if req.IsAdd {
  353. // 占领获取了建筑,不用设置建造时间
  354. p.leagueTable.Builds.AddBuild(req.ConfigID, req.ObjectID, req.X, req.Y, 0)
  355. } else {
  356. removeBuild, success := p.leagueTable.Builds.RemoveBuild(req.ObjectID)
  357. mapObjectUpdate.IsDelete = true
  358. // 给所有联盟成员推送邮件
  359. // TODO 先用系统邮件通知,等后面有了联盟系统邮件再改
  360. leagueMail.Service().SendAllMemberMail(p.leagueID, constant.MailID_LeagueBuildDiscard, nil, req.ConfigID, point)
  361. if success {
  362. // 添加放弃日志
  363. opPlayerName := leagueBase.Service().GetPlayerName(removeBuild.PlayerID)
  364. leagueLogTable, _ := db.GetMapLeagueLogTable(p.leagueID)
  365. leagueLogTable.AddLeagueLog(enum.Log_LeagueGiveUpBuilding, utils.ToStringSlice(removeBuild.JobID, opPlayerName, removeBuild.ConfigID)...)
  366. }
  367. }
  368. p.leagueTable.Save2Queue()
  369. // 给所有联盟成员推送建筑更新
  370. p.pushToMembers(nameRoute.PushMapLeague_BuildUpdate, &pb.MapObjectsUpdate{
  371. List: []*pb.MapObjectUpdate{mapObjectUpdate},
  372. })
  373. }
  374. // 联盟建筑建造/升级完成事件处理
  375. func (p *actorLeague) buildFinish(eventData cfacade.IEventData) {
  376. p.Debug("[buildFinish] leagueID:%d, eventData=%+v", p.leagueID, eventData)
  377. evt, ok := eventData.(*event.LeagueBuildFinish)
  378. if !ok {
  379. p.Warn("[buildFinish] eventData error. leagueID:%d", p.leagueID)
  380. return
  381. }
  382. var (
  383. req = evt.Key()
  384. isUp = evt.Value()
  385. objectID = req.Key
  386. level = req.Value
  387. )
  388. build, ok := p.leagueTable.Builds.GetBuild(objectID)
  389. if !ok {
  390. p.Warn("[buildFinish] build not found. leagueID:%d, objectID:%d", p.leagueID, objectID)
  391. return
  392. }
  393. build.Level = level
  394. p.leagueTable.Save2Queue()
  395. if !isUp {
  396. // 建造完成的时候推送全员邮件
  397. // TODO 先用系统邮件通知,等后面有了联盟系统邮件再改
  398. leagueMail.Service().SendAllMemberMail(p.leagueID, constant.MailID_LeagueBuild, nil, build.ConfigID, build.Point)
  399. }
  400. mapBuildRow, found := data.MapBuild.GetByConfigID(build.ConfigID)
  401. // 当前完成的是联盟资源田 刷新联盟属性
  402. if found && mapBuildRow.BuildingCategory == enum.LeagueMapBuildType_Res {
  403. leagueAttr.Service().UpdateAttrs(p.leagueTable)
  404. }
  405. }
  406. // 联盟驻地摧毁
  407. func (p *actorLeague) pointDestroy(eventData cfacade.IEventData) {
  408. evt, ok := eventData.(*event.LeaguePointDestroy)
  409. if !ok {
  410. p.Warn("[leaguePointDestroy] eventData error. leagueID:%d", p.leagueID)
  411. return
  412. }
  413. p.Debug("[leaguePointDestroy] leagueID = %d, objectID = %d", p.leagueID, evt.ObjectID())
  414. // 通知所有联盟成员再起
  415. for _, re := range p.leagueTable.Residences {
  416. mapCall.Player.AutoReborn(re.PlayerID)
  417. }
  418. // 清空驻地
  419. p.leagueTable.Residences.Clear()
  420. // 删除驻地
  421. p.leagueTable.Builds.RemoveBuild(evt.ObjectID())
  422. p.leagueTable.Save2Queue()
  423. // 通知在线成员更新驻地信息
  424. p.pushToMembers(nameRoute.PushMapLeague_Residence, &pb.LeagueResidenceList{})
  425. }