WorldBoss.lua 32 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. WorldBoss = {}
  2. local this = {}
  3. -- 报名过的玩家
  4. local SIGNIN_ALL_PLAYER = "G$SignInWorldBossPlayers"
  5. local MonLowHp = "G$_WorldBossMonsterLowHP"
  6. local CurBossRepId = "R$_WorldBoss_Cur_Activity_ID"
  7. local Activity_ID = 23001 -- 世界BOSS活动ID
  8. local Activity_TYPE = 30001 -- 世界BOSS活动类型
  9. local AucMailConst = {
  10. SUCCESS = 102061,
  11. FAIL = 102062,
  12. A_BONUS = 102063
  13. }
  14. function WorldBoss.start()
  15. local curRepId = WorldBoss.UpDataCurRepId()
  16. local nullActor = getactor(0, 0)
  17. local mapId = DuplicateCommon.CreateDupMapCommon(nullActor, curRepId, true)
  18. local x, y = DuplicateCommon.GetEnterPointXYCommon(curRepId)
  19. jprint("世界BOSS副本开始", mapId, x, y)
  20. end
  21. function WorldBoss.UpDataCurRepId()
  22. local openDay = getbaseinfo("serveropendays")
  23. local cfgDay = tonumber(ConfigDataManager.getTableValue("cfg_repGlobal", "value", "id", 23001002))
  24. if openDay < cfgDay then
  25. return 0
  26. end
  27. local repId = getsysvar(CurBossRepId) or 0
  28. local id = Activity_ID
  29. if repId > 0 then
  30. id = repId + 1
  31. local monsterId = ConfigDataManager.getTableValue("cfg_rep", "monster", "id", id)
  32. if monsterId == nil or monsterId == "" then
  33. id = Activity_ID
  34. end
  35. end
  36. setsysvar(CurBossRepId,id)
  37. return id
  38. end
  39. function WorldBoss.stop()
  40. local existDupList = getduplicatelist(Activity_TYPE)
  41. if table.isEmpty(existDupList) then
  42. return
  43. end
  44. local mapId = existDupList[1].id
  45. setduplicatestate(mapId, SetDuplicateStateConst.TO_FINISH)
  46. setduplicatestate(mapId, SetDuplicateStateConst.TO_CLOSED)
  47. closeactivity(DuplicateType.WORLD_BOSS)
  48. end
  49. function WorldBoss.Run(action)
  50. -- local serverType = getbaseinfo("servertype")
  51. if tonumber(action) == 1 then
  52. WorldBoss.start()
  53. elseif tonumber(action) == 0 then
  54. WorldBoss.stop()
  55. -- elseif tonumber(action) == 2 then
  56. -- -- 调试:查看排行榜
  57. -- local existDupList = getduplicatelist(Activity_TYPE)
  58. -- if not table.isEmpty(existDupList) then
  59. -- local mapId = existDupList[1].id
  60. -- WorldBoss.DebugRankList(mapId)
  61. -- end
  62. -- elseif tonumber(action) == 3 then
  63. -- -- 调试:手动分配奖励(仅测试用)
  64. -- local existDupList = getduplicatelist(Activity_TYPE)
  65. -- if not table.isEmpty(existDupList) then
  66. -- local mapId = existDupList[1].id
  67. -- WorldBoss.DistributeRewards(mapId, 10000)
  68. -- end
  69. end
  70. end
  71. function WorldBoss.DupStateUpdate(system, mapId, state, nextStateStartTime, configId)
  72. if state == DuplicateState.PREPARE then
  73. -- info("WorldBoss副本进入准备阶段, mapId:", mapId, " state:", state, " nextStateStartTime:", nextStateStartTime, " configId:", configId)
  74. -- 准备阶段
  75. elseif state == DuplicateState.FIGHT then
  76. -- info("WorldBoss副本进入战斗阶段, mapId:", mapId, " state:", state, " nextStateStartTime:", nextStateStartTime, " configId:", configId)
  77. -- 战斗阶段
  78. local monsterId = ConfigDataManager.getTableValue("cfg_rep", "monster", "id", configId)
  79. DuplicateCommon.DupGenMonsterCommon(mapId, monsterId) --刷怪
  80. WorldBoss.upDataState(mapId)
  81. elseif state == DuplicateState.FINISH then
  82. -- info("WorldBoss副本进入结算阶段, mapId:", mapId, " state:", state, " nextStateStartTime:", nextStateStartTime, " configId:", configId)
  83. -- 结束阶段
  84. elseif state == DuplicateState.CLOSED then
  85. -- info("WorldBoss副本进入销毁阶段, mapId:", mapId, " state:", state, " nextStateStartTime:", nextStateStartTime, " configId:", configId)
  86. end
  87. end
  88. -- 更新当前状态
  89. function WorldBoss.upDataState(mapId)
  90. local players = getenvirvar(mapId, SIGNIN_ALL_PLAYER) or {}
  91. for k, v in pairs(players) do
  92. local actor = getactor(k)
  93. if actor then
  94. WorldBoss.SendWorldBossInfo(actor)
  95. end
  96. end
  97. end
  98. function WorldBoss.ReqWorldBossMsg(actor, msgData)
  99. local reqType = msgData["type"]
  100. if reqType == 1 then
  101. WorldBoss.SendWorldBossInfo(actor)
  102. elseif reqType == 2 then
  103. WorldBoss.ReqSignInWorldBoss(actor)
  104. end
  105. end
  106. -- 活动报名
  107. function WorldBoss.ReqSignInWorldBoss(actor)
  108. -- 检查是否拥有战盟
  109. local guildid = getbaseinfo(actor, "guildid")
  110. if guildid == 0 then
  111. tipinfo(actor, "不在战盟中")
  112. return
  113. end
  114. local existDupList = getduplicatelist(Activity_TYPE)
  115. if table.isEmpty(existDupList) then
  116. tipinfo(actor, "活动未开启,无法报名")
  117. return
  118. end
  119. local state = existDupList[1].state
  120. if state ~= DuplicateState.PREPARE then
  121. tipinfo(actor, "非报名时间,无法报名")
  122. return
  123. end
  124. local mapId = existDupList[1].id
  125. local players = getenvirvar(mapId, SIGNIN_ALL_PLAYER) or {}
  126. local rid = getbaseinfo(actor, "rid")
  127. if players[rid] then
  128. tipinfo(actor, "已报名,无需重复操作")
  129. return
  130. end
  131. players[rid] = 0
  132. setenvirvar(mapId, SIGNIN_ALL_PLAYER, players)
  133. WorldBoss.SendWorldBossInfo(actor)
  134. tipinfo(actor, "世界boss活动报名成功")
  135. end
  136. -- 发送当前状态信息
  137. function WorldBoss.SendWorldBossInfo(actor)
  138. local resData = {}
  139. local existDupList = getduplicatelist(Activity_TYPE)
  140. if table.isEmpty(existDupList) then
  141. return
  142. end
  143. -- id:副本地图唯一ID
  144. -- dupcfgid:配置表id,cfg_rep表的ID
  145. -- activityid:活动id
  146. -- players:玩家actor列表
  147. -- state:当前阶段
  148. -- nextstatetime:下一阶段开始时间戳(毫秒),
  149. -- type:副本类型
  150. local mapId = existDupList[1].id
  151. resData["signin"] = WorldBoss.GetSignInNUm(mapId)
  152. resData["mapid"] = mapId
  153. resData["dupcfgid"] = existDupList[1].dupcfgid
  154. resData["activityid"] = existDupList[1].activityid
  155. resData["state"] = existDupList[1].state
  156. resData["type"] = existDupList[1].type
  157. resData["nextstatetime"] = existDupList[1].nextstatetime
  158. sendluamsg(actor, LuaMessageIdToClient.RES_WORLD_BOSS_INFO, resData)
  159. end
  160. function WorldBoss.isWorldBoss(actor)
  161. local mapId = getbaseinfo(actor, "unimapid")
  162. local dupInfo = getduplicate(mapId)
  163. if not dupInfo then
  164. return false
  165. end
  166. local type = dupInfo["type"]
  167. if type ~= DuplicateType.WORLD_BOSS then
  168. return false
  169. end
  170. return true
  171. end
  172. -- 获取报名人数
  173. function WorldBoss.GetSignInNUm(mapId)
  174. local players = getenvirvar(mapId, SIGNIN_ALL_PLAYER) or {}
  175. return table.count(players)
  176. end
  177. -- @description 请求进入世界boss活动
  178. function WorldBoss.ReqEnterWorldBoss(actor)
  179. local activityInfo = getactivityinfo(Activity_TYPE)
  180. if not activityInfo["open"] then
  181. tipinfo(actor, "活动未开启")
  182. return
  183. end
  184. -- 检查是否拥有战盟
  185. local guildid = getbaseinfo(actor, "guildid")
  186. if guildid == 0 then
  187. tipinfo(actor, "不在战盟中")
  188. return
  189. end
  190. if WorldBoss.isWorldBoss(actor) then
  191. WbAuction.Publish()
  192. tipinfo(actor, "您已经在世界boss挑战中")
  193. return
  194. end
  195. -- 寻找是否有可进入的副本,如果没有创建副本
  196. local existDupList = getduplicatelist(Activity_TYPE)
  197. if table.isEmpty(existDupList) then
  198. tipinfo(actor, "活动副本未开启")
  199. return
  200. -- mapId = DuplicateCommon.CreateDupMapCommon(actor, Activity_ID, true)
  201. end
  202. local mapId = existDupList[1].id
  203. local curRepId = existDupList[1].dupcfgid
  204. local curState = existDupList[1].state
  205. if curState ~= DuplicateState.FIGHT then
  206. tipinfo(actor, "非战斗时间,无法进入")
  207. return
  208. end
  209. local players = getenvirvar(mapId, SIGNIN_ALL_PLAYER) or {}
  210. if not players[getbaseinfo(actor, "rid")] then
  211. tipinfo(actor, "未报名,无法进入")
  212. return
  213. end
  214. local LowNum = tonumber(ConfigDataManager.getTableValue("cfg_repGlobal", "value", "id", 23001001))
  215. if LowNum > 0 then
  216. local num = WorldBoss.GetSignInNUm(mapId)
  217. if num < LowNum then
  218. tipinfo(actor, "当前报名人数不足,无法进入")
  219. return
  220. end
  221. end
  222. if mapId ~= 0 and curRepId ~= 0 then
  223. local x, y = DuplicateCommon.GetEnterPointXYCommon(curRepId)
  224. enterduplicate(actor, mapId, x, y)
  225. end
  226. -- 设置pk状态
  227. setplayersetting(actor, 1, 1) --1 和平 5战盟
  228. end
  229. function WorldBoss.afterenterduplicate(actor, mapId, dupType, state, nextStateStartTime, configId)
  230. sendluamsg(actor, LuaMessageIdToClient.RES_WORLD_BOSS_RANK, {})
  231. end
  232. function WorldBoss.OnMonsterDie( mapId, killer, monCfgId)
  233. local timer = setenvirontimer(mapId,60000,1000,1,"closeworldboss",mapId)
  234. local exit = hasenvirtimer(mapId,timer)
  235. WbAuction.Publish()
  236. end
  237. -- 关闭世界boss副本
  238. function closeworldboss(_, mapId)
  239. -- info("boss死亡,关闭世界boss副本mapId:", mapId)
  240. end
  241. function WorldBoss.calculatePlayerRankList(mapId)
  242. local playerList = {}
  243. local guildList = {}
  244. local guildDamage = {}
  245. local guildName = {}
  246. local players = getenvirvar(mapId, SIGNIN_ALL_PLAYER)
  247. if players ~= nil then
  248. for id, damage in pairs(players) do
  249. local actor = getactor(id)
  250. local guildid = getbaseinfo(actor, "guildid") or 0
  251. guildName[guildid] = getbaseinfo(actor, "guildname") or ""
  252. -- playerList[id] = { damage = damage ,name = tostring(getbaseinfo(actor, "rolename")) }
  253. table.insert(playerList, {id = id, damage = damage ,name = tostring(getbaseinfo(actor, "rolename")) })
  254. -- local curDamage = guildList[guildid] and guildList[guildid].damage or 0
  255. -- guildList[guildid] = { damage = curDamage + damage ,name = getbaseinfo(actor, "guildname") }
  256. guildDamage[guildid] = (guildDamage[guildid] or 0) + damage
  257. end
  258. for k,v in pairs(guildDamage) do
  259. table.insert(guildList, {id = k, damage = v ,name = guildName[k] or "" })
  260. end
  261. end
  262. table.sort(playerList, this.SortFunc)
  263. table.sort(guildList, this.SortFunc)
  264. local topPlayerList = {}
  265. for i = 1, math.min(50, #playerList) do
  266. table.insert(topPlayerList, playerList[i])
  267. end
  268. local topGuildList = {}
  269. for i = 1, math.min(50, #guildList) do
  270. table.insert(topGuildList, guildList[i])
  271. end
  272. return topPlayerList,topGuildList
  273. end
  274. function this.SortFunc(a, b)
  275. if a.damage ~= b.damage then
  276. return b.damage < a.damage
  277. end
  278. end
  279. -- 世界BOSS奖励分配
  280. -- 总奖励:10000钻石
  281. -- 战盟分配:第1名50%,第2名30%,第3名20%
  282. -- 玩家分配:第1名30%,第2名25%,第3名20%,第4名10%,第5名5%,剩余平均分配
  283. function WorldBoss.DistributeRewards(totalReward)
  284. local existDupList = getduplicatelist(Activity_TYPE)
  285. if table.isEmpty(existDupList) then
  286. -- tipinfo(actor, "活动副本未开启")
  287. return
  288. end
  289. local mapId = existDupList[1].id
  290. if not totalReward or totalReward <= 0 then
  291. -- jprint("世界BOSS奖励分配失败:奖励金额无效", totalReward)
  292. return
  293. end
  294. local playerList, guildList = WorldBoss.calculatePlayerRankList(mapId)
  295. if table.isEmpty(playerList) or table.isEmpty(guildList) then
  296. -- jprint("世界BOSS奖励分配失败:没有玩家或战盟数据")
  297. return
  298. end
  299. -- jprint(string.format("=== 世界BOSS奖励分配开始,总奖励:%d钻石 ===", totalReward))
  300. -- 获取前3名战盟
  301. local top3Guilds = {}
  302. for i = 1, math.min(3, #guildList) do
  303. table.insert(top3Guilds, guildList[i])
  304. end
  305. -- 战盟分配比例
  306. local guildRewardRatios = {0.5, 0.3, 0.2} -- 50%, 30%, 20%
  307. -- 为每个前3名战盟计算奖励
  308. local guildRewards = {}
  309. local totalDistributedReward = 0
  310. for i, guild in ipairs(top3Guilds) do
  311. local guildTotalReward = totalReward * guildRewardRatios[i]
  312. totalDistributedReward = totalDistributedReward + guildTotalReward
  313. guildRewards[guild.id] = {
  314. totalReward = guildTotalReward,
  315. guildName = guild.name,
  316. guildRank = i,
  317. players = {}
  318. }
  319. -- jprint(string.format("第%d名战盟:%s,获得%.0f钻石(%.0f%%)",
  320. -- i, guild.name, guildTotalReward, guildRewardRatios[i] * 100))
  321. end
  322. -- 玩家分配比例(前5名)
  323. local playerRewardRatios = {0.3, 0.25, 0.2, 0.1, 0.05} -- 30%, 25%, 20%, 10%, 5%
  324. -- 为每个战盟的玩家分配奖励
  325. for guildId, guildData in pairs(guildRewards) do
  326. local guildPlayers = {}
  327. -- 获取该战盟的所有玩家(按照伤害排名)
  328. for _, player in ipairs(playerList) do
  329. local actor = getactor(player.id)
  330. local playerGuildId = getbaseinfo(actor, "guildid") or 0
  331. if playerGuildId == guildId then
  332. table.insert(guildPlayers, player)
  333. end
  334. end
  335. -- jprint(string.format(" 战盟%s共有%d名玩家参与", guildData.guildName, #guildPlayers))
  336. if #guildPlayers > 0 then
  337. -- 前5名玩家按比例分配
  338. local distributedReward = 0
  339. local top5Count = math.min(5, #guildPlayers)
  340. for i = 1, top5Count do
  341. local player = guildPlayers[i]
  342. local playerReward = guildData.totalReward * playerRewardRatios[i]
  343. distributedReward = distributedReward + playerReward
  344. table.insert(guildData.players, {
  345. playerId = player.id,
  346. playerName = player.name,
  347. damage = player.damage,
  348. rank = i,
  349. reward = playerReward,
  350. rewardType = i <= 5 and "排名奖励" or "参与奖励"
  351. })
  352. -- jprint(string.format(" 第%d名:%s,%.0f钻石(%.0f%%),伤害:%d",
  353. -- i, player.name, playerReward, playerRewardRatios[i] * 100, player.damage))
  354. end
  355. -- 剩余玩家平均分配
  356. if #guildPlayers > 5 then
  357. local remainingCount = #guildPlayers - 5
  358. local remainingReward = guildData.totalReward - distributedReward
  359. local avgReward = remainingReward / remainingCount
  360. -- jprint(string.format(" 剩余%d名玩家,每人平均获得%.0f钻石", remainingCount, avgReward))
  361. for i = 6, #guildPlayers do
  362. local player = guildPlayers[i]
  363. table.insert(guildData.players, {
  364. playerId = player.id,
  365. playerName = player.name,
  366. damage = player.damage,
  367. rank = i,
  368. reward = avgReward,
  369. rewardType = "参与奖励"
  370. })
  371. end
  372. end
  373. end
  374. end
  375. -- jprint(string.format("=== 奖励分配完成,总计分配%.0f钻石 ===", totalDistributedReward))
  376. -- 发送奖励
  377. WorldBoss.SendWorldBossRewards(guildRewards)
  378. return guildRewards
  379. end
  380. -- 发送世界BOSS奖励
  381. function WorldBoss.SendWorldBossRewards(guildRewards)
  382. -- jprint("=== 世界BOSS奖励分配开始 ===")
  383. for guildId, guildData in pairs(guildRewards) do
  384. -- jprint(string.format("战盟:%s,总奖励:%.0f钻石", guildData.guildName, guildData.totalReward))
  385. for _, playerData in ipairs(guildData.players) do
  386. local actor = getactor(playerData.playerId)
  387. if actor then
  388. -- 发送钻石奖励
  389. local rewardItems = { [10040001] = math.floor(playerData.reward) } -- 钻石ID和数量
  390. -- 发送奖励邮件
  391. -- local title = "世界BOSS奖励"
  392. -- local content = string.format("恭喜您在世界BOSS活动中获得排名奖励!\n战盟:%s\n个人排名:第%d名\n伤害值:%d\n获得钻石:%.0f", guildData.guildName, guildData.guildName, playerData.damage, playerData.reward)
  393. -- sendmailbycompleteitems(actor, playerData.playerId, title, content, rewardItems)--getbaseinfo(actor, "rid")
  394. local param = guildData.guildName .. "#" ..guildData.guildRank.. "#" .. math.floor(guildData.totalReward).. "#" .. playerData.rank
  395. sendconfigmailbyrid(actor, playerData.playerId, AucMailConst.A_BONUS, rewardItems, param)
  396. -- 记录日志
  397. -- jprint(string.format(" 玩家:%s,排名:%d,伤害:%d,奖励:%.0f钻石",
  398. -- playerData.playerName, playerData.rank, playerData.damage, playerData.reward))
  399. end
  400. end
  401. end
  402. -- jprint("=== 世界BOSS奖励分配结束 ===")
  403. end
  404. -- 调试函数:查看排行榜详情
  405. -- function WorldBoss.DebugRankList(mapId)
  406. -- local playerList, guildList = WorldBoss.calculatePlayerRankList(mapId)
  407. -- jprint("=== 世界BOSS战盟排行榜 ===")
  408. -- for i = 1, math.min(3, #guildList) do
  409. -- local guild = guildList[i]
  410. -- jprint(string.format("第%d名战盟:%s,伤害:%d", i, guild.name, guild.damage))
  411. -- end
  412. -- jprint("=== 世界BOSS玩家排行榜 ===")
  413. -- for i = 1, math.min(10, #playerList) do
  414. -- local player = playerList[i]
  415. -- local actor = getactor(player.id)
  416. -- local guildName = ""
  417. -- if actor then
  418. -- guildName = getbaseinfo(actor, "guildname") or "无战盟"
  419. -- end
  420. -- jprint(string.format("第%d名:%s(%s),伤害:%d", i, player.name, guildName, player.damage))
  421. -- end
  422. -- end
  423. -- 攻击事件
  424. function WorldBoss.Attack(actor, fightData)
  425. local mapId = getbaseinfo(actor, "unimapid")
  426. local dupInfo = getduplicate(mapId)
  427. if dupInfo == nil then
  428. return
  429. end
  430. local type = dupInfo["type"]
  431. if type ~= DuplicateType.WORLD_BOSS then
  432. return
  433. end
  434. local casterType = fightData.castertype
  435. local targetType = fightData.targettype
  436. if casterType ~= MapObjectType.PLAYER or targetType ~= MapObjectType.MONSTER then
  437. return
  438. end
  439. -- 累计伤害
  440. local players = getenvirvar(mapId, SIGNIN_ALL_PLAYER) or {}
  441. local rid = getbaseinfo(actor, "rid")
  442. players[rid] = (players[rid] or 0) + fightData.targethurt
  443. setenvirvar(mapId, SIGNIN_ALL_PLAYER, players)
  444. WorldBoss.CheckMonsterHp(actor,mapId,fightData.target)
  445. end
  446. -- 检查怪当前血量是否低于30%
  447. function WorldBoss.CheckMonsterHp(actor,mapId,Monste)
  448. local tip = getenvirvar(mapId, MonLowHp)
  449. if tonumber(tip) == 1 then
  450. return
  451. end
  452. local mInfo = getmonsterinfo(Monste)
  453. if mInfo == nil then
  454. return
  455. end
  456. local hp = mInfo["hp"]
  457. local maxhp = mInfo["maxhp"]
  458. local bossName = mInfo["name"]
  459. if (hp / maxhp) <= 0.3 then
  460. setenvirvar(mapId, MonLowHp, 1)
  461. -- 本地通广播提示
  462. noticeTip.noticeinfo(Monste, StringIdConst.text35010, bossName)
  463. setplayersetting(actor, 1, 5) --1 和平 5战盟
  464. end
  465. end
  466. function WorldBoss.DupSecondHeart(mapId, dupConfig, state)
  467. if state == DuplicateState.PREPARE then
  468. elseif state == DuplicateState.FIGHT then
  469. -- 刷新排行榜
  470. this.sendPanelRankData(mapId)
  471. return
  472. end
  473. end
  474. function this.sendPanelRankData(mapId)
  475. local resData = {}
  476. local rankList,guildList = WorldBoss.calculatePlayerRankList(mapId)
  477. resData["playerRank"] = rankList
  478. resData["guildRank"] = guildList
  479. local existDupList = getduplicatelist(Activity_TYPE)
  480. if table.isEmpty(existDupList) then
  481. return
  482. end
  483. local playerData = existDupList[1].players or {}
  484. for _, actor in pairs(playerData) do
  485. -- local actor = getactor(rid)
  486. sendluamsg(actor, LuaMessageIdToClient.RES_WORLD_BOSS_RANK, resData)
  487. end
  488. end
  489. -- ------------------------------------------------------------- --
  490. WbAuction = {}
  491. local aucDbFunc = {}
  492. local function __AuctionDbKey()
  493. return "R$_WORLD_BOSS_AUCTION_DATA"
  494. end
  495. local function _flushTime()
  496. return 5
  497. end
  498. local function _stallWay()
  499. return 11
  500. end
  501. function WbAuction.ontimer()
  502. this.onTimer()
  503. end
  504. function WbAuction.Publish()
  505. callonserial("_wb_auction_publish_goods")
  506. end
  507. function WbAuction.PlayerBidding(actor, msgData)
  508. callonserial(actor,"_wb_auction_player_bidding", msgData)
  509. end
  510. function WbAuction.login(actor)
  511. this.sendAuctionPanelData(actor)
  512. end
  513. -- 拍卖行上架道具
  514. function _wb_auction_publish_goods()
  515. if not this.auctionIsOpen() then
  516. return
  517. end
  518. local configList = ConfigDataManager.getTable("cfg_stall", 'way', _stallWay())
  519. if table.isEmpty(configList) then
  520. return
  521. end
  522. local repId = getsysvar(CurBossRepId) or Activity_ID
  523. local UIreward = ConfigDataManager.getTableValue("cfg_rep", "UIreward", "id", repId)
  524. local rewardMap = string.split(UIreward, "|")
  525. if table.isNullOrEmpty(rewardMap) then
  526. return
  527. end
  528. local now = getbaseinfo("now")
  529. local goodsMap = {}
  530. for _, config in pairs(configList) do
  531. local probability = string.tonumber(config["probability"])
  532. local cfgid = tonumber(config['id'])
  533. local count = tonumber(config['number'])
  534. local isup = false
  535. for _, uiId in pairs(rewardMap) do
  536. if tonumber(uiId) == cfgid then
  537. isup = true
  538. break
  539. end
  540. end
  541. if not isup then
  542. -- goto continue
  543. end
  544. if probability > 0 then
  545. local num = math.random(1, 10000)
  546. if probability < num then
  547. goto continue
  548. end
  549. end
  550. local goodsInfo = createitemsbymap({ [cfgid] = count })
  551. if table.isEmpty(goodsInfo) then
  552. goto continue
  553. end
  554. local endTime = now + tonumber(config['time']) * 60 * 1000
  555. for _, goods in pairs(goodsInfo) do
  556. goods['bidderId'] = 0
  557. goods['startTime'] = now
  558. goods['endTime'] = endTime
  559. goods['price'] = tonumber(config["startingprice"])
  560. goods['coinType'] = tonumber(config['money'])
  561. goods['fixedPrice'] = tonumber(config['fixedprice'])
  562. goods['auction'] = tonumber(config['auction'])
  563. goods['addTimes'] = tonumber(config['addtime'])
  564. goodsMap[goods['id']] = goods
  565. end
  566. :: continue ::
  567. end
  568. this.debug("---- 拍卖行上架道具 -----", goodsMap)
  569. aucDbFunc.setPublishGoods(goodsMap)
  570. GlobalTimer.setontimerex(TimerIds.WORLD_BOSS_AUCTION, _flushTime())
  571. -- local goods = aucDbFunc.getPublishGoods()
  572. -- this.sendOnLinePlayerPanel(this.sendAuctionPanelData, goods)
  573. end
  574. function _wb_auction_player_bidding(actor, msgData)
  575. local goodsId = string.tonumber(msgData['goodsId'])
  576. if goodsId < 1 then
  577. return
  578. end
  579. if not this.auctionIsOpen() then
  580. this.info(actor, "拍卖未开启")
  581. return
  582. end
  583. if not this.isParticipate(actor) then
  584. this.info(actor, "您未参与活动")
  585. return
  586. end
  587. local goodsMap = aucDbFunc.getPublishGoods()
  588. local goods = goodsMap[goodsId]
  589. if table.isEmpty(goods) then
  590. this.info(actor, "该商品已被购买或已下架")
  591. return
  592. end
  593. local now = getbaseinfo("now")
  594. local endTime = goods['endTime']
  595. if endTime < now then
  596. this.info(actor, "该商品已下架")
  597. return
  598. end
  599. local buyNow = string.tonumber(msgData['type']) == 1
  600. local rid = tonumber(actor:toString())
  601. local lastBidder = goods['bidderId']
  602. if not buyNow and lastBidder == rid then
  603. this.info(actor, "您已参与竞拍, 请勿重复参与")
  604. return
  605. end
  606. local bidPrice = goods["price"]
  607. local fixedPrice = tonumber(goods['fixedPrice'])
  608. local newPrice = buyNow and fixedPrice or this.calculateNewPrice(goods)
  609. local coinType = goods['coinType']
  610. local coinCount = getbagitemcountbyid(actor, coinType)
  611. if coinCount < newPrice then
  612. this.info(actor, "资源不足, 无法竞拍")
  613. return
  614. end
  615. removeitemfrombag(actor, coinType, newPrice, 0, 9999, "世界BOSS竞拍")
  616. goods['bidderId'] = rid
  617. goods['price'] = newPrice
  618. -- 一口价进入结算阶段
  619. if buyNow or newPrice == fixedPrice then
  620. this.toSettleAuction(goods)
  621. -- 结算并清理缓存
  622. goodsMap[goodsId] = nil
  623. goods['endTime'] = 0
  624. else
  625. -- 增加时间 保存变动内容
  626. local finalTime = (goods["addTimes"] or 0) * 1000 + endTime
  627. goods["endTime"] = finalTime
  628. goodsMap[goodsId] = goods
  629. end
  630. aucDbFunc.setPublishGoods(goodsMap)
  631. -- 返还上个竞拍玩家道具
  632. if lastBidder > 0 then
  633. this.bidFailMail(lastBidder, goodsId, coinType, bidPrice)
  634. end
  635. local result = { ["result"] = true, ["goods"] = goods }
  636. -- this.sendOnLinePlayerPanel(this.sendAuctionResult, result)
  637. this.sendAuctionResult(actor, result)
  638. end
  639. function this.sendAuctionResult(actor, result)
  640. sendluamsg(actor, LuaMessageIdToClient.RES_KUN_DUN_AUCTION_BUY_RESULT, result)
  641. end
  642. -- ------------------------------------------------------------- --
  643. function this.sendAuctionPanelData(actor, goods)
  644. local data = {}
  645. if goods == nil then
  646. goods = aucDbFunc.getPublishGoods()
  647. end
  648. -- this.debug("---- 发送拍卖行面板数据 -----", goods, this.auctionIsOpen(), this.isParticipate(actor))
  649. if this.auctionIsOpen() and this.isParticipate(actor) and table.notEmpty(goods) then
  650. data['isOpen'] = true
  651. data['goods'] = goods
  652. else
  653. data['isOpen'] = false
  654. end
  655. sendluamsg(actor, LuaMessageIdToClient.RES_WORLD_BOSS_AUCTION_GOODS, data)
  656. end
  657. function this.sendOnLinePlayerPanel(func, resData)
  658. local data = dataFunc.getAllKunDunPlayerData()
  659. this.debug("---- 获取所有玩家数据 -----", resData, data)
  660. for rid, _ in pairs(data) do
  661. local actor = getactor(rid)
  662. local onlineState = tonumber(getbaseinfo(actor, "onlinestate"))
  663. if onlineState == 1 then
  664. func(actor, resData)
  665. end
  666. end
  667. end
  668. function this.bidFailMail(bidderId, goodsId, costType, costNum)
  669. local itemName = ConfigDataManager.getTableValue("cfg_item", "name", "id", goodsId)
  670. local costName = ConfigDataManager.getTableValue("cfg_item", "name", "id", costType)
  671. local param = itemName .. "#" .. costName
  672. local actor = getactor(bidderId)
  673. sendconfigmailbyrid(actor, bidderId, AucMailConst.FAIL, { [costType] = costNum }, param)
  674. end
  675. function this.calculateNewPrice(goods)
  676. local bidPrice = goods["price"]
  677. local auction = goods["auction"] or 1
  678. local fixedPrice = goods["fixedPrice"]
  679. -- local newPrice = math.ceil(bidPrice * (1 + auction * 0.0001))
  680. local newPrice = bidPrice + auction
  681. this.debug("---- 拍卖行计算价格 -----", goods, bidPrice, auction, "|", "fixedPrice", fixedPrice, "newPrice", newPrice)
  682. return math.min(newPrice, fixedPrice)
  683. end
  684. function this.onTimer()
  685. local goodsData = aucDbFunc.getPublishGoods()
  686. -- this.debug("---- 拍卖行定时器 -----", goodsData)
  687. if table.notEmpty(goodsData) then
  688. local clearIdList = {}
  689. local now = string.tonumber(getbaseinfo("now"))
  690. for goodsId, goods in pairs(goodsData) do
  691. local endTime = goods["endTime"]
  692. if endTime < now then
  693. table.insert(clearIdList, goodsId)
  694. end
  695. end
  696. -- this.debug("---- 需要清理的拍卖行道具 -----", clearIdList)
  697. -- 结算到期竞拍道具
  698. for _, goodsId in pairs(clearIdList) do
  699. local goods = goodsData[goodsId]
  700. this.toSettleAuction(goods)
  701. goodsData[goodsId] = nil
  702. end
  703. aucDbFunc.setPublishGoods(goodsData)
  704. end
  705. -- 没有物品后关闭定时器
  706. if table.isEmpty(goodsData) then
  707. GlobalTimer.setofftimer(TimerIds.WORLD_BOSS_AUCTION)
  708. end
  709. end
  710. function this.toSettleAuction(goods)
  711. if table.isEmpty(goods) then
  712. return
  713. end
  714. local buyerId = goods['bidderId']
  715. if buyerId < 1 then
  716. return
  717. end
  718. -- 发放拍卖道具
  719. local buyer = getactor(buyerId)
  720. local mailConfig = ConfigDataManager.getById("cfg_mail", AucMailConst.SUCCESS)
  721. if table.notEmpty(mailConfig) then
  722. local title = mailConfig['title']
  723. local content = mailConfig['content']
  724. local itemName = ConfigDataManager.getTableValue("cfg_item", "name", "id", goods['cfgid'])
  725. content = string.format(content, itemName)
  726. sendmailbycompleteitems(buyer, buyerId, title, content, { goods })
  727. end
  728. -- 分红名单
  729. local ridList = {}
  730. -- -- 是否为稀有道具
  731. -- local rareItem = false
  732. -- if rareItem then
  733. -- local rankData = dataFunc.getKunDunFactionRankData()
  734. -- local faction = rankData[1]['faction']
  735. -- ridList = dataFunc.getFactionPlayers(faction)
  736. -- else
  737. -- local allPlayer = dataFunc.getAllKunDunPlayerData()
  738. -- for rid, _ in pairs(allPlayer) do
  739. -- table.insert(ridList, rid)
  740. -- end
  741. -- end
  742. local buyerName = getbaseinfo(buyer, "rolename")
  743. this.debug("---- 拍卖结算 ----", goods, buyerId, buyerName, "-- 分红名单 --", ridList)
  744. local cfgId = goods['cfgid']
  745. local coinType = goods['coinType']
  746. local price = goods['price']
  747. local taxStr = ConfigDataManager.getTableValue("cfg_stall", "tax", "id", cfgId, "way", _stallWay())
  748. this.debug("config_data", cfgId, _stallWay(), taxStr)
  749. local aBonus = this.calculationTax(price, taxStr)
  750. local getABonus = math.round(aBonus / #ridList)
  751. -- -- 发送邮件
  752. -- 分配世界BOSS奖励 (10000钻石)
  753. WorldBoss.DistributeRewards( aBonus)
  754. -- local reward = { [coinType] = aBonus }
  755. -- -- 解析文本配置
  756. -- local itemName = ConfigDataManager.getTableValue("cfg_item", "name", "id", cfgId)
  757. -- local priceTypeName = ConfigDataManager.getTableValue("cfg_item", "name", "id", coinType)
  758. -- local param = itemName .. "#" .. priceTypeName .. "#" .. aBonus .. "#" .. getABonus
  759. -- for _, rid in pairs(ridList) do
  760. -- if tonumber(rid) ~= buyerId then
  761. -- local player = getactor(rid)
  762. -- sendconfigmailbyrid(player, rid, AucMailConst.A_BONUS, reward, param)
  763. -- end
  764. -- end
  765. end
  766. -- 计算税率
  767. function this.calculationTax(price, taxStr)
  768. local tableTax = {}
  769. string.putIntIntMap(tableTax, taxStr)
  770. local deleteRate = tableTax[1]
  771. this.debug("---- 拍卖行税率 ----", price, taxStr, tableTax)
  772. local totalPrice = math.round(price * (100 - deleteRate) / 100)
  773. local deleteCount = tableTax[2]
  774. totalPrice = totalPrice - deleteCount
  775. return totalPrice
  776. end
  777. function this.auctionIsOpen()
  778. -- boss被击杀才开启
  779. -- local taskData = dataFunc.getKunDunTaskData()
  780. -- return taskData["stage"] == KunDun.TaskStage.DUP_FINISH
  781. return true
  782. end
  783. function this.isParticipate(actor)
  784. -- 参与了活动
  785. -- local data = dataFunc.getKunDunPlayerData(actor)
  786. -- return table.notEmpty(data)
  787. return true
  788. end
  789. function aucDbFunc.getAuctionData()
  790. -- return dataFunc.getCrossGlobalData(__AuctionDbKey())
  791. return getsysvar(__AuctionDbKey()) or {}
  792. end
  793. function aucDbFunc.setAuctionData(data)
  794. -- dataFunc.setCrossGlobalData(__AuctionDbKey(), data)
  795. setsysvar(__AuctionDbKey(), data)
  796. end
  797. function aucDbFunc.getPublishGoods()
  798. local data = aucDbFunc.getAuctionData()
  799. return data['publishgoods'] or {}
  800. end
  801. function aucDbFunc.setPublishGoods(goodsData)
  802. local data = aucDbFunc.getAuctionData()
  803. data['publishgoods'] = goodsData
  804. aucDbFunc.setAuctionData(data)
  805. end
  806. -- ------------------------------------------------------------- --
  807. this.log_open = true
  808. this.log_name = "[WorldBossAuction]"
  809. function this.debug(...)
  810. if not this.log_open then
  811. return
  812. end
  813. gameDebug.print(this.log_name, ...)
  814. end
  815. function this.print(...)
  816. if not this.log_open then
  817. return
  818. end
  819. if param == nil then
  820. param = "error! 输出内容为空. nil"
  821. end
  822. jprint(this.log_name, ...)
  823. end
  824. function this.info(actor, ...)
  825. tipinfo(actor, ...)
  826. this.print(...)
  827. end
  828. -- ------------------------------------------------------------- --