OfflineOnHook.lua 17 KB

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