LineManager.lua 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. --[[
  2. 作者: 无心
  3. 日期: 2025-08-24
  4. 功能介绍:
  5. 本文件为线路管理(LineManager)模块,主要负责处理与游戏多线路相关的逻辑,包括获取玩家当前线路、地图、线路策略配置等功能。
  6. 该模块为游戏服务器提供多线路分流、线路策略、进入条件等相关的辅助方法。
  7. --]] LineManager = {}
  8. LineManager.init = function()
  9. end
  10. --- 获取玩家当前线路ID
  11. --- @param actor table 玩家对象
  12. --- @return number 当前线路ID
  13. function LineManager.getCurrentLine(actor)
  14. return getbaseinfo(actor, "line")
  15. end
  16. --- 获取玩家当前地图ID
  17. --- @param actor table 玩家对象
  18. --- @return number 当前地图ID
  19. function LineManager.getCurrentMapId(actor)
  20. return getbaseinfo(actor, "mapid")
  21. end
  22. -- 公共方法:根据actor和字段名获取线路策略配置表中的字段值
  23. function LineManager.getLinePolicyField(actor, fieldName)
  24. local lineId = LineManager.getCurrentLine(actor)
  25. local mapId = LineManager.getCurrentMapId(actor)
  26. local linePolicyConfig = ConfigDataManager.getTableValue("cfg_map_line", "line_policy_config", "id", mapId)
  27. -- info(string.format("MS LineManager.getLinePolicyField: 获取线路策略配置信息=%s", linePolicyConfig))
  28. if not linePolicyConfig or linePolicyConfig == "" then
  29. return nil
  30. end
  31. local foundPolicyId = nil
  32. for _, mapping in ipairs(string.split(linePolicyConfig, "|")) do
  33. local parts = string.split(mapping, "#")
  34. if #parts == 2 then
  35. local configLineId = tonumber(parts[1])
  36. local policyId = tonumber(parts[2])
  37. if configLineId == lineId then
  38. foundPolicyId = policyId
  39. break
  40. end
  41. end
  42. end
  43. -- info(string.format("MS LineManager.getLinePolicyField: 获取线路策略配置id = %s", foundPolicyId))
  44. if not foundPolicyId then
  45. return nil
  46. end
  47. local value = ConfigDataManager.getTableValue("cfg_line_policy", fieldName, "id", foundPolicyId)
  48. -- info(string.format("MS LineManager.getLinePolicyField: 获取线路策略配置字段[%s]=%s", fieldName, value))
  49. return value
  50. end
  51. -- 通过公共方法获取经验倍率
  52. function LineManager.GetLineExpRate(actor)
  53. local expRate = LineManager.getLinePolicyField(actor, "expRate")
  54. return tonumber(expRate) or 100
  55. end
  56. -- 通过公共方法获取掉落倍率
  57. function LineManager.getDropRate(actor)
  58. local dropRate = LineManager.getLinePolicyField(actor, "dropRate")
  59. -- info(string.format("MS LineManager >>> 配置文件 -> 获取地图基础爆率加成完成 线路爆率=%s", tostring(dropRate)))
  60. return tonumber(dropRate) or 100
  61. end
  62. --- 获取线路经验倍率
  63. --- @param actor table 玩家对象
  64. --- @return number 经验倍率
  65. function LineManager.getExpRate(actor)
  66. local expRate = LineManager.getLinePolicyField(actor, "expRate")
  67. return tonumber(expRate) or 100
  68. end
  69. --- 检查线路是否允许PK
  70. --- @param actor table 玩家对象
  71. --- @return boolean
  72. function LineManager.isPvpEnabled(actor)
  73. local pvpEnabled = LineManager.getLinePolicyField(actor, "pvpEnabled")
  74. return tonumber(pvpEnabled) == 1
  75. end
  76. --- 检查线路是否允许交易
  77. --- @param actor table 玩家对象
  78. --- @return boolean
  79. function LineManager.isTradeEnabled(actor)
  80. local tradeEnabled = LineManager.getLinePolicyField(actor, "tradeEnabled")
  81. return tonumber(tradeEnabled) == 1
  82. end
  83. --- 检查线路是否开放古战场
  84. --- @param actor table 玩家对象
  85. --- @return boolean
  86. function LineManager.isAncientBattleEnabled(actor)
  87. local ancientBattleEnabled = LineManager.getLinePolicyField(actor, "ancientBattleEnabled")
  88. return tonumber(ancientBattleEnabled) == 1
  89. end
  90. --- 检查线路是否开放活动
  91. --- @param actor table 玩家对象
  92. --- @return boolean
  93. function LineManager.isActivityEnabled(actor)
  94. local activityEnabled = LineManager.getLinePolicyField(actor, "activityEnabled")
  95. return tonumber(activityEnabled) == 1
  96. end
  97. --- 检查线路是否允许怪物
  98. --- @param actor table 玩家对象
  99. --- @return boolean
  100. function LineManager.isMonsterEnabled(actor)
  101. local monsterEnabled = LineManager.getLinePolicyField(actor, "monsterEnabled")
  102. return tonumber(monsterEnabled) == 1
  103. end
  104. function LineManager.takeoffequip(actor)
  105. local mapId = getbaseinfo(actor, "map")
  106. local wear_conditions = ConfigDataManager.getTableValue("cfg_map_line", "wear_conditions", "id", mapId)
  107. if string.isNullOrEmpty(wear_conditions) then
  108. return
  109. end
  110. local checkResult, tip = LineManager.CheckCanEnterLineMap(actor, mapId, 1)
  111. if checkResult ~= nil and checkResult == false then
  112. APISetInt(actor, "IS_TAKEOFF_EQUIP_TRANSFET", 1)
  113. tipinfo(actor, tip)
  114. maptransfer(actor, 135, 128 , 1001, 1, 0)
  115. end
  116. end
  117. --- 检查玩家是否可以进入指定线路地图
  118. --- @param actor table 玩家对象
  119. --- @param targetLine number 目标线路ID
  120. --- @param targetMap number 目标地图ID
  121. --- @return boolean, string 是否可以进入,原因
  122. function LineManager.CheckCanEnterLineMap(actor, targetMapId, targetLine)
  123. -- 获取目标地图和线路的线路策略配置字段
  124. local function getLinePolicyFieldForTarget(mapId, lineId, fieldName)
  125. local linePolicyConfig = ConfigDataManager.getTableValue("cfg_map_line", "line_policy_config", "id", mapId)
  126. if not linePolicyConfig or linePolicyConfig == "" then
  127. return nil
  128. end
  129. local foundPolicyId = nil
  130. for _, mapping in ipairs(string.split(linePolicyConfig, "|")) do
  131. local parts = string.split(mapping, "#")
  132. if #parts == 2 then
  133. local configLineId = tonumber(parts[1])
  134. local policyId = tonumber(parts[2])
  135. if configLineId == lineId then
  136. foundPolicyId = policyId
  137. break
  138. end
  139. end
  140. end
  141. if not foundPolicyId then
  142. return nil
  143. end
  144. local value = ConfigDataManager.getTableValue("cfg_line_policy", fieldName, "id", foundPolicyId)
  145. return value
  146. end
  147. local wear_conditions = ConfigDataManager.getTableValue("cfg_map_line", "wear_conditions", "id", targetMapId)
  148. if not string.isNullOrEmpty(wear_conditions) then
  149. local equips = getputonequipinfo(actor)
  150. local conditions = string.split(wear_conditions, "|")
  151. local isPass = table.count(conditions) <= 0
  152. local strTip = ""
  153. local notNecessaryMax = 0
  154. local notNecessaryPass = 0
  155. for _, v in ipairs(conditions) do
  156. local condition = string.split(v, "#")
  157. local type = condition[1]
  158. local equip = condition[2]
  159. local isNecessary = tonumber(condition[3])
  160. local tip = condition[4]
  161. local isPassTmp = false
  162. if type == "1" then
  163. local allposs = string.split(equip, "&")
  164. local isWear = nil
  165. for _, vpos in ipairs(allposs) do
  166. local hasWear, v = table.findByCondi(equips, function(a)
  167. local pos = gameequip.pos(a.equipindex)
  168. if a.equipindex > 70000 and tonumber(vpos) >= 10000 then
  169. pos = pos + 9984
  170. end
  171. return pos == tonumber(vpos);
  172. end)
  173. if tonumber(vpos) == 10003 then
  174. local allmount = getallmount(actor)
  175. local isWear2 = table.findByCondi(allmount, function(a)
  176. return a.wear == 1 or a.auto == 1
  177. end)
  178. hasWear = isWear2
  179. end
  180. if hasWear == nil then
  181. isWear = nil
  182. break
  183. else
  184. isWear = hasWear
  185. end
  186. end
  187. isPassTmp = isWear ~= nil
  188. elseif type == "2" then
  189. local hasWear, v = table.findByCondi(equips, function(a)
  190. return a.cfgid == tonumber(equip);
  191. end)
  192. isPassTmp = hasWear ~= nil
  193. elseif type == "3" then
  194. local hasWear, v = table.findByCondi(equips, function(a)
  195. return a.cfgid == tonumber(equip);
  196. end)
  197. local count = getbagitemcountbyid(actor, tonumber(equip))
  198. isPassTmp = not (hasWear == nil and count == 0)
  199. elseif type == "4" then
  200. local totalLv = 0
  201. for kk, vv in pairs(equips) do
  202. local lv = EquipFunc.getEquipStrengthLevel(actor, vv.id)
  203. totalLv = totalLv + lv
  204. end
  205. isPassTmp = not (totalLv < tonumber(equip))
  206. end
  207. if isNecessary == 1 then
  208. if isPassTmp ~= true then
  209. strTip = tip
  210. isPass = false
  211. break
  212. else
  213. isPass = isPassTmp
  214. end
  215. end
  216. if isNecessary == 0 then
  217. notNecessaryMax = notNecessaryMax + 1
  218. if isPassTmp == true then
  219. isPass = true
  220. notNecessaryPass = notNecessaryPass + 1
  221. else
  222. strTip = tip
  223. end
  224. end
  225. end
  226. if isPass ~= true then
  227. -- tipinfo(actor, strTip)
  228. return false, strTip
  229. elseif notNecessaryMax > 0 and notNecessaryPass <= 0 then
  230. -- tipinfo(actor, strTip)
  231. return false, strTip
  232. end
  233. end
  234. local tradeCardRequired = tonumber(getLinePolicyFieldForTarget(targetMapId, targetLine, "TradeCard")) or 0
  235. local enjoyPrivilegeRequired = tonumber(getLinePolicyFieldForTarget(targetMapId, targetLine, "EnjoyPrivilege")) or 0
  236. local unionLevelRequired = tonumber(getLinePolicyFieldForTarget(targetMapId, targetLine, "UnionLevel")) or 0
  237. local vipLevelRequired = tonumber(getLinePolicyFieldForTarget(targetMapId, targetLine, "VipLevel")) or 0
  238. -- info("MS LineManager.CheckCanEnterLineMap-> ", targetMapId, targetLine, tradeCardRequired, enjoyPrivilegeRequired,
  239. -- unionLevelRequired, vipLevelRequired)
  240. -- 1. 检查是否需要交易卡
  241. if tradeCardRequired > 0 then
  242. if not LineManager.hasTradeCard(actor) then
  243. tipinfo(actor, "需要交易卡")
  244. return false, "需要交易卡"
  245. end
  246. end
  247. -- 2. 检查是否需要畅玩特权
  248. if enjoyPrivilegeRequired > 0 then
  249. if not PrivilegeMonth.hasEnjoyPrivilege(actor) then
  250. tipinfo(actor, "需要畅玩特权")
  251. return false, "需要畅玩特权"
  252. end
  253. end
  254. -- 3. 检查战盟等级
  255. if unionLevelRequired > 0 then
  256. if not LineManager.checkUnionLevel(actor, unionLevelRequired) then
  257. tipinfo(actor, string.format("战盟等级不足,需要%d级", unionLevelRequired))
  258. return false, string.format("战盟等级不足,需要%d级", unionLevelRequired)
  259. end
  260. end
  261. -- 4. 检查VIP等级
  262. if vipLevelRequired > 0 then
  263. if not LineManager.checkVipLevel(actor, vipLevelRequired) then
  264. tipinfo(actor, string.format("VIP等级不足,需要%d级", vipLevelRequired))
  265. return false, string.format("VIP等级不足,需要%d级", vipLevelRequired)
  266. end
  267. end
  268. -- 所有条件都满足
  269. return true, "可以进入"
  270. end
  271. --- 检查玩家进入指定线路地图所需道具
  272. --- @param actor table 玩家对象
  273. --- @param itemsNeedIndex number 所需道具idx
  274. --- @param targetMapId number 目标地图ID
  275. --- @return boolean, string 是否可以进入,原因
  276. function LineManager.CheckEnterLineMapItemsNeed(actor, targetMapId, itemsNeedIndex)
  277. local mapId = tonumber(getbaseinfo(actor, "mapid"))
  278. if mapId == targetMapId then
  279. return true, ""
  280. end
  281. local joinMapNeedSelect = {} -- 选择需要道具
  282. local joinMapNeed = {} -- 必须需要道具
  283. local strTip = ""
  284. local items_need = ConfigDataManager.getTableValue("cfg_map_line", "items_need", "id", targetMapId)
  285. if not string.isNullOrEmpty(items_need) then
  286. local conditionArr = string.split(items_need, "|")
  287. for _, conditionData in ipairs(conditionArr) do
  288. local conditionStr = string.split(conditionData, "#")
  289. strTip = conditionStr[3]
  290. if tonumber(conditionStr[2]) == 0 then
  291. local itemArr = string.split(conditionStr[1], "&")
  292. if table.count(itemArr) > 1 then
  293. table.insert(joinMapNeedSelect, {itemId = tonumber(itemArr[1]), itemNum = tonumber(itemArr[2])})
  294. end
  295. else
  296. local itemArr = string.split(conditionStr[1], "&")
  297. if table.count(itemArr) > 1 then
  298. table.insert(joinMapNeed, {itemId = tonumber(itemArr[1]), itemNum = tonumber(itemArr[2])})
  299. end
  300. end
  301. end
  302. end
  303. local itemNeed = joinMapNeedSelect[itemsNeedIndex]
  304. if table.count(joinMapNeedSelect) > 0 then
  305. if itemNeed == nil then
  306. tipinfo(actor, "材料扣除失败")
  307. return false, strTip
  308. end
  309. else
  310. return true, ""
  311. end
  312. -- 整合所需道具
  313. local itemsNeed = {}
  314. if itemNeed ~= nil then
  315. table.insert(itemsNeed, {itemId = itemNeed.itemId, itemNum = itemNeed.itemNum})
  316. end
  317. for _,item in ipairs(joinMapNeed) do
  318. table.insert(itemsNeed, {itemId = item.itemId, itemNum = item.itemNum})
  319. end
  320. -- 传送至地图所需道具扣除
  321. for _, item in ipairs(itemsNeed) do
  322. local result = removeitemfrombag(actor, item.itemId, item.itemNum, 0, 9999, "传送古战场消耗")
  323. if not result then
  324. tipinfo(actor, "扣除道具失败")
  325. return false, strTip
  326. end
  327. end
  328. return true, ""
  329. end
  330. --- 检查是否有交易卡(优化版本)
  331. --- @param actor table 玩家对象
  332. --- @return boolean
  333. function LineManager.hasTradeCard(actor)
  334. -- 这里需要根据实际的交易卡道具ID进行判断
  335. local tradeCardId = 70700001 -- 假设的交易卡道具ID,需要根据实际配置调整
  336. return Bag.hasItem(actor, tradeCardId, 1)
  337. end
  338. --- 检查VIP等级(优化版本)
  339. --- @param actor table 玩家对象
  340. --- @param requiredLevel number 要求的VIP等级
  341. --- @return boolean
  342. function LineManager.checkVipLevel(actor, requiredLevel)
  343. -- 这里需要根据实际的VIP系统进行判断
  344. -- local vipLevel = getplaydef(actor, "VIP_LEVEL") or 0
  345. -- return tonumber(vipLevel) >= requiredLevel
  346. return true
  347. end
  348. --- 检查战盟等级(优化版本)
  349. --- @param actor table 玩家对象
  350. --- @param requiredLevel number 要求的战盟等级
  351. --- @return boolean
  352. function LineManager.checkUnionLevel(actor, requiredLevel)
  353. -- 这里需要根据实际的战盟系统进行判断
  354. -- local unionLevel = getplaydef(actor, "UNION_LEVEL") or 0
  355. -- return tonumber(unionLevel) >= requiredLevel
  356. return true
  357. end
  358. --- 应用掉落倍率
  359. --- @param actor table 玩家对象
  360. --- @param baseDropRate number 基础掉落率
  361. --- @return number 实际掉落率
  362. function LineManager.applyDropRate(actor, baseDropRate)
  363. -- 获取线路爆率
  364. local lineDropRate = LineManager.getDropRate(actor)
  365. -- info(string.format("MS LineManager >>> 开始获取地图基础爆率加成 基础掉落率=%s", tostring(baseDropRate)))
  366. -- info(string.format("MS LineManager >>> 获取地图基础爆率加成完成 线路爆率=%s", tostring(lineDropRate)))
  367. -- 计算实际掉落率
  368. local actualDropRate = baseDropRate * (lineDropRate / 100)
  369. -- info(string.format("MS LineManager >>> 玩家掉落倍率调整:倍率=%s, 基础掉落率=%s, 实际掉落率=%s", tostring(lineDropRate), tostring(baseDropRate), tostring(actualDropRate)))
  370. return actualDropRate
  371. end
  372. --- 应用经验倍率
  373. --- @param actor table 玩家对象
  374. --- @param baseExp number 基础经验
  375. --- @return number 实际经验
  376. function LineManager.applyExpRate(actor, baseExp)
  377. local lineExpRate = LineManager.GetLineExpRate(actor)
  378. if not lineExpRate then
  379. -- info(string.format("MS LineManager.applyExpRate 检测没有线路策略: actor=%s, currentLine=%d, lineExpRate=nil", actor:toString(), currentLine))
  380. return baseExp
  381. end
  382. -- 计算实际经验
  383. local actualExp = baseExp * (tonumber(lineExpRate) / 100)
  384. return actualExp
  385. end