OfflineOnHook.lua 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. --- 离线挂机
  2. local this = {}
  3. onHook = {}
  4. -- 离线挂机最大时长
  5. function onHook.getOfflineOnHookMaxTime(actor)
  6. local offlineTimeout =
  7. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_TIMEOUT)
  8. -- 月卡增加时长
  9. local addMinutes = ActMonthlyGrow.getAfkAddTime(actor) or 0
  10. offlineTimeout = offlineTimeout + addMinutes
  11. return offlineTimeout
  12. end
  13. --- 获取离线挂机信息
  14. ---@param actor 玩家对象
  15. function onHook.getOffLineOnHookInfo(actor)
  16. local onHookInfo = getonhookinfo(actor)
  17. local duration = tonumber(onHookInfo["duration"])
  18. local fightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  19. if string.isNullOrEmpty(fightExp) then
  20. fightExp = 0
  21. end
  22. local offlineStartTime = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  23. local offlineEndTime = getplaydef(actor, PlayerDefKey.offline.END_TIME)
  24. if not string.isNullOrEmpty(offlineEndTime) then
  25. duration = math.round((offlineEndTime - offlineStartTime) / 1000)
  26. end
  27. local offlineTimeout = onHook.getOfflineOnHookMaxTime(actor)
  28. local timeout = duration > offlineTimeout * 60
  29. duration = timeout and offlineTimeout * 60 or duration
  30. local secondDiff = this.calcBubblePointTime(actor, offlineTimeout)
  31. -- 泡点经验
  32. local bubblePointExp = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP)
  33. if string.isNullOrEmpty(bubblePointExp) then
  34. bubblePointExp = 0
  35. end
  36. onHookInfo["freeExp"] = tostring(bubblePointExp)
  37. -- 泡点时长与总离线挂机时长
  38. onHookInfo["freeDuration"] = math.round(secondDiff)
  39. onHookInfo["duration"] = math.round(duration)
  40. onHookInfo["fightDuration"] = math.round(duration - secondDiff)
  41. onHookInfo["fightExp"] = tostring(fightExp)
  42. local isReceive = getplaydef(actor, PlayerDefKey.offline.IS_RECEIVE)
  43. if string.isNullOrEmpty(isReceive) then
  44. isReceive = false
  45. end
  46. onHookInfo["receiveExp"] = isReceive
  47. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  48. if string.isNullOrEmpty(totalExp) then
  49. totalExp = 0
  50. end
  51. local expLimit =
  52. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT)
  53. local total = totalExp + bubblePointExp + fightExp
  54. if expLimit and tonumber(expLimit) < total then
  55. total = tonumber(expLimit)
  56. end
  57. onHookInfo["totalExp"] = tostring(total)
  58. local openBubbleTime = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START)
  59. local mapId = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP)
  60. if not string.isNullOrEmpty(openBubbleTime) and tonumber(openBubbleTime) >= tonumber(offlineStartTime) then
  61. local temp = {}
  62. temp[openBubbleTime] = mapId
  63. onHookInfo["openBubbleMap"] = temp
  64. end
  65. -- 如果后端服务重启,则需要重新计算挂机超时时间点
  66. if timeout then
  67. onHookInfo["timeout"] = offlineStartTime + offlineTimeout * TimeUnit.MINUTE * TimeUnit.MILLISECOND
  68. end
  69. sendluamsg(actor, LuaMessageIdToClient.RES_OFFLINE_ON_HOOK_INFO, onHookInfo)
  70. end
  71. --- 请求领取离线挂机经验
  72. ---@param actor 玩家对象
  73. function onHook.reqReceiveOfflineOnHookExp(actor)
  74. -- 领取过离线挂机经验直接返回
  75. if getplaydef(actor, PlayerDefKey.offline.IS_RECEIVE) then
  76. return
  77. end
  78. -- 计算角色泡点经验
  79. local bubblePointExp = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP)
  80. if string.isNullOrEmpty(bubblePointExp) then
  81. bubblePointExp = 0
  82. end
  83. local fightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  84. if string.isNullOrEmpty(fightExp) then
  85. fightExp = 0
  86. end
  87. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  88. if string.isNullOrEmpty(totalExp) then
  89. totalExp = 0
  90. end
  91. local exp = bubblePointExp + fightExp + totalExp
  92. local expLimit =
  93. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT)
  94. exp = exp > tonumber(expLimit) and tonumber(expLimit) or exp
  95. Bag.addItemToBag(actor, ItemConfigId.EXP, exp, 0, 9999, "离线挂机")
  96. setplaydef(actor, PlayerDefKey.offline.IS_RECEIVE, true)
  97. end
  98. --- 角色登录存储泡点结束时间与离线挂机结束时间
  99. ---@param actor 玩家对象
  100. function onHook.login(actor)
  101. onHook.setOfflineState(actor, 0)
  102. local offlineTimeout = onHook.getOfflineOnHookMaxTime(actor)
  103. local offlineStartTime = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  104. local offlineBubblePointStart = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START)
  105. local serverStart = getsysvar(SystemVarConst.SERVER_START)
  106. -- 角色离线挂机开始时间小于服务器最新启动时间则表示玩家离线挂机期间服务器重启过
  107. local now = getbaseinfo(actor, "now")
  108. local fightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  109. local isFirstLogin = getplaydef(actor, "T$_isResetReceiveFlag")
  110. if offlineStartTime and serverStart and tonumber(serverStart) > tonumber(offlineStartTime) and not isFirstLogin then
  111. -- 设置已经重置过领取经验标识
  112. setplaydef(actor, "T$_isResetReceiveFlag", 1)
  113. -- 设置领取经验为false,防止服务器重启领取经验标识为true,导致玩家无法领取离线挂机经验
  114. setplaydef(actor, PlayerDefKey.offline.IS_RECEIVE, false)
  115. -- 判断当前角色是否在安全区内,如果不在安全区,根据配表计算战斗经验
  116. if string.isNullOrEmpty(offlineBubblePointStart) then
  117. local lastTime = getplaydef(actor, PlayerDefKey.offline.LAST_TIME)
  118. local fightTime = tonumber(now) - tonumber(lastTime)
  119. local timeoutMillisecond = tonumber(offlineTimeout * TimeUnit.MINUTE * TimeUnit.MILLISECOND)
  120. fightTime = fightTime > timeoutMillisecond and timeoutMillisecond or fightTime
  121. local mapId = getbaseinfo(actor, "map")
  122. local patrolState = getplaydef(actor, PlayerDefKey.offline.PATROL_STATE)
  123. local monsterState = patrolState and 2 or 1
  124. local expConfig =
  125. ConfigDataManager.getTableValue(
  126. "cfg_hangupReboot",
  127. "monstergroup",
  128. "mapid",
  129. mapId,
  130. "monstername",
  131. monsterState
  132. )
  133. if not string.isNullOrEmpty(expConfig) then
  134. local second = string.split(expConfig, "#")[1]
  135. local exp = string.split(expConfig, "#")[2]
  136. local step = math.round((fightTime / 1000) / second)
  137. if step > 0 then
  138. local baseExp = fightExp and tonumber(fightExp) or 0
  139. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, baseExp + (tonumber(exp) * step))
  140. end
  141. end
  142. end
  143. end
  144. if offlineBubblePointStart then
  145. local offlineBubblePointEnd = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END)
  146. if string.isNullOrEmpty(offlineBubblePointEnd) and this.checkBubblePointArea(actor) then
  147. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END, now)
  148. -- 将离线泡点经验持久化,如果上次离线的泡点经验没有领取则累加
  149. local onHookInfo = getonhookinfo(actor)
  150. if table.isNullOrEmpty(onHookInfo) then
  151. return
  152. end
  153. local bubblePointExp = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP)
  154. local exp = this.calcBubblePointExp(actor, this.calcBubblePointTime(actor, offlineTimeout))
  155. if string.isNullOrEmpty(bubblePointExp) then
  156. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, exp)
  157. else
  158. local bubbleExp = tonumber(bubblePointExp) + exp
  159. if this.checkExpLimit(actor, fightExp, bubbleExp) then
  160. local expLimit =
  161. ConfigDataManager.getTableValue(
  162. "cfg_global",
  163. "value",
  164. "id",
  165. GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT
  166. )
  167. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  168. bubbleExp = tonumber(expLimit) - fightExp - totalExp
  169. end
  170. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, bubbleExp)
  171. end
  172. end
  173. end
  174. if offlineStartTime then
  175. local offlineEndTime = getplaydef(actor, PlayerDefKey.offline.END_TIME)
  176. if string.isNullOrEmpty(offlineEndTime) then
  177. setplaydef(actor, PlayerDefKey.offline.END_TIME, now)
  178. end
  179. end
  180. end
  181. --- 角色退出清空泡点相关信息
  182. ---@param actor 玩家对象
  183. function onHook.logout(actor)
  184. -- 角色重置过领取经验标识重置
  185. setplaydef(actor, "T$_isResetReceiveFlag", nil)
  186. -- 重置结束时间
  187. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END, nil)
  188. setplaydef(actor, PlayerDefKey.offline.END_TIME, nil)
  189. -- 取消离线后自动打怪,将挂机状态改为手动状态
  190. -- local mapId = getbaseinfo(actor, "mapid")
  191. -- local rebirthPointStr = ConfigDataManager.getTableValue("cfg_map_info", "rebirthPoint", "id", mapId)
  192. -- local rebirthPointArr = string.split(rebirthPointStr, "#")
  193. -- local pointX = rebirthPointArr[1] - math.floor(rebirthPointArr[3] / 2) + math.random(0, rebirthPointArr[3])
  194. -- local pointY = rebirthPointArr[2] - math.floor(rebirthPointArr[3] / 2) + math.random(0, rebirthPointArr[3])
  195. -- onhookstop(actor, mapId, pointX, pointY)
  196. -- onhookstop(actor, 1001, 0, 0)
  197. -- 领取过经验后重置存储的经验信息
  198. if getplaydef(actor, PlayerDefKey.offline.IS_RECEIVE) then
  199. setplaydef(actor, PlayerDefKey.offline.IS_RECEIVE, false)
  200. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, nil)
  201. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, nil)
  202. setplaydef(actor, PlayerDefKey.offline.TOTAL_EXP, nil)
  203. else
  204. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  205. if string.isNullOrEmpty(totalExp) then
  206. totalExp = 0
  207. end
  208. local bubblePointExp = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP)
  209. if not string.isNullOrEmpty(bubblePointExp) then
  210. totalExp = totalExp + bubblePointExp
  211. end
  212. local fightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  213. if not string.isNullOrEmpty(fightExp) then
  214. totalExp = totalExp + fightExp
  215. end
  216. local expLimit =
  217. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT)
  218. if expLimit and totalExp > tonumber(expLimit) then
  219. totalExp = tonumber(expLimit)
  220. end
  221. setplaydef(actor, PlayerDefKey.offline.TOTAL_EXP, totalExp)
  222. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, nil)
  223. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, nil)
  224. end
  225. local level = tonumber(getbaseinfo(actor, "level"))
  226. -- 是否达到开启离线挂机的等级
  227. local offlineOnHookLevel =
  228. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_LEVEL)
  229. if level >= tonumber(offlineOnHookLevel) then
  230. -- 记录离线挂机开启时间
  231. setplaydef(actor, PlayerDefKey.offline.START_TIME, getbaseinfo(actor, "now"))
  232. if this.checkBubblePointArea(actor) then
  233. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP, getbaseinfo(actor, "map"))
  234. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START, getbaseinfo(actor, "now"))
  235. else
  236. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP, nil)
  237. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START, nil)
  238. end
  239. end
  240. end
  241. --- 玩家传送后判断是否进入泡点区域
  242. ---@param actor 玩家对象
  243. function onHook.afterTransmit(actor)
  244. -- 非离线挂机玩家不处理
  245. if not isofflineplay(actor) then
  246. return
  247. end
  248. -- 是否达到开启离线挂机的等级,离线挂机传送只可能是免费复活后传送,所以此处不需要判断区域是否为泡点区域
  249. local offlineOnHookLevel =
  250. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_LEVEL)
  251. if tonumber(getbaseinfo(actor, "level")) >= tonumber(offlineOnHookLevel) then
  252. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP, getbaseinfo(actor, "map"))
  253. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START, getbaseinfo(actor, "now"))
  254. end
  255. end
  256. --- 检查区域是否是泡点区域
  257. ---@param actor 玩家对象
  258. ---@return boolean 是否在泡点区域
  259. function this.checkBubblePointArea(actor)
  260. local level = tonumber(getbaseinfo(actor, "level"))
  261. local mapId = getbaseinfo(actor, "map")
  262. local safeArea = getbaseinfo(actor, "safearea")
  263. local configString = ConfigDataManager.getTableValue("cfg_bubble_point", "expMap", "id", level)
  264. local configMap = string.toStringStringMap(configString, "#", "|")
  265. local checkArea = false
  266. for key, value in pairs(configMap) do
  267. mapId = tonumber(mapId)
  268. key = tonumber(key)
  269. value = tonumber(value)
  270. if mapId == key then
  271. if value == 3 then
  272. checkArea = true
  273. elseif value == 2 and safeArea == false then
  274. checkArea = true
  275. elseif value == 1 and safeArea == true then
  276. checkArea = true
  277. end
  278. end
  279. end
  280. return checkArea
  281. end
  282. --- 计算泡点时长
  283. ---@param actor 玩家对象
  284. ---@param duration 离线挂机战斗时长
  285. ---@return number 泡点时长(s)
  286. function this.calcBubblePointTime(actor, offlineTimeout)
  287. local offlineBubblePointStart = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START)
  288. local offlineBubblePointEnd = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END)
  289. if
  290. not string.isNullOrEmpty(offlineBubblePointStart) and not string.isNullOrEmpty(offlineBubblePointEnd) and
  291. tonumber(offlineBubblePointEnd) - tonumber(offlineBubblePointStart) > 0
  292. then
  293. local secondDiff = math.round((tonumber(offlineBubblePointEnd) - tonumber(offlineBubblePointStart)) / 1000)
  294. -- 泡点时长处理
  295. if secondDiff > tonumber(offlineTimeout) * 60 then
  296. secondDiff = tonumber(offlineTimeout) * 60
  297. local offlineStartTime = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  298. local diff = (offlineBubblePointStart - offlineStartTime) / 1000
  299. if offlineBubblePointStart > offlineStartTime then
  300. if diff > offlineTimeout * 60 then
  301. secondDiff = 0
  302. else
  303. secondDiff = offlineTimeout * 60 - diff
  304. end
  305. end
  306. end
  307. return secondDiff
  308. end
  309. return 0
  310. end
  311. --- 根据泡点时长获取泡点经验
  312. ---@param actor 玩家对象
  313. ---@param secondDiff 泡点时长(s)
  314. ---@return number 泡点经验
  315. function this.calcBubblePointExp(actor, secondDiff)
  316. if secondDiff == 0 then
  317. return 0
  318. end
  319. local level = tonumber(getbaseinfo(actor, "level"))
  320. local expInterval = ConfigDataManager.getTableValue("cfg_bubble_point", "expInterval", "id", level)
  321. if string.isNullOrEmpty(expInterval) then
  322. gameDebug.print("找不到cfg_bubble_point配置,id:" .. level)
  323. return 0, 0
  324. end
  325. local times = math.round(secondDiff / expInterval)
  326. local expConfig = ConfigDataManager.getTableValue("cfg_bubble_point", "exp", "id", level)
  327. local exp = string.split(expConfig, "#")[2]
  328. return tonumber(exp) * tonumber(times)
  329. end
  330. --- 检查离线挂机经验是否超限
  331. ---@param fightExp number 战斗经验
  332. ---@param bubblePointExp number 泡点经验
  333. function this.checkExpLimit(actor, fightExp, bubblePointExp)
  334. local expLimit =
  335. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT)
  336. if not string.isNullOrEmpty(expLimit) then
  337. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  338. totalExp = totalExp or 0
  339. fightExp = fightExp or 0
  340. bubblePointExp = bubblePointExp or 0
  341. return fightExp + bubblePointExp + totalExp > tonumber(expLimit)
  342. end
  343. return false
  344. end
  345. --- 保存离线挂机战斗经验
  346. ---@param actor 玩家对象
  347. ---@param exp 战斗经验
  348. function onHook.saveOfflineFightExp(actor, exp)
  349. -- 是否超时判断
  350. local start = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  351. local offlineTimeout =
  352. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_TIMEOUT)
  353. local now = getbaseinfo(actor, "now")
  354. if now - start > tonumber(offlineTimeout) * TimeUnit.MINUTE * TimeUnit.MILLISECOND then
  355. return
  356. end
  357. local offlineFightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  358. if string.isNullOrEmpty(offlineFightExp) then
  359. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, tonumber(exp))
  360. else
  361. if not this.checkExpLimit(actor, offlineFightExp, 0) then
  362. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, tonumber(offlineFightExp) + tonumber(exp))
  363. end
  364. end
  365. end
  366. --- 设置离线挂机巡逻状态
  367. ---@param actor table 玩家对象
  368. ---@param state number 状态 巡逻状态:1 战斗状态:2 无状态:0
  369. function onHook.setOfflineState(actor, state)
  370. setofflinepatrolstate(actor, state)
  371. setplaydef(actor, PlayerDefKey.offline.PATROL_STATE, state)
  372. end
  373. --- 记录玩家最后一次离线挂机时间
  374. ---@param actor table 玩家对象
  375. function onHook.recordLastTime(actor)
  376. setplaydef(actor, PlayerDefKey.offline.LAST_TIME, getbaseinfo(actor, "now"))
  377. end
  378. --- 记录服务器启动时间
  379. function onHook.recordStartTime()
  380. setsysvar(SystemVarConst.SERVER_START, getbaseinfo("now"))
  381. end
  382. --- 进入泡点地图设置离线挂机状态
  383. function onHook.enterMap(actor)
  384. if this.checkBubblePointArea(actor) then
  385. setofflinepatrolstate(actor, 0)
  386. else
  387. -- 不是泡点地图设置为战斗状态
  388. setofflinepatrolstate(actor, 2)
  389. end
  390. end