OfflineOnHook_1.lua 18 KB

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