OfflineOnHook.lua 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401
  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. -- additemtobag(actor, ItemConfigId.EXP, exp, 0, 9999, '离线挂机')
  96. -- 增加加成逻辑
  97. _, exp = Bag.addItemToBag(actor, ItemConfigId.EXP, exp, 0, 9999, "离线挂机")
  98. setplaydef(actor, PlayerDefKey.offline.IS_RECEIVE, true)
  99. end
  100. --- 角色登录存储泡点结束时间与离线挂机结束时间
  101. ---@param actor 玩家对象
  102. function onHook.login(actor)
  103. onHook.setOfflineState(actor, 0)
  104. local offlineTimeout = onHook.getOfflineOnHookMaxTime(actor)
  105. local offlineStartTime = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  106. local offlineBubblePointStart = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START)
  107. local serverStart = getsysvar(SystemVarConst.SERVER_START)
  108. -- 角色离线挂机开始时间小于服务器最新启动时间则表示玩家离线挂机期间服务器重启过
  109. local now = getbaseinfo(actor, "now")
  110. local fightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  111. local isFirstLogin = getplaydef(actor, "T$_isResetReceiveFlag")
  112. if offlineStartTime and serverStart and tonumber(serverStart) > tonumber(offlineStartTime) and not isFirstLogin then
  113. -- 设置已经重置过领取经验标识
  114. setplaydef(actor, "T$_isResetReceiveFlag", 1)
  115. -- 设置领取经验为false,防止服务器重启领取经验标识为true,导致玩家无法领取离线挂机经验
  116. setplaydef(actor, PlayerDefKey.offline.IS_RECEIVE, false)
  117. -- 判断当前角色是否在安全区内,如果不在安全区,根据配表计算战斗经验
  118. if string.isNullOrEmpty(offlineBubblePointStart) then
  119. local lastTime = getplaydef(actor, PlayerDefKey.offline.LAST_TIME)
  120. local fightTime = tonumber(now) - tonumber(lastTime)
  121. local timeoutMillisecond = tonumber(offlineTimeout * TimeUnit.MINUTE * TimeUnit.MILLISECOND)
  122. fightTime = fightTime > timeoutMillisecond and timeoutMillisecond or fightTime
  123. local mapId = getbaseinfo(actor, "map")
  124. local patrolState = getplaydef(actor, PlayerDefKey.offline.PATROL_STATE)
  125. local monsterState = patrolState and 2 or 1
  126. local expConfig =
  127. ConfigDataManager.getTableValue(
  128. "cfg_hangupReboot",
  129. "monstergroup",
  130. "mapid",
  131. mapId,
  132. "monstername",
  133. monsterState
  134. )
  135. if not string.isNullOrEmpty(expConfig) then
  136. local second = string.split(expConfig, "#")[1]
  137. local exp = string.split(expConfig, "#")[2]
  138. local step = math.round((fightTime / 1000) / second)
  139. if step > 0 then
  140. local baseExp = fightExp and tonumber(fightExp) or 0
  141. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, baseExp + (tonumber(exp) * step))
  142. end
  143. end
  144. end
  145. end
  146. if offlineBubblePointStart then
  147. local offlineBubblePointEnd = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END)
  148. if string.isNullOrEmpty(offlineBubblePointEnd) and this.checkBubblePointArea(actor) then
  149. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END, now)
  150. -- 将离线泡点经验持久化,如果上次离线的泡点经验没有领取则累加
  151. local onHookInfo = getonhookinfo(actor)
  152. if table.isNullOrEmpty(onHookInfo) then
  153. return
  154. end
  155. local bubblePointExp = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP)
  156. local exp = this.calcBubblePointExp(actor, this.calcBubblePointTime(actor, offlineTimeout))
  157. if string.isNullOrEmpty(bubblePointExp) then
  158. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, exp)
  159. else
  160. local bubbleExp = tonumber(bubblePointExp) + exp
  161. if this.checkExpLimit(actor, fightExp, bubbleExp) then
  162. local expLimit =
  163. ConfigDataManager.getTableValue(
  164. "cfg_global",
  165. "value",
  166. "id",
  167. GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT
  168. )
  169. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  170. bubbleExp = tonumber(expLimit) - fightExp - totalExp
  171. end
  172. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, bubbleExp)
  173. end
  174. end
  175. end
  176. if offlineStartTime then
  177. local offlineEndTime = getplaydef(actor, PlayerDefKey.offline.END_TIME)
  178. if string.isNullOrEmpty(offlineEndTime) then
  179. setplaydef(actor, PlayerDefKey.offline.END_TIME, now)
  180. end
  181. end
  182. end
  183. --- 角色退出清空泡点相关信息
  184. ---@param actor 玩家对象
  185. function onHook.logout(actor)
  186. -- 角色重置过领取经验标识重置
  187. setplaydef(actor, "T$_isResetReceiveFlag", nil)
  188. -- 重置结束时间
  189. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END, nil)
  190. setplaydef(actor, PlayerDefKey.offline.END_TIME, nil)
  191. -- 领取过经验后重置存储的经验信息
  192. if getplaydef(actor, PlayerDefKey.offline.IS_RECEIVE) then
  193. setplaydef(actor, PlayerDefKey.offline.IS_RECEIVE, false)
  194. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, nil)
  195. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, nil)
  196. setplaydef(actor, PlayerDefKey.offline.TOTAL_EXP, nil)
  197. else
  198. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  199. if string.isNullOrEmpty(totalExp) then
  200. totalExp = 0
  201. end
  202. local bubblePointExp = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP)
  203. if not string.isNullOrEmpty(bubblePointExp) then
  204. totalExp = totalExp + bubblePointExp
  205. end
  206. local fightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  207. if not string.isNullOrEmpty(fightExp) then
  208. totalExp = totalExp + fightExp
  209. end
  210. local expLimit =
  211. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT)
  212. if expLimit and totalExp > tonumber(expLimit) then
  213. totalExp = tonumber(expLimit)
  214. end
  215. setplaydef(actor, PlayerDefKey.offline.TOTAL_EXP, totalExp)
  216. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_EXP, nil)
  217. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, nil)
  218. end
  219. local level = tonumber(getbaseinfo(actor, "level"))
  220. -- 是否达到开启离线挂机的等级
  221. local offlineOnHookLevel =
  222. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_LEVEL)
  223. if level >= tonumber(offlineOnHookLevel) then
  224. -- 记录离线挂机开启时间
  225. setplaydef(actor, PlayerDefKey.offline.START_TIME, getbaseinfo(actor, "now"))
  226. if this.checkBubblePointArea(actor) then
  227. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP, getbaseinfo(actor, "map"))
  228. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START, getbaseinfo(actor, "now"))
  229. else
  230. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP, nil)
  231. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START, nil)
  232. end
  233. end
  234. end
  235. --- 玩家传送后判断是否进入泡点区域
  236. ---@param actor 玩家对象
  237. function onHook.afterTransmit(actor)
  238. -- 非离线挂机玩家不处理
  239. if not isofflineplay(actor) then
  240. return
  241. end
  242. -- 是否达到开启离线挂机的等级,离线挂机传送只可能是免费复活后传送,所以此处不需要判断区域是否为泡点区域
  243. local offlineOnHookLevel =
  244. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_LEVEL)
  245. if tonumber(getbaseinfo(actor, "level")) >= tonumber(offlineOnHookLevel) then
  246. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_MAP, getbaseinfo(actor, "map"))
  247. setplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START, getbaseinfo(actor, "now"))
  248. end
  249. end
  250. --- 检查区域是否是泡点区域
  251. ---@param actor 玩家对象
  252. ---@return boolean 是否在泡点区域
  253. function this.checkBubblePointArea(actor)
  254. local level = tonumber(getbaseinfo(actor, "level"))
  255. local mapId = getbaseinfo(actor, "map")
  256. local safeArea = getbaseinfo(actor, "safearea")
  257. local configString = ConfigDataManager.getTableValue("cfg_bubble_point", "expMap", "id", level)
  258. local configMap = string.toStringStringMap(configString, "#", "|")
  259. local checkArea = false
  260. for key, value in pairs(configMap) do
  261. mapId = tonumber(mapId)
  262. key = tonumber(key)
  263. value = tonumber(value)
  264. if mapId == key then
  265. if value == 3 then
  266. checkArea = true
  267. elseif value == 2 and safeArea == false then
  268. checkArea = true
  269. elseif value == 1 and safeArea == true then
  270. checkArea = true
  271. end
  272. end
  273. end
  274. return checkArea
  275. end
  276. --- 计算泡点时长
  277. ---@param actor 玩家对象
  278. ---@param duration 离线挂机战斗时长
  279. ---@return number 泡点时长(s)
  280. function this.calcBubblePointTime(actor, offlineTimeout)
  281. local offlineBubblePointStart = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_START)
  282. local offlineBubblePointEnd = getplaydef(actor, PlayerDefKey.offline.BUBBLE_POINT_END)
  283. if
  284. not string.isNullOrEmpty(offlineBubblePointStart) and not string.isNullOrEmpty(offlineBubblePointEnd) and
  285. tonumber(offlineBubblePointEnd) - tonumber(offlineBubblePointStart) > 0
  286. then
  287. local secondDiff = math.round((tonumber(offlineBubblePointEnd) - tonumber(offlineBubblePointStart)) / 1000)
  288. -- 泡点时长处理
  289. if secondDiff > tonumber(offlineTimeout) * 60 then
  290. secondDiff = tonumber(offlineTimeout) * 60
  291. local offlineStartTime = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  292. local diff = (offlineBubblePointStart - offlineStartTime) / 1000
  293. if offlineBubblePointStart > offlineStartTime then
  294. if diff > offlineTimeout * 60 then
  295. secondDiff = 0
  296. else
  297. secondDiff = offlineTimeout * 60 - diff
  298. end
  299. end
  300. end
  301. return secondDiff
  302. end
  303. return 0
  304. end
  305. --- 根据泡点时长获取泡点经验
  306. ---@param actor 玩家对象
  307. ---@param secondDiff 泡点时长(s)
  308. ---@return number 泡点经验
  309. function this.calcBubblePointExp(actor, secondDiff)
  310. if secondDiff == 0 then
  311. return 0
  312. end
  313. local level = tonumber(getbaseinfo(actor, "level"))
  314. local expInterval = ConfigDataManager.getTableValue("cfg_bubble_point", "expInterval", "id", level)
  315. if string.isNullOrEmpty(expInterval) then
  316. gameDebug.print("找不到cfg_bubble_point配置,id:" .. level)
  317. return 0, 0
  318. end
  319. local times = math.round(secondDiff / expInterval)
  320. local expConfig = ConfigDataManager.getTableValue("cfg_bubble_point", "exp", "id", level)
  321. local exp = string.split(expConfig, "#")[2]
  322. return tonumber(exp) * tonumber(times)
  323. end
  324. --- 检查离线挂机经验是否超限
  325. ---@param fightExp number 战斗经验
  326. ---@param bubblePointExp number 泡点经验
  327. function this.checkExpLimit(actor, fightExp, bubblePointExp)
  328. local expLimit =
  329. ConfigDataManager.getTableValue("cfg_global", "value", "id", GlobalConfigId.OFFLINE_ON_HOOK_EXP_LIMIT)
  330. if not string.isNullOrEmpty(expLimit) then
  331. local totalExp = getplaydef(actor, PlayerDefKey.offline.TOTAL_EXP)
  332. totalExp = totalExp or 0
  333. fightExp = fightExp or 0
  334. bubblePointExp = bubblePointExp or 0
  335. return fightExp + bubblePointExp + totalExp > tonumber(expLimit)
  336. end
  337. return false
  338. end
  339. --- 保存离线挂机战斗经验
  340. ---@param actor 玩家对象
  341. ---@param exp 战斗经验
  342. function onHook.saveOfflineFightExp(actor, exp)
  343. -- 是否超时判断
  344. local start = getplaydef(actor, PlayerDefKey.offline.START_TIME)
  345. local offlineTimeout = onHook.getOfflineOnHookMaxTime(actor)
  346. local now = getbaseinfo(actor, "now")
  347. if now - start > tonumber(offlineTimeout) * TimeUnit.MINUTE * TimeUnit.MILLISECOND then
  348. return
  349. end
  350. local offlineFightExp = getplaydef(actor, PlayerDefKey.offline.FIGHT_EXP)
  351. if string.isNullOrEmpty(offlineFightExp) then
  352. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, tonumber(exp))
  353. else
  354. if not this.checkExpLimit(actor, offlineFightExp, 0) then
  355. setplaydef(actor, PlayerDefKey.offline.FIGHT_EXP, tonumber(offlineFightExp) + tonumber(exp))
  356. end
  357. end
  358. end
  359. --- 设置离线挂机巡逻状态
  360. ---@param actor table 玩家对象
  361. ---@param state number 状态 巡逻状态:1 战斗状态:2 无状态:0
  362. function onHook.setOfflineState(actor, state)
  363. setofflinepatrolstate(actor, state)
  364. setplaydef(actor, PlayerDefKey.offline.PATROL_STATE, state)
  365. end
  366. --- 记录玩家最后一次离线挂机时间
  367. ---@param actor table 玩家对象
  368. function onHook.recordLastTime(actor)
  369. setplaydef(actor, PlayerDefKey.offline.LAST_TIME, getbaseinfo(actor, "now"))
  370. end
  371. --- 记录服务器启动时间
  372. function onHook.recordStartTime()
  373. setsysvar(SystemVarConst.SERVER_START, getbaseinfo("now"))
  374. end
  375. --- 进入泡点地图设置离线挂机状态
  376. function onHook.enterMap(actor)
  377. if this.checkBubblePointArea(actor) then
  378. setofflinepatrolstate(actor, 0)
  379. else
  380. -- 不是泡点地图设置为战斗状态
  381. setofflinepatrolstate(actor, 2)
  382. end
  383. end