-------------------------------- -- 文件名 : ServerCommerceActPeakBettle.lua -- 文件说明 : 巅峰战场 -- 创建时间 : 2025/11/13 -- 创建人 : WWF -------------------------------- local Util = require("common.Util") local Lang = require("common.Lang") local Msg = require("core.Msg") local ObjHuman = require("core.ObjHuman") local Broadcast = require("broadcast.Broadcast") local BagLogic = require("bag.BagLogic") local Grid = require("bag.Grid") local ItemDefine = require("bag.ItemDefine") local CombatLogic = require("combat.CombatLogic") local CombatDefine = require("combat.CombatDefine") local CombatPosLogic = require("combat.CombatPosLogic") local RoleLogic = require("role.RoleLogic") local RoleDBLogic = require("role.RoleDBLogic") local MailManager = require("mail.MailManager") local MailExcel = require("excel.mail") local ServerCommerceManager = require("serverCommerce.ServerCommerceManager") local ServerCommerceActDefine = require("serverCommerce.ServerCommerceActDefine") -- Excel配置表 local PeakBattlefieldRewardExcel = require("excel.peakbattlefieldReward") -- 巅峰战场常量定义 local INIT_RANK = 999 local MATCH_RANK_MIN_OFFSET = 50 -- 向下偏移 local MATCH_RANK_MAX_OFFSET = 10 -- 向上偏移 local MATCH_RANK_MIN = 1 -- 最小排名 local MATCH_RANK_MAX = 999 -- 最大排名 local OPPONENT_LIST_SIZE = 5 local DAILY_FREE_CHALLENGE_CNT = 5 local CHALLENGE_COST_ITEM_ID = 115 local CHALLENGE_COST_ITEM_CNT = 3 local REWARD_ID_WIN = 1001 -- 战胜奖励 local REWARD_ID_LOSE = 1002 -- 战败奖励 local INIT_HERO_CNT_PER_CAMP = 5 local MAIL_ID_DEFEAT = 1 -- 战败通知邮件ID local MAIL_ID_RANK_REWARD = 0 -- 排名奖励邮件ID -- 战斗类型 local COMBAT_TYPE_PEAK_BATTLEFIELD = CombatDefine.COMBAT_TYPE35 or 35 -- 数据库相关 local LuaMongo = _G.lua_mongo local DB = require("common.DB") -- 排名映射:uuid -> rank local UUID_2_RANK = {} -- 排名数据:rank -> data local RANK_2_DATA = {} -- 查询条件 local QueryByUuid = {_id = nil} ----------------------------------------- 内部处理开始 ------------------------------------- -- 获取玩家排名 local function getRank(uuid) local rank = UUID_2_RANK[uuid] return rank or 0 end -- 获取玩家数据 local function getData(uuid) local rank = UUID_2_RANK[uuid] if not rank then return end return RANK_2_DATA[rank] end -- 获取对手列表 local function getOpponentList(uuid) local data = getData(uuid) if not data then return {} end return data.opponentList or {} end -- 获取已解锁英雄列表 local function getUnlockedHeroes(uuid) local data = getData(uuid) if not data then return {} end return data.unlockedHeroes or {} end -- 获取每日免费挑战次数 local function getFreeChallengeCnt(uuid) local data = getData(uuid) if not data then return DAILY_FREE_CHALLENGE_CNT end -- 检查是否需要重置(跨天) local lastResetTime = data.lastResetTime or 0 if not Util.isSameDay(lastResetTime, os.time()) then return DAILY_FREE_CHALLENGE_CNT end return data.freeChallengeCnt or DAILY_FREE_CHALLENGE_CNT end -- 消耗免费挑战次数 local function consumeFreeChallenge(uuid) local data = getData(uuid) if not data then return false end local lastResetTime = data.lastResetTime or 0 local freeCnt = getFreeChallengeCnt(uuid) if freeCnt <= 0 then return false end -- 跨天重置 if not Util.isSameDay(lastResetTime, os.time()) then data.freeChallengeCnt = DAILY_FREE_CHALLENGE_CNT data.lastResetTime = os.time() end data.freeChallengeCnt = (data.freeChallengeCnt or DAILY_FREE_CHALLENGE_CNT) - 1 updateData(data) return true end -- 排名比较函数(排名越小越靠前) local function rankCmp(a, b) if a.rank ~= b.rank then return a.rank < b.rank end return (a.time or 0) < (b.time or 0) end -- 排序排名 local function sortRank() if #RANK_2_DATA > 1 then table.sort(RANK_2_DATA, rankCmp) end for rank = 1, #RANK_2_DATA do local uuid = RANK_2_DATA[rank]._id UUID_2_RANK[uuid] = rank RANK_2_DATA[rank].rank = rank end end -- 添加玩家 local function addPlayer(human) local rank = UUID_2_RANK[human.db._id] if rank then return end local data = {} data._id = human.db._id data.rank = INIT_RANK data.opponentList = {} data.unlockedHeroes = {} data.freeChallengeCnt = DAILY_FREE_CHALLENGE_CNT data.lastResetTime = os.time() data.time = os.time() data.name = human.db.name or "" data.lv = human.db.lv or 1 data.head = human.db.head or 0 data.zhandouli = human.db.zhandouli or 0 LuaMongo.insert(DB.db_peak_battlefield, data) RANK_2_DATA[#RANK_2_DATA + 1] = data sortRank() end -- 交换排名(挑战胜利时) local function swapRank(atkUuid, defUuid) local atkRank = UUID_2_RANK[atkUuid] local defRank = UUID_2_RANK[defUuid] if not atkRank or not defRank then return end local atkData = RANK_2_DATA[atkRank] local defData = RANK_2_DATA[defRank] if not atkData or not defData then return end -- 交换排名 local tempRank = atkData.rank atkData.rank = defData.rank defData.rank = tempRank atkData.time = os.time() defData.time = os.time() -- 清空对手列表(需要重新匹配) atkData.opponentList = {} defData.opponentList = {} updateData(atkData) updateData(defData) sortRank() end -- 设置对手列表 local function setOpponentList(uuid, opponentList) local data = getData(uuid) if not data then return end data.opponentList = opponentList or {} updateData(data) end -- 更新已解锁英雄 local function updateUnlockedHeroes(uuid, heroList) local data = getData(uuid) if not data then return end data.unlockedHeroes = heroList or {} updateData(data) end -- 更新数据库 local function updateData(data) QueryByUuid._id = data._id LuaMongo.update(DB.db_peak_battlefield, QueryByUuid, data) end -- 获取排名范围内的玩家(用于匹配) local function getPlayersInRankRange(minRank, maxRank, excludeUuid) local result = {} excludeUuid = excludeUuid or "" for rank = 1, #RANK_2_DATA do local data = RANK_2_DATA[rank] if data.rank >= minRank and data.rank <= maxRank and data._id ~= excludeUuid then result[#result + 1] = data end end return result end -- 获取活动剩余时间(秒) local function getActivityLeftTime() local nStartTime, nEndTime = ServerCommerceManager.CommerceAct_GetOpenAndEndTime() if not nEndTime or nEndTime == 0 then return 0 end local now = os.time() local leftTime = math.max(nEndTime - now, 0) return leftTime end -- 生成对手列表 local function generateOpponents(uuid, rank) if rank <= 0 then rank = INIT_RANK end local minRank = math.max(rank - MATCH_RANK_MIN_OFFSET, MATCH_RANK_MIN) local maxRank = math.min(rank + MATCH_RANK_MAX_OFFSET, MATCH_RANK_MAX) local candidates = getPlayersInRankRange(minRank, maxRank, uuid) -- 随机选择5个对手 local opponentList = {} local selected = {} local needCnt = OPPONENT_LIST_SIZE if #candidates > 0 then for i = 1, math.min(needCnt, #candidates) do local idx = math.random(1, #candidates) while selected[idx] do idx = math.random(1, #candidates) end selected[idx] = true opponentList[#opponentList + 1] = { uuid = candidates[idx]._id, rank = candidates[idx].rank } end end return opponentList end -- 检查并解锁英雄(使用系统英雄表,按阵营解锁) local function checkAndUnlockHeroes(human, rank) local HeroExcel = require("excel.hero").hero local currentUnlocked = getUnlockedHeroes(human.db._id) local newUnlocked = {} -- 根据排名解锁英雄:初始排名时解锁每个阵营的初始英雄 if rank <= INIT_RANK then -- 使用 CombatDefine 中的阵营定义 local camps = { CombatDefine.CAMP_TYPE1, -- 恶魔(人族) CombatDefine.CAMP_TYPE2, -- 圣堂(妖族) CombatDefine.CAMP_TYPE3 -- 不死(兽族) } for _, camp in ipairs(camps) do -- 从HeroExcel中获取该阵营的所有英雄 local campHeroes = {} for heroId, heroConfig in pairs(HeroExcel) do if heroConfig.camp == camp then campHeroes[#campHeroes + 1] = heroId end end -- 随机选择初始数量的英雄 if #campHeroes > 0 then local count = INIT_HERO_CNT_PER_CAMP local maxSelect = math.min(count, #campHeroes) for i = 1, maxSelect do local idx = math.random(1, #campHeroes) local heroId = campHeroes[idx] -- 检查是否已解锁 local found = false for k = 1, #currentUnlocked do if currentUnlocked[k] == heroId then found = true break end end if not found then newUnlocked[#newUnlocked + 1] = heroId currentUnlocked[#currentUnlocked + 1] = heroId end -- 移除已选择的英雄,避免重复 table.remove(campHeroes, idx) if #campHeroes == 0 then break end end end end end if #newUnlocked > 0 then updateUnlockedHeroes(human.db._id, currentUnlocked) -- 发送提示 Broadcast.sendErr(human, "解锁新英雄!") end end -- 发送战败邮件 local function sendDefeatMail(defeatedUuid, attackerUuid, newRank) local attackerDb = RoleDBLogic.getDb(attackerUuid) if not attackerDb then return end -- 获取邮件配置 local mailID = MAIL_ID_DEFEAT if not mailID or mailID == 0 then return end local mailConfig = MailExcel.mail[mailID] if not mailConfig then print("[sendDefeatMail] 邮件配置不存在,mailID=" .. (mailID or 0)) return end -- 格式化邮件内容 local content = Util.format(mailConfig.content or "", attackerDb.name or "", newRank) -- 发送邮件 MailManager.add(MailManager.SYSTEM, defeatedUuid, mailConfig.title or "巅峰战区赛", content, nil, mailConfig.senderName or "系统") end ----------------------------------------- 外部调用 ------------------------------------- -- 起服初始化 function Init() -- 从数据库加载所有玩家数据 local cursor = LuaMongo.find(DB.db_peak_battlefield, {}) if cursor then RANK_2_DATA = {} UUID_2_RANK = {} local data = LuaMongo.next(cursor) while data do RANK_2_DATA[#RANK_2_DATA + 1] = data data = LuaMongo.next(cursor) end sortRank() print("[ServerCommerceActPeakBettle - Init] 巅峰战场数据加载完成") end return true end -- 活动结束 function End() -- 活动结束后可以重置数据(如果需要) end -- 重置玩家数据 function ResetData(human) if not human then return end end ----------------------------------------- 客户端请求 ------------------------------------- -- 查询巅峰战场数据 function CommerceActPeakBettle_Query(human) -- 确保玩家已加入 if not getData(human.db._id) then addPlayer(human) end local data = getData(human.db._id) if not data then return end local rank = getRank(human.db._id) local freeCnt = getFreeChallengeCnt(human.db._id) local opponentList = getOpponentList(human.db._id) local unlockedHeroes = getUnlockedHeroes(human.db._id) -- 如果没有对手列表,生成对手 if not opponentList or #opponentList == 0 then opponentList = generateOpponents(human.db._id, rank) setOpponentList(human.db._id, opponentList) end local msgRet = Msg.gc.GC_PEAK_BATTLEFIELD_QUERY msgRet.rank = rank msgRet.freeChallengeCnt = freeCnt -- 填充对手列表 local len = 0 for i = 1, #opponentList do if len >= OPPONENT_LIST_SIZE then break end local opponent = opponentList[i] local opponentData = getData(opponent.uuid) if opponentData then len = len + 1 local net = msgRet.opponentList[len] net.uuid = opponent.uuid net.rank = opponent.rank or opponentData.rank -- 填充角色信息 local db = RoleDBLogic.getDb(opponent.uuid) if db then RoleLogic.makeRoleBase(db, net.roleBase) else -- 如果是NPC或离线玩家,使用缓存数据 net.roleBase.name = opponentData.name or "" net.roleBase.lv = opponentData.lv or 1 net.roleBase.head = opponentData.head or 0 net.roleBase.zhandouli = opponentData.zhandouli or 0 end end end msgRet.opponentList[0] = len -- 填充已解锁英雄 len = 0 for i = 1, #unlockedHeroes do if len >= 50 then break end len = len + 1 msgRet.unlockedHeroes[len] = unlockedHeroes[i] end msgRet.unlockedHeroes[0] = len -- 活动剩余时间 msgRet.leftTime = getActivityLeftTime() Msg.send(msgRet, human.fd) end -- 刷新对手列表 function CommerceActPeakBettle_Refresh(human) local rank = getRank(human.db._id) local opponentList = generateOpponents(human.db._id, rank) setOpponentList(human.db._id, opponentList) local msgRet = Msg.gc.GC_PEAK_BATTLEFIELD_REFRESH local len = 0 for i = 1, #opponentList do if len >= OPPONENT_LIST_SIZE then break end local opponent = opponentList[i] local opponentData = getData(opponent.uuid) if opponentData then len = len + 1 local net = msgRet.opponentList[len] net.uuid = opponent.uuid net.rank = opponent.rank or opponentData.rank local db = RoleDBLogic.getDb(opponent.uuid) if db then RoleLogic.makeRoleBase(db, net.roleBase) else net.roleBase.name = opponentData.name or "" net.roleBase.lv = opponentData.lv or 1 net.roleBase.head = opponentData.head or 0 net.roleBase.zhandouli = opponentData.zhandouli or 0 end end end msgRet.opponentList[0] = len Msg.send(msgRet, human.fd) end -- 挑战对手 function CommerceActPeakBettle_Challenge(human, opponentUuid) -- 检查对手是否在对手列表中 local opponentList = getOpponentList(human.db._id) local found = false for i = 1, #opponentList do if opponentList[i].uuid == opponentUuid then found = true break end end if not found then return Broadcast.sendErr(human, "对手不在列表中") end -- 检查挑战次数 local freeCnt = getFreeChallengeCnt(human.db._id) if freeCnt > 0 then -- 使用免费次数 if not consumeFreeChallenge(human.db._id) then return Broadcast.sendErr(human, "免费挑战次数不足") end else -- 消耗道具 if not BagLogic.checkItem(human, CHALLENGE_COST_ITEM_ID, CHALLENGE_COST_ITEM_CNT) then return Broadcast.sendErr(human, "挑战券不足") end BagLogic.delItem(human, CHALLENGE_COST_ITEM_ID, CHALLENGE_COST_ITEM_CNT, "peak_battlefield_challenge") end -- 启动战斗 local cbParam = { opponentUuid = opponentUuid, combatType = COMBAT_TYPE_PEAK_BATTLEFIELD } CombatLogic.combatBegin(human, nil, {opponentUuid = opponentUuid}, COMBAT_TYPE_PEAK_BATTLEFIELD, cbParam, false) end -- 战斗结束回调 function CommerceActPeakBettle_OnFightEnd(human, result, combatType, cbParam, combatInfo, param) if combatType ~= COMBAT_TYPE_PEAK_BATTLEFIELD then return end if not cbParam or not cbParam.opponentUuid then return end local opponentUuid = cbParam.opponentUuid local isWin = (result == CombatDefine.RESULT_WIN) local oldRank = getRank(human.db._id) local newRank = oldRank -- 胜利:交换排名 if isWin then swapRank(human.db._id, opponentUuid) newRank = getRank(human.db._id) -- 刷新对手列表 local rank = getRank(human.db._id) local opponentList = generateOpponents(human.db._id, rank) setOpponentList(human.db._id, opponentList) -- 检查英雄解锁 checkAndUnlockHeroes(human, newRank) end -- 失败:发送邮件通知(给被挑战者) if not isWin then sendDefeatMail(opponentUuid, human.db._id, newRank) end -- 发放奖励(挑战奖励,rewardType=1) local rewardId = isWin and REWARD_ID_WIN or REWARD_ID_LOSE local rewardConfig = PeakBattlefieldRewardExcel.rankReward[rewardId] if rewardConfig and rewardConfig.rewardType == 1 and rewardConfig.reward then BagLogic.addItemList(human, rewardConfig.reward, "peak_battlefield_challenge") end -- 发送结果 local msgRet = Msg.gc.GC_PEAK_BATTLEFIELD_CHALLENGE msgRet.result = isWin and 1 or 2 msgRet.newRank = newRank -- 填充奖励 msgRet.reward[0] = 0 if rewardConfig and rewardConfig.rewardType == 1 and rewardConfig.reward then for i = 1, #rewardConfig.reward do local item = rewardConfig.reward[i] msgRet.reward[0] = msgRet.reward[0] + 1 Grid.makeItem(msgRet.reward[msgRet.reward[0]], item[1], item[2]) end end -- 胜利时发送新的对手列表 if isWin then local opponentList = getOpponentList(human.db._id) local len = 0 for i = 1, #opponentList do if len >= OPPONENT_LIST_SIZE then break end local opponent = opponentList[i] local opponentData = getData(opponent.uuid) if opponentData then len = len + 1 local net = msgRet.opponentList[len] net.uuid = opponent.uuid net.rank = opponent.rank or opponentData.rank local db = RoleDBLogic.getDb(opponent.uuid) if db then RoleLogic.makeRoleBase(db, net.roleBase) else net.roleBase.name = opponentData.name or "" net.roleBase.lv = opponentData.lv or 1 net.roleBase.head = opponentData.head or 0 net.roleBase.zhandouli = opponentData.zhandouli or 0 end end end msgRet.opponentList[0] = len else msgRet.opponentList[0] = 0 end Msg.send(msgRet, human.fd) end -- 查询排名奖励 function CommerceActPeakBettle_RankRewardQuery(human) local rank = getRank(human.db._id) local msgRet = Msg.gc.GC_PEAK_BATTLEFIELD_RANK_REWARD_QUERY msgRet.rank = rank -- 填充奖励列表(从Excel配置读取,只读取排名奖励 rewardType=2或3) local len = 0 if PeakBattlefieldRewardExcel.rankReward then for rewardID, config in pairs(PeakBattlefieldRewardExcel.rankReward) do -- 只处理排名奖励(rewardType=2或3),排除挑战奖励(rewardType=1) if config.rewardType and (config.rewardType == 2 or config.rewardType == 3) then if config.rank and type(config.rank) == "table" and #config.rank >= 2 then local rankMin = config.rank[1] local rankMax = config.rank[2] if rank >= rankMin and rank <= rankMax then len = len + 1 if len <= 20 then msgRet.rewardList[len] = rewardID end end end end end end msgRet.rewardList[0] = len Msg.send(msgRet, human.fd) end -- 活动结算(活动结束时发放排名奖励) function CommerceActPeakBettle_OnActivityEnd() -- 获取所有玩家数据 if not RANK_2_DATA then return end -- 遍历所有排名,发放奖励 for rank = 1, #RANK_2_DATA do local data = RANK_2_DATA[rank] if data and data._id then -- 根据排名查找对应的奖励配置(只处理排名奖励 rewardType=2或3) if PeakBattlefieldRewardExcel.rankReward then for rewardID, config in pairs(PeakBattlefieldRewardExcel.rankReward) do -- 只处理排名奖励,排除挑战奖励 if config.rewardType and (config.rewardType == 2 or config.rewardType == 3) then if config.rank and type(config.rank) == "table" and #config.rank >= 2 then local rankMin = config.rank[1] local rankMax = config.rank[2] if rank >= rankMin and rank <= rankMax then -- 发放排名奖励(通过邮件) local mailID = MAIL_ID_RANK_REWARD if mailID and mailID > 0 then local mailConfig = MailExcel.mail[mailID] if mailConfig and config.reward then local content = Util.format(mailConfig.content or "", rank) MailManager.add(MailManager.SYSTEM, data._id, mailConfig.title or "巅峰战场排名奖励", content, config.reward, mailConfig.senderName or "系统") end end break -- 找到对应奖励后跳出 end end end end end end end end -- 检查红点 function isRed(human) -- 可以根据需要实现红点逻辑 return false end ----------------------------------------- 战斗模块接口 ------------------------------------- -- 供战斗模块使用的公共接口 -- 获取玩家数据(供战斗模块使用) function GetData(uuid) return getData(uuid) end -- 检查对手是否存在(供战斗模块使用) function CheckOpponent(opponentUuid) local data = getData(opponentUuid) return data ~= nil end