WorldBoss.lua 34 KB

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