RedFortress.lua 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  1. --赤色要塞副本
  2. RedFortress = {}
  3. local this = {}
  4. local NoteType = { KILL = 1, RELIVE = 2 }
  5. -- @description 请求进入副本
  6. -- @param 玩家对象;cfg_rep的id
  7. -- @return
  8. function RedFortress.ReqEnterFortress(actor, configId)
  9. -- 验证等级
  10. local playerLevel = tonumber(getbaseinfo(actor, "level"))
  11. local levelConfig = ConfigDataManager.getTableValue("cfg_rep", "level", "id", configId)
  12. local levelList = string.split(levelConfig, "#")
  13. local minLevel = tonumber(levelList[1])
  14. local maxLevel = tonumber(levelList[2])
  15. if playerLevel < minLevel or playerLevel > maxLevel then
  16. tipinfo(actor, "等级不足")
  17. error(actor:toString() .. "赤色要塞副本进入等级不符合要求{" .. playerLevel .. "}{" .. configId .. "}")
  18. return
  19. end
  20. -- 寻找空闲副本
  21. local mapId = DuplicateCommon.FindEnterableDupCommon(configId, 1)
  22. if mapId == 0 then
  23. mapId = DuplicateCommon.CreateDupMapCommon(actor, configId, true)
  24. end
  25. local x, y = DuplicateCommon.GetEnterPointXYCommon(configId)
  26. --回蓝回血
  27. DuplicateCommon.RecoverHPMP(actor)
  28. enterduplicate(actor, mapId, x, y)
  29. end
  30. -- @description 副本阶段更新
  31. -- @param 系统id;地图唯一id;副本类型;副本阶段(1-准备 2-战斗 3-结算);下一阶段开始时间戳;配置id(cfg_rep的id)
  32. -- @return
  33. function RedFortress.RedFortressStateUpdate(system, mapId, state, nextStateStartTime, configId)
  34. if state == DuplicateState.PREPARE then
  35. --准备阶段
  36. -- 初始化副本数据
  37. -- 战斗区域
  38. local fightAreaConfig = ConfigDataManager.getTableValue("cfg_repRedfortress", "start", "repId", configId)
  39. local fightAreaList = string.split(fightAreaConfig, "#")
  40. setenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA, fightAreaList)
  41. setenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_PHASE, 1) --地图每减少一次阶段+1
  42. elseif state == DuplicateState.FIGHT then
  43. --更新阶段信息
  44. this.ResAllPlayCurrencyStateInfo(mapId)
  45. --战斗阶段
  46. -- 生成怪物
  47. local config = ConfigDataManager.getTableFirst("cfg_repRedfortress", "repId", configId)
  48. local monsterConfig = config.monsterid
  49. local monsterList = string.split(monsterConfig, "#")
  50. local playerCount = getplaycountinmap(mapId)
  51. playerCount = tonumber(playerCount or 1)
  52. local maxUnit = config.maxunit
  53. local leftCount = tonumber(maxUnit) - playerCount
  54. for i = 1, leftCount do
  55. --怪物id
  56. local randomIndex = math.random(#monsterList)
  57. local monsterId = monsterList[randomIndex]
  58. --随机点位
  59. local x, y = this.RandomFightPointXY(mapId, 100)
  60. mongen(mapId, x, y, 0, monsterId, 1)
  61. end
  62. -- 玩家传送到战斗区域
  63. local players = getmapplayer(mapId)
  64. for index, player in ipairs(players) do
  65. --随机点位
  66. local x, y = this.RandomFightPointXY(mapId, 100)
  67. maptransfer(player, x, y, 0, 0, 0)
  68. end
  69. this.ResAllPlayersTaskInfo(mapId, maxUnit)
  70. elseif state == DuplicateState.FINISH then
  71. --结算阶段
  72. this.ResAllPlayCurrencyStateInfo(mapId)
  73. -- 计算积分
  74. this.CalcuAndSetPlayerPointRank(mapId)
  75. end
  76. end
  77. -- @description 副本5秒钟心跳
  78. -- @param
  79. -- @return
  80. function RedFortress.Dup5SecondHeart(mapId, dupConfig, state)
  81. if state == DuplicateState.FIGHT then
  82. local area = getenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA)
  83. local x1 = tonumber(area[1])
  84. local x2 = tonumber(area[2])
  85. local y1 = tonumber(area[3])
  86. local y2 = tonumber(area[4])
  87. -- 不在区域内的怪物死亡
  88. local monsterInfoList = mapbossinfo(mapId)
  89. for key, monInfo in pairs(monsterInfoList) do
  90. local monId = monInfo["id"]
  91. local px = monInfo["x"]
  92. local py = monInfo["y"]
  93. local isIn = this.IsPointInArea(px, py, x1, x2, y1, y2)
  94. if isIn == false then
  95. local monster = getactor(monId, mapId)
  96. removemapobject(monster)
  97. end
  98. end
  99. end
  100. end
  101. -- @description 玩家进入副本后
  102. -- @param 玩家对象;地图唯一id;副本类型;副本阶段(1-准备 2-战斗 3-结算);下一阶段开始时间戳;配置id(cfg_rep的id)
  103. -- @return
  104. function RedFortress.AfterRedFortressEnter(actor, mapId, state, nextStateStartTime, configId)
  105. -- 清除召唤兽
  106. local petId = getbaseinfo(actor, "petid")
  107. local petActor = getactor(petId, mapId)
  108. removemapobject(petActor)
  109. local playerDupInfo = getplaydef(actor, RedFortressPlayerConst.MAP_INFO)
  110. -- 是否是新进入的副本
  111. local newEnter = false
  112. if playerDupInfo == nil or playerDupInfo == "" then
  113. newEnter = true
  114. else
  115. local lastMapId = playerDupInfo[1]
  116. if mapId ~= lastMapId then
  117. newEnter = true
  118. end
  119. end
  120. if newEnter == true then
  121. -- 进入新的副本,初始化玩家变量
  122. playerDupInfo = { mapId, configId }
  123. setplaydef(actor, RedFortressPlayerConst.MAP_INFO, playerDupInfo)
  124. -- 复活次数
  125. setplaydef(actor, RedFortressPlayerConst.RELIVE_TIMES, 0)
  126. -- 积分
  127. setplaydef(actor, RedFortressPlayerConst.KILL_SCORES, 0)
  128. -- 击杀数
  129. setplaydef(actor, RedFortressPlayerConst.KILL_COUNT, 0)
  130. -- 排名
  131. setplaydef(actor, RedFortressPlayerConst.PLAYER_RANK, 0)
  132. end
  133. this.ResCurrencyStateInfo(actor, mapId)
  134. if state == DuplicateState.PREPARE then
  135. -- 发送副本人数
  136. this.ResAllPreparePlayerCount(mapId)
  137. elseif state == DuplicateState.FIGHT then
  138. -- 随机传送
  139. local x, y = this.RandomFightPointXY(mapId, 100)
  140. maptransfer(actor, x, y, 0, 0, 0)
  141. this.ResPlayerTaskInfo(actor, mapId)
  142. end
  143. end
  144. -- @description 玩家退出副本
  145. -- @param 玩家对象;地图唯一id;副本类型;副本阶段(1-准备 2-战斗 3-结算);下一阶段开始时间戳;配置id(cfg_rep的id)
  146. -- @return
  147. function RedFortress.AfterRedFortressQuit(actor, mapId, state, nextStateStartTime, configId)
  148. -- 排名更新;战斗阶段是人数;结算阶段是积分
  149. if state == DuplicateState.PREPARE then
  150. this.ResAllPreparePlayerCount(mapId)
  151. elseif state == DuplicateState.FIGHT then
  152. local players = getmapplayer(mapId)
  153. local playerCount = #players
  154. local monCount = getmoncount(mapId)
  155. local totalCount = monCount + playerCount
  156. setplaydef(actor, RedFortressPlayerConst.PLAYER_RANK, totalCount + 1)
  157. this.LeftCountChange(mapId, 0)
  158. end
  159. end
  160. -- @description 怪物死亡事件
  161. -- @param 地图id;击杀者
  162. -- @return
  163. function RedFortress.OnMonsterDie(mapId, killer)
  164. this.LeftCountChange(mapId, 0)
  165. local dupInfo = getduplicate(mapId)
  166. local configId = dupInfo["dupcfgid"]
  167. if killer:toString() ~= "0" then
  168. -- 击杀者加积分;显示图标
  169. local oldCount = getplaydef(killer, RedFortressPlayerConst.KILL_SCORES)
  170. local pointConfig = ConfigDataManager.getTableValue("cfg_repRedfortress", "points", "repId", configId)
  171. local pointList = string.split(pointConfig, "#")
  172. local addPoint = tonumber(pointList[2])
  173. local newPoint = oldCount + addPoint
  174. setplaydef(killer, RedFortressPlayerConst.KILL_SCORES, newPoint)
  175. -- 加击杀次数
  176. local oldKill = getplaydef(killer, RedFortressPlayerConst.KILL_COUNT)
  177. setplaydef(killer, RedFortressPlayerConst.KILL_COUNT, oldKill + 1)
  178. sendluamsg(killer, LuaMessageIdToClient.RES_RED_FORTRESS_KILL_RELIVE, { NoteType.KILL, oldKill + 1 })
  179. end
  180. end
  181. -- @description 击杀玩家
  182. -- @param 玩家对象;被击杀的玩家
  183. -- @return
  184. function RedFortress.KillPlayer(actor, diePlayer)
  185. local mapId = getbaseinfo(actor, "unimapid")
  186. local dupInfo = getduplicate(mapId)
  187. if dupInfo == nil or dupInfo == "" then
  188. return
  189. end
  190. local type = dupInfo["type"]
  191. if type ~= DuplicateType.RED_FORTRESS then
  192. return
  193. end
  194. local configId = dupInfo["dupcfgids"]
  195. -- 加积分
  196. local oldCount = getplaydef(actor, RedFortressPlayerConst.KILL_SCORES)
  197. local pointConfig = ConfigDataManager.getTableValue("cfg_repRedfortress", "points", "repId", configId)
  198. local pointList = string.split(pointConfig, "#")
  199. local addPoint = tonumber(pointList[1])
  200. local newPoint = oldCount + addPoint
  201. setplaydef(actor, RedFortressPlayerConst.KILL_SCORES, newPoint)
  202. -- 加击杀次数
  203. local oldKill = getplaydef(actor, RedFortressPlayerConst.KILL_COUNT)
  204. setplaydef(actor, RedFortressPlayerConst.KILL_COUNT, oldKill + 1)
  205. -- 击杀图标
  206. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_KILL_RELIVE, { NoteType.KILL, oldKill + 1 })
  207. end
  208. -- @description 玩家死亡事件
  209. -- @param 玩家对象
  210. -- @return
  211. function RedFortress.PlayerDie(actor)
  212. local mapId = getbaseinfo(actor, "unimapid")
  213. local dupInfo = getduplicate(mapId)
  214. if dupInfo == nil then
  215. return
  216. end
  217. local type = dupInfo["type"]
  218. if type ~= DuplicateType.RED_FORTRESS then
  219. return
  220. end
  221. local configId = dupInfo["dupcfgid"]
  222. -- 更新死亡次数
  223. local oldCount = getplaydef(actor, RedFortressPlayerConst.RELIVE_TIMES)
  224. local maxCount = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornChance", "repId", configId)
  225. if oldCount >= tonumber(maxCount) then
  226. -- 退出副本并结算
  227. DuplicateCommon.ReqQuitDuplicate(actor)
  228. else
  229. -- 延迟执行复活
  230. local delaySec = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornTime", "repId", configId)
  231. local delayMillis = tonumber(delaySec) * 1000
  232. intervalcalldelay(actor, delayMillis, 0, 1, "redfortressdorelive")
  233. end
  234. end
  235. function redfortressdorelive(actor)
  236. playrevive(actor, 7, 3)
  237. end
  238. function redfortresskillnotinrange(system, mapId, isFinal)
  239. local area = getenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA)
  240. local x1 = tonumber(area[1])
  241. local x2 = tonumber(area[2])
  242. local y1 = tonumber(area[3])
  243. local y2 = tonumber(area[4])
  244. -- 不在区域内的怪物死亡
  245. local monsterInfoList = mapbossinfo(mapId)
  246. for key, monInfo in pairs(monsterInfoList) do
  247. local monId = monInfo["id"]
  248. local px = monInfo["x"]
  249. local py = monInfo["y"]
  250. local isIn = this.IsPointInArea(px, py, x1, x2, y1, y2)
  251. if isIn == false then
  252. local monster = getactor(monId, mapId)
  253. removemapobject(monster)
  254. end
  255. end
  256. -- 缩圈阻挡点
  257. this.ShrinkingCircleBlockPoint(system, mapId)
  258. local players = getmapplayer(mapId)
  259. for key, actor in pairs(players) do
  260. local px = getbaseinfo(actor, "x")
  261. local py = getbaseinfo(actor, "y")
  262. local isIn = this.IsPointInArea(px, py, x1, x2, y1, y2)
  263. if isIn == false then
  264. sethp(actor, 0)
  265. local dupInfo = getduplicate(mapId)
  266. local configId = dupInfo["dupcfgid"]
  267. -- 延迟执行复活
  268. local delaySec = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornTime", "repId", configId)
  269. local delayMillis = tonumber(delaySec) * 1000
  270. intervalcalldelay(actor, delayMillis, 0, 1, "redfortressdorelive")
  271. end
  272. end
  273. if isFinal then
  274. local dupInfo = getduplicate(mapId)
  275. local configId = dupInfo["dupcfgid"]
  276. local maxCount = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornChance", "repId", configId)
  277. maxCount = tonumber(maxCount)
  278. for key, player in pairs(players) do
  279. setplaydef(player, RedFortressPlayerConst.RELIVE_TIMES, maxCount)
  280. clearallbuff(player)
  281. end
  282. end
  283. end
  284. -- @description 缩圈阻挡点
  285. -- @param
  286. -- @return
  287. function this.ShrinkingCircleBlockPoint(system, mapId)
  288. local area = getenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA)
  289. local x1 = tonumber(area[1]) - 1
  290. local x2 = tonumber(area[2]) + 1
  291. local y1 = tonumber(area[3]) - 1
  292. local y2 = tonumber(area[4]) + 1
  293. for x = x1, x2 do
  294. setblockpoint(x, y1, true, mapId) -- 下边
  295. end
  296. for y = y1 + 1, y2 - 1 do
  297. setblockpoint(x2, y, true, mapId) -- 右边(不包括顶点)
  298. end
  299. for x = x2 - 1, x1 + 1, -1 do
  300. setblockpoint(x, y2, true, mapId) -- 上边(不包括顶点)
  301. end
  302. for y = y2 - 1, y1 + 1, -1 do
  303. setblockpoint(x1, y, true, mapId) -- 左边(不包括顶点)
  304. end
  305. -- 设置四个顶点
  306. setblockpoint(x1, y1, true, mapId) -- 左下角
  307. setblockpoint(x2, y1, true, mapId) -- 右下角
  308. setblockpoint(x1, y2, true, mapId) -- 左上角
  309. setblockpoint(x2, y2, true, mapId) -- 右上角
  310. end
  311. -- @description 玩家复活事件
  312. -- @param 玩家对象
  313. -- @return
  314. function RedFortress.PlayerRelive(actor)
  315. local mapId = getbaseinfo(actor, "unimapid")
  316. local dupInfo = getduplicate(mapId)
  317. if dupInfo == nil then
  318. return
  319. end
  320. local type = dupInfo["type"]
  321. if type ~= DuplicateType.RED_FORTRESS then
  322. return
  323. end
  324. local configId = dupInfo["dupcfgid"]
  325. local maxCount = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornChance", "repId", configId)
  326. maxCount = tonumber(maxCount)
  327. -- 扣除复活次数
  328. local oldCount = getplaydef(actor, RedFortressPlayerConst.RELIVE_TIMES)
  329. local newCount = oldCount + 1
  330. if newCount > maxCount then
  331. newCount = maxCount
  332. end
  333. setplaydef(actor, RedFortressPlayerConst.RELIVE_TIMES, newCount)
  334. -- 传送
  335. --随机点位
  336. local x, y = this.RandomFightPointXY(mapId, 100)
  337. maptransfer(actor, x, y, 0, 0, 0)
  338. -- 被击杀图标
  339. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_KILL_RELIVE, { NoteType.RELIVE, newCount })
  340. -- 面板消息
  341. this.ResPlayerTaskInfo(actor, mapId)
  342. end
  343. -- @description 地图玩家怪物数量改变
  344. -- @param 地图id;偏移量
  345. -- @return
  346. function this.LeftCountChange(mapId, offset)
  347. -- 回包
  348. local dupInfo = getduplicate(mapId)
  349. local state = dupInfo["state"]
  350. local players = dupInfo["players"]
  351. local configId = dupInfo["dupcfgid"]
  352. local monCount = getmoncount(mapId)
  353. local totalCount = tonumber(monCount) + #players - offset
  354. if state == DuplicateState.PREPARE then
  355. elseif state == DuplicateState.FIGHT then
  356. this.ResAllPlayersTaskInfo(mapId, totalCount)
  357. -- 只剩最后一个玩家,则结束副本
  358. if totalCount <= 1 then
  359. setduplicatestate(mapId, SetDuplicateStateConst.TO_FINISH)
  360. return
  361. end
  362. -- 阶段更新,地形,
  363. local phase = getenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_PHASE)
  364. local mapCutConfig = ConfigDataManager.getTableValue("cfg_repRedfortress", "mapCut", "repId", configId)
  365. local mapCutList = string.split(mapCutConfig, "|")
  366. if phase <= #mapCutList then
  367. local mapCutItemStr = mapCutList[phase]
  368. local mapCutItemList = string.split(mapCutItemStr, "#")
  369. local countCondition = mapCutItemList[1]
  370. if totalCount < tonumber(countCondition) then
  371. -- 阶段更新,地形
  372. local percentage = mapCutItemList[2]
  373. local area = getenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA)
  374. local oldX1 = tonumber(area[1])
  375. local oldX2 = tonumber(area[2])
  376. local oldY1 = tonumber(area[3])
  377. local oldY2 = tonumber(area[4])
  378. local newX1, newX2, newY1, newY2 = this.ShrinkRectangle(oldX1, oldX2, oldY1, oldY2, tonumber(percentage))
  379. -- 取整
  380. newX1 = math.floor(newX1)
  381. newX2 = math.floor(newX2)
  382. newY1 = math.floor(newY1)
  383. newY2 = math.floor(newY2)
  384. local newArea = { newX1, newX2, newY1, newY2 }
  385. setenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA, newArea)
  386. -- 回包
  387. this.ResAllShrinkingStage(mapId, phase, newArea)
  388. -- 延迟5秒,击杀所有不在区域内的玩家
  389. local warningCfg = ConfigDataManager.getTableValue("cfg_repRedfortress", "warning", "repId", configId)
  390. local warningList = string.split(warningCfg, "#")
  391. local delayMillis = tonumber(warningList[phase]) * 1000
  392. if phase >= #mapCutList then
  393. -- 如果是最后一个阶段,扣除玩家所有复活次数和增益效果
  394. -- local maxCount = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornChance", "repId", configId)
  395. -- for key, player in pairs(players) do
  396. -- setplaydef(player, RedFortressPlayerConst.RELIVE_TIMES, maxCount)
  397. -- clearallbuff(player)
  398. -- end
  399. setenvirontimer(mapId, delayMillis, 0, 1, "redfortresskillnotinrange", mapId, true)
  400. else
  401. setenvirontimer(mapId, delayMillis, 0, 1, "redfortresskillnotinrange", mapId, false)
  402. end
  403. setenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_PHASE, phase + 1)
  404. end
  405. end
  406. end
  407. end
  408. -- @description 玩家进入任意地图事件
  409. -- @param 玩家对象;上一个地图配置id;当前地图配置id
  410. -- @return
  411. function RedFortress.EnterAllMap(actor, lastMapCfgId, mapCfgId)
  412. -- 判断发送副本结算
  413. local dupInfo = getplaydef(actor, RedFortressPlayerConst.MAP_INFO)
  414. if dupInfo == nil or dupInfo == "" then
  415. return
  416. end
  417. local bloodyId = dupInfo[1]
  418. local mapId = getbaseinfo(actor, "unimapid")
  419. local mapInfo = getmapinfobyid(mapId)
  420. local isDup = mapInfo["isdup"]
  421. if isDup == true and bloodyId ~= mapId then
  422. error(actor:toString() .. "赤色要塞有未结算的奖励")
  423. RedFortress.ClearPlayerDef(actor)
  424. return
  425. end
  426. if bloodyId ~= mapId then
  427. -- 结算
  428. this.Settlement(actor)
  429. end
  430. end
  431. -- @description 结算
  432. -- @param 玩家对象
  433. -- @return
  434. function this.Settlement(actor)
  435. local dupInfo = getplaydef(actor, RedFortressPlayerConst.MAP_INFO)
  436. if dupInfo == nil or dupInfo == "" then
  437. error("赤色要塞副本结算找不到副本信息")
  438. return
  439. end
  440. local killCount = getplaydef(actor, RedFortressPlayerConst.KILL_COUNT)
  441. local rank = getplaydef(actor, RedFortressPlayerConst.PLAYER_RANK)
  442. -- 发邮件奖励
  443. local configId = dupInfo[2]
  444. local rewardCfg = ConfigDataManager.getTableValue("cfg_repRedfortress", "reward", "repId", configId)
  445. local rewardRank = string.split(rewardCfg, "|")
  446. local itemBing = ConfigDataManager.getTableValue("cfg_bind", "bind", "id", 9)
  447. for key, value in pairs(rewardRank) do
  448. local innerList = string.split(value, "#")
  449. local min = tonumber(innerList[1])
  450. local max = tonumber(innerList[2])
  451. local itemMap = {}
  452. for i = 3, #innerList, 2 do
  453. if i + 1 <= #innerList then
  454. itemMap[tonumber(innerList[i])] = tonumber(innerList[i + 1])
  455. end
  456. end
  457. if min <= rank and rank <= max then
  458. sendconfigmailbyrid(actor, actor:toString(), MailConfig.RED_FORTRESS_REWARD, itemMap, rank, itemBing)
  459. break
  460. end
  461. end
  462. -- 发包
  463. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_SETTLEMENT_PANEL, { killCount, rank })
  464. -- 清楚玩家变量
  465. RedFortress.ClearPlayerDef(actor)
  466. end
  467. function RedFortress.ClearPlayerDef(actor)
  468. setplaydef(actor, RedFortressPlayerConst.MAP_INFO, nil)
  469. setplaydef(actor, RedFortressPlayerConst.RELIVE_TIMES, 0)
  470. setplaydef(actor, RedFortressPlayerConst.KILL_SCORES, 0)
  471. setplaydef(actor, RedFortressPlayerConst.KILL_COUNT, 0)
  472. setplaydef(actor, RedFortressPlayerConst.PLAYER_RANK, 0)
  473. end
  474. -- @description 准备阶段人数响应
  475. -- @param 地图id
  476. -- @return
  477. function this.ResAllPreparePlayerCount(mapId)
  478. local dupInfo = getduplicate(mapId)
  479. local state = dupInfo["state"]
  480. if state ~= DuplicateState.PREPARE then
  481. return
  482. end
  483. local players = dupInfo["players"]
  484. for index, actor in ipairs(players) do
  485. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_PREPARE_COUNT, #players)
  486. end
  487. end
  488. -- @description 响应给客户端当前阶段
  489. -- @param 玩家对象;地图id
  490. -- @return
  491. function this.ResCurrencyStateInfo(actor, mapId)
  492. local dupInfo = getduplicate(mapId)
  493. local state = dupInfo["state"]
  494. local configId = dupInfo["dupcfgid"]
  495. local nextStateStartTime = dupInfo["nextstatetime"]
  496. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_STATE_UPDATE, { state, tostring(nextStateStartTime), configId })
  497. end
  498. -- @description 通知地图所有玩家更新当前阶段
  499. -- @param 地图id
  500. -- @return
  501. function this.ResAllPlayCurrencyStateInfo(mapId)
  502. local dupInfo = getduplicate(mapId)
  503. local players = dupInfo["players"]
  504. for index, actor in ipairs(players) do
  505. this.ResCurrencyStateInfo(actor, mapId)
  506. end
  507. end
  508. -- @description 通知当前玩家任务数据
  509. -- @param 地图id
  510. -- @return
  511. function this.ResPlayerTaskInfo(actor, mapId, totalCount)
  512. totalCount = tonumber(totalCount)
  513. if totalCount == nil or totalCount <= 0 then
  514. local monCount = getmoncount(mapId)
  515. local players = getmapplayer(mapId)
  516. totalCount = monCount + #players
  517. end
  518. -- 剩余复活次数
  519. local reliveCount = getplaydef(actor, RedFortressPlayerConst.RELIVE_TIMES)
  520. local dupInfo = getplaydef(actor, RedFortressPlayerConst.MAP_INFO)
  521. local configId = dupInfo[2]
  522. local maxCount = ConfigDataManager.getTableValue("cfg_repRedfortress", "rebornChance", "repId", configId)
  523. local leftCount = maxCount - reliveCount
  524. -- 协议待定
  525. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_TASK_INFO, { totalCount, leftCount })
  526. end
  527. -- @description 通知地图所有玩家任务数据
  528. -- @param 地图id
  529. -- @return
  530. function this.ResAllPlayersTaskInfo(mapId, totalCount)
  531. local dupInfo = getduplicate(mapId)
  532. local players = dupInfo["players"]
  533. for index, actor in ipairs(players) do
  534. this.ResPlayerTaskInfo(actor, mapId, totalCount)
  535. end
  536. end
  537. -- @description 通知地图所有玩家缩圈信息
  538. -- @param 地图id
  539. -- @return
  540. function this.ResAllShrinkingStage(mapId, phase, newArea)
  541. local dupInfo = getduplicate(mapId)
  542. local players = dupInfo["players"]
  543. for index, actor in ipairs(players) do
  544. sendluamsg(actor, LuaMessageIdToClient.RES_RED_FORTRESS_SHRINKING_STAGE, { phase, newArea })
  545. end
  546. end
  547. -- @description 找一个战斗区域可用的点
  548. -- @param 地图id;递归次数
  549. -- @return
  550. function this.RandomFightPointXY(mapId, times)
  551. -- 设置递归最大次数
  552. local maxTimes = 100
  553. if times > maxTimes then
  554. times = maxTimes
  555. end
  556. local area = getenvirvar(mapId, DuplicateVarConst.RED_FORTRESS_FIGHT_AREA)
  557. -- 检查 area 数据格式
  558. if not area or #area ~= 4 then
  559. gameDebug.printTraceback("获取战斗区域错误", area)
  560. return 0, 0
  561. end
  562. local x1 = tonumber(area[1])
  563. local x2 = tonumber(area[2])
  564. local y1 = tonumber(area[3])
  565. local y2 = tonumber(area[4])
  566. -- 检查转换后的数据是否为数字
  567. if not (x1 and x2 and y1 and y2) then
  568. gameDebug.printTraceback("参数错误", x1, x2, y1, y2)
  569. return 0, 0
  570. end
  571. local randomX = this.GetRandomInt(x1, x2)
  572. local randomY = this.GetRandomInt(y1, y2)
  573. local notEmpty = isnotemptyinmap(mapId, randomX, randomY)
  574. local notBlock = notblockpoint(randomX, randomY, mapId)
  575. if notEmpty == false or notBlock == false then
  576. if times > 0 then
  577. times = times - 1
  578. return this.RandomFightPointXY(mapId, times)
  579. else
  580. return 0, 0
  581. end
  582. else
  583. return randomX, randomY
  584. end
  585. end
  586. function this.GetRandomInt(a, b)
  587. -- 确保 a 小于等于 b
  588. if a > b then
  589. a, b = b, a
  590. end
  591. -- 生成 (a, b) 区间内的随机整数
  592. return math.random(a + 1, b - 1)
  593. end
  594. -- @description 计算缩小后的坐标点
  595. -- @param x1, x2, y1, y2坐标点;缩小的百分比,例如20
  596. -- @return x1, x2, y1, y2坐标点
  597. function this.ShrinkRectangle(x1, x2, y1, y2, percentage)
  598. if x1 > x2 then
  599. x1, x2 = x2, x1
  600. end
  601. if y1 > y2 then
  602. y1, y2 = y2, y1
  603. end
  604. local r = percentage / 100
  605. local center_x = (x1 + x2) / 2
  606. local center_y = (y1 + y2) / 2
  607. local width = math.abs(x2 - x1)
  608. local height = math.abs(y2 - y1)
  609. local new_width = width * (1 - r)
  610. local new_height = height * (1 - r)
  611. local x1_new = center_x - new_width / 2
  612. local y1_new = center_y - new_height / 2
  613. local x2_new = center_x + new_width / 2
  614. local y2_new = center_y + new_height / 2
  615. return x1_new, x2_new, y1_new, y2_new
  616. end
  617. -- @description 判断坐标点在不在矩形区域内
  618. -- @param
  619. -- @return
  620. function this.IsPointInArea(px, py, x1, x2, y1, y2)
  621. if x1 > x2 then
  622. x1, x2 = x2, x1
  623. end
  624. if y1 > y2 then
  625. y1, y2 = y2, y1
  626. end
  627. local isIn = not (px < x1 or px > x2 or py < y1 or py > y2)
  628. return isIn
  629. end
  630. -- @description 获取积分排名列表
  631. -- @param 地图id
  632. -- @return 列表<玩家id字符串;排名>
  633. function this.GetScoreRanking(mapId)
  634. local players = getmapplayer(mapId)
  635. local playerList = {}
  636. for index, actor in ipairs(players) do
  637. local score = getplaydef(actor, RedFortressPlayerConst.KILL_SCORES)
  638. table.insert(playerList, { actor = actor, score = score })
  639. end
  640. -- 按积分从高到低排序,如果积分相同,保持原有顺序
  641. table.sort(
  642. playerList,
  643. function(a, b)
  644. return a.score > b.score
  645. end
  646. )
  647. -- 计算排名
  648. local ranks = {}
  649. local rank = 1 -- 当前排名
  650. local sameRankCount = 0 -- 相同积分的计数
  651. local lowestRank = 1 -- 当前积分组的最低排名
  652. for i = 1, #playerList do
  653. local player = playerList[i]
  654. local nextPlayer = playerList[i + 1]
  655. -- 判断下一名玩家是否与当前玩家积分相同
  656. if nextPlayer and player.score == nextPlayer.score then
  657. sameRankCount = sameRankCount + 1
  658. else
  659. -- 更新最低排名为当前排名加上相同积分玩家的数量
  660. lowestRank = rank + sameRankCount
  661. -- 将当前积分相同的所有玩家设为最低排名
  662. for j = i - sameRankCount, i do
  663. ranks[playerList[j].actor.toString] = lowestRank
  664. end
  665. -- 更新排名和计数
  666. rank = lowestRank + 1
  667. sameRankCount = 0
  668. end
  669. end
  670. return ranks
  671. end
  672. -- @description 获取指定玩家的积分排名
  673. -- @param 玩家对象;地图id
  674. -- @return 排名
  675. function this.GetPlayerRank(actor, mapId)
  676. local ranking = this.GetScoreRanking(mapId)
  677. return ranking[actor.toString]
  678. end
  679. -- @description 计算并设置玩家积分
  680. -- @param 地图id
  681. -- @return
  682. function this.CalcuAndSetPlayerPointRank(mapId)
  683. local players = getmapplayer(mapId)
  684. if #players <= 0 then
  685. return
  686. end
  687. local playerList = {}
  688. for index, actor in ipairs(players) do
  689. local score = getplaydef(actor, RedFortressPlayerConst.KILL_SCORES)
  690. table.insert(playerList, { actor = actor, score = score })
  691. end
  692. -- 按积分从高到低排序,如果积分相同,保持原有顺序
  693. table.sort(
  694. playerList,
  695. function(a, b)
  696. return a.score > b.score
  697. end
  698. )
  699. -- 计算排名
  700. local rank = 1 -- 当前排名
  701. local sameRankCount = 0 -- 相同积分的计数
  702. local lowestRank = 1 -- 当前积分组的最低排名
  703. if #playerList > 1 then
  704. for i = 1, #playerList do
  705. local player = playerList[i]
  706. local nextPlayer = playerList[i + 1]
  707. -- 判断下一名玩家是否与当前玩家积分相同
  708. if nextPlayer and player.score == nextPlayer.score then
  709. sameRankCount = sameRankCount + 1
  710. else
  711. -- 更新最低排名为当前排名加上相同积分玩家的数量
  712. lowestRank = rank + sameRankCount
  713. -- 将当前积分相同的所有玩家设为最低排名
  714. for j = i - sameRankCount, i do
  715. local actor = playerList[j].actor
  716. setplaydef(actor, RedFortressPlayerConst.PLAYER_RANK, lowestRank)
  717. end
  718. -- 更新排名和计数
  719. rank = lowestRank + 1
  720. sameRankCount = 0
  721. end
  722. end
  723. else
  724. local player = playerList[1].actor
  725. setplaydef(player, RedFortressPlayerConst.PLAYER_RANK, 1)
  726. end
  727. end
  728. function test_rolandchangestate(actor, state)
  729. local mapId = getbaseinfo(actor, "unimapid")
  730. setduplicatestate(mapId, state)
  731. end