local RolePvp = {} RolePvp.bind = function (Role) local PVP_RANK_TIME_SORT_STD = 1924876800 -- 2030-12-31 00:00:00 local PVP_RANK_TIME_SORT_PLACE = 1000000 -- 时间戳占据 6位数 local PVP_RANK_TIME_SORT_PRECISION = 360 -- 时间精度 每6分钟忽略差异 local PVP_RANK_BASE_SCORE = globalCsv.pvp_base_score -- 初始积分 -- 匹配规则改为以排名来匹配 local PVP_GET_ROBOT_SCORE = 2400 -- 2400分以下低档位匹配机器人 local PRE_RANGE_COUNT = 20 -- 每个档位人数 local NEED_MATCH = 3 --匹配到多少人 function Role:unpackPvpScore(score) if not score then return globalCsv.pvp_base_score end score = tonumber(score) return math.floor(score / PVP_RANK_TIME_SORT_PLACE) end function Role:packPvpScore(score, now) now = now or skynet.timex() return math.floor(score * PVP_RANK_TIME_SORT_PLACE + (PVP_RANK_TIME_SORT_STD - now) / PVP_RANK_TIME_SORT_PRECISION) end function Role:changePvpScore(rankKey, changeScoreCallback, matchId) local dbKey = self:getPvpDBKey(rankKey) local roleId = self:getProperty("id") local isPlayer = matchId ~= -1 local redret = redisproxy:pipelining(function(red) red:zscore(dbKey, roleId) red:zrevrank(dbKey, roleId) if isPlayer then red:zscore(dbKey, matchId) end end) local myScore = self:unpackPvpScore(redret[1]) local oldMyRank = tonumber(redret[2] or -2) + 1 local matchScore = PVP_RANK_BASE_SCORE if isPlayer then matchScore = self:unpackPvpScore(redret[3]) end local oldmyScore, oldMatchScore = myScore, matchScore myScore, matchScore = changeScoreCallback(myScore, matchScore) myScore = math.max(myScore, 0) matchScore = math.max(matchScore, 0) local now = skynet.timex() redret = redisproxy:pipelining(function(red) red:zadd(dbKey, self:packPvpScore(myScore, now), roleId) if isPlayer then red:zadd(dbKey, self:packPvpScore(matchScore, now), matchId) end red:zrevrank(dbKey, roleId) end) local myRank if isPlayer then myRank = tonumber(redret[3] or -2) + 1 else myRank = tonumber(redret[2] or -2) + 1 end return {myScore, matchScore, oldmyScore, oldMatchScore, myRank, oldMyRank} end function Role:changePvpScoreCommon(matchId, isWin) local function changeScoreCallback(myScore, matchScore) if isWin then local scoreChange = math.ceil(60 / (1 + 10 ^ ((myScore - matchScore) / 400))) myScore = myScore + scoreChange matchScore = matchScore - math.ceil(scoreChange / 3) -- 防守方失败时,扣分减为原来的1/3 else local scoreChange = math.ceil(60 / (1 + 10 ^ ((matchScore - myScore) / 400))) myScore = myScore - scoreChange matchScore = matchScore + scoreChange end return myScore, matchScore end local result = self:changePvpScore(RANK_PVP_COMMON, changeScoreCallback, matchId) self:refreshPvpMatchC(result[1], result[5]) return table.unpack(result) end function Role:getPvpHDivision(score) local score = score or self:unpackPvpScore(redisproxy:zscore(self:getPvpDBKey(RANK_PVP_HIGHT), self:getProperty("id"))) local divisionId = nil for _id, division in ipairs(csvdb["pvp_group_divisionCsv"]) do if score >= division.division then divisionId = _id else break end end return divisionId end function Role:calculatePvpHGift(division) local now = skynet.timex() local oldTime = self:getProperty("pvpHGTime") local oldReward = self:getProperty("pvpHGift") local newTime = oldTime local newReward = clone(oldReward) local divisionData = csvdb["pvp_group_divisionCsv"][division] if oldTime == 0 then newTime = now else local times = math.floor((now - oldTime) / globalCsv.pvp_high_reward_add_pre) newTime = oldTime + times * globalCsv.pvp_high_reward_add_pre for itemId, count in pairs(divisionData.reward:toNumMap()) do newReward[itemId] = math.min((newReward[itemId] or 0) + count * times, divisionData.limit) end end return newTime, newReward end function Role:changePvpScoreHigh(matchId, isWin) local oldMyDivision local function changeScoreCallback(myScore, matchScore) local myMinId = 0 local matchMinId = 0 for _id, division in ipairs(csvdb["pvp_group_divisionCsv"]) do if myScore >= division.division then myMinId = _id end if matchScore >= division.division then matchMinId = _id end end oldMyDivision = myMinId if isWin then local scoreChange = math.ceil(50 / (1 + 10 ^ ((myScore - matchScore) / 1000))) myScore = myScore + scoreChange matchScore = matchScore - math.ceil(scoreChange / 3) -- 防守方失败时,扣分减为原来的1/3 else local scoreChange = math.ceil(50 / (1 + 10 ^ ((matchScore - myScore) / 1000))) myScore = myScore - scoreChange matchScore = matchScore + scoreChange end return math.max(myScore, csvdb["pvp_group_divisionCsv"][myMinId].division), math.max(matchScore, csvdb["pvp_group_divisionCsv"][matchMinId].division) end local result = self:changePvpScore(RANK_PVP_HIGHT, changeScoreCallback, matchId) --{myScore, matchScore, oldmyScore, oldMatchScore, myRank, oldMyRank} local newDivision = nil for _id, division in ipairs(csvdb["pvp_group_divisionCsv"]) do if result[1] >= division.division then newDivision = _id else break end end if newDivision ~= oldMyDivision or self:getProperty("pvpHGTime") == 0 then --刷新段位奖励 local newTime, newReward = self:calculatePvpHGift(oldMyDivision) self:updateProperties({ pvpHGTime = newTime, pvpHGift = newReward, }) end self:refreshPvpMatchH(result[1], result[5]) return table.unpack(result) end function Role:isInPvpRank(rankKey) local Fields = { [RANK_PVP_COMMON] = "pvpMC", [RANK_PVP_HIGHT] = "pvpMH", } local dbKey = self:getPvpDBKey(rankKey) local roleId = self:getProperty("id") if redisproxy:zscore(dbKey, roleId) then return true end return false end -- 新的匹配规则 function Role:refreshPvpMatch(score, rank, rankKey) local Fields = { [RANK_PVP_COMMON] = "pvpMC", [RANK_PVP_HIGHT] = "pvpMH", } local RobotCsvs = { [RANK_PVP_COMMON] = "pvp_robotCsv", [RANK_PVP_HIGHT] = "pvp_robot_groupCsv", } local mField = Fields[rankKey] local robotCsv = RobotCsvs[rankKey] local dbKey = self:getPvpDBKey(rankKey) local roleId = self:getProperty("id") if not score and not rank then local redret = redisproxy:pipelining(function(red) red:zscore(dbKey, roleId) red:zrevrank(dbKey, roleId) end) score = self:unpackPvpScore(redret[1]) rank = tonumber(redret[2] or -2) + 1 end score = score or self:unpackPvpScore(redisproxy:zscore(dbKey, roleId)) rank = rank or tonumber(redisproxy:zrevrank(dbKey, roleId) or -2) + 1 local match = self:getProperty(mField) local hadPlayer = {[roleId] = 1} local hadRobot = {} for _, one in pairs(match) do if one.t == 1 then hadPlayer[one.id] = 1 elseif one.t == 2 then hadRobot[one.id] = 1 end end local tempMatch = {} local needRobot = 0 -- -1 没有上榜 if rank == -1 then needRobot = NEED_MATCH else local need = PRE_RANGE_COUNT * NEED_MATCH local low, heigh = math.floor(rank - need / 2 - 1), math.floor(rank + need / 2 - 1) if low < 0 then low = 0 end local rangeIds = redisproxy:ZREVRANGE(dbKey, low, heigh) local lastRangeIds = {} for idx, one in ipairs(rangeIds) do local cid = tonumber(one) if not hadPlayer[cid] then lastRangeIds[#lastRangeIds + 1] = cid end end if score < PVP_GET_ROBOT_SCORE then needRobot = 1 end local len = #lastRangeIds if len < NEED_MATCH then needRobot = NEED_MATCH - len end local needPlayer = NEED_MATCH - needRobot if needPlayer > 0 then local pre = math.floor(len / needPlayer) for i = 1, needPlayer do local idx = math.randomInt((i - 1) * pre + 1, i * pre) tempMatch[#tempMatch + 1] = {t = 1, id = lastRangeIds[idx]} end end end if needRobot > 0 then local RobotPoolCount = 20 local max = #csvdb[robotCsv] local min = 1 local mid while min <= max do mid = math.floor((min + max) / 2) local tempPt = csvdb[robotCsv][mid].pt if score == tempPt then break elseif score > tempPt then min = mid + 1 elseif score < tempPt then max = mid - 1 end end assert(mid, "pvp no robot " .. robotCsv) local low = mid - RobotPoolCount / 2 local heigh = mid + RobotPoolCount / 2 if low < 1 then heigh = heigh + (1 - low) low = 1 end if heigh > #csvdb[robotCsv] then heigh = #csvdb[robotCsv] end local pools = {} for i = low, heigh do if not hadRobot[i] then pools[#pools + 1] = i end end local pre = math.floor(#pools / needRobot) for i = 1, needRobot do local idx = math.randomInt((i - 1) * pre + 1, i * pre) tempMatch[#tempMatch + 1] = {t = 2, id = pools[idx]} end end self:setProperty(mField, tempMatch) end -- function Role:refreshPvpMatch(score, rank, rankKey) -- local Fields = { -- [RANK_PVP_COMMON] = "pvpMC", -- [RANK_PVP_HIGHT] = "pvpMH", -- } -- local RobotCsvs = { -- [RANK_PVP_COMMON] = "pvp_robotCsv", -- [RANK_PVP_HIGHT] = "pvp_robotCsv", -- } -- local mField = Fields[rankKey] -- local robotCsv = RobotCsvs[rankKey] -- local dbKey = self:getPvpDBKey(rankKey) -- local roleId = self:getProperty("id") -- score = score or self:unpackPvpScore(redisproxy:zscore(dbKey, roleId)) -- local function getPlayers(levels) -- local redret = redisproxy:pipelining(function(red) -- for _, level in ipairs(levels) do -- local low, high = table.unpack(level) -- red:zadd(dbKey, low * PVP_RANK_TIME_SORT_PLACE, "std_temp1") -- red:zadd(dbKey, high * PVP_RANK_TIME_SORT_PLACE + PVP_RANK_TIME_SORT_PLACE - 1, "std_temp2") -- red:ZREVRANK(dbKey, "std_temp1") -- red:ZREVRANK(dbKey, "std_temp2") -- end -- red:zrem(dbKey, "std_temp1", "std_temp2") -- end) -- local PreGetCount = 7 -- local redret = redisproxy:pipelining(function(red) -- for idx, level in ipairs(levels) do -- local rank1 = tonumber(redret[(idx - 1) * 4 + 3]) -- local rank2 = tonumber(redret[(idx - 1) * 4 + 4]) -- if rank1 - rank2 > PreGetCount then -- rank2 = math.randomInt(rank2, rank1 - PreGetCount + 1) -- rank1 = rank2 + PreGetCount - 1 -- end -- red:ZREVRANGE(dbKey, rank2, rank1) -- end -- end) -- return redret -- end -- local findIdx = #globalCsv.pvp_division -- for idx, limit in ipairs(globalCsv.pvp_division) do -- if score < limit then -- findIdx = idx - 1 -- break -- end -- end -- local levels = { -- {}, {}, {} -- } -- if globalCsv.pvp_division[findIdx + 1] then -- levels[1] = {globalCsv.pvp_division[findIdx + 1], (globalCsv.pvp_division[findIdx + 2] or 100000000) - 1} -- end -- levels[2] = {globalCsv.pvp_division[findIdx], (globalCsv.pvp_division[findIdx + 1] or 100000000) - 1} -- if globalCsv.pvp_division[findIdx - 1] then -- levels[3] = {globalCsv.pvp_division[findIdx - 1], globalCsv.pvp_division[findIdx] - 1} -- end -- local redirect = {} -- for i = #levels , 1, -1 do -- if not next(levels[i]) then -- table.remove(levels, i) -- redirect[i] = -1 -- for _, v in pairs(redirect) do -- redirect[_] = v - 1 -- end -- else -- redirect[i] = i -- end -- end -- local result = getPlayers(levels) -- local match = self:getProperty(mField) -- local hadPlayer = {[roleId] = 1} -- local hadRobot = {} -- for _, one in pairs(match) do -- if one.t == 1 then -- hadPlayer[one.id] = 1 -- elseif one.t == 2 then -- hadRobot[one.id] = 1 -- end -- end -- for _, temp in pairs(result) do -- for i = #temp, 1, -1 do -- local id = tonumber(temp[i]) -- if hadPlayer[id] then -- table.remove(temp, i) -- else -- temp[i] = id -- hadPlayer[id] = 1 -- end -- end -- end -- -- 增加第几个 -- local function getPlayer(idx) -- for i = idx, 3 do -- if redirect[i] ~= -1 then -- local curR = result[redirect[i]] or {} -- if next(curR) then -- local curIdx = math.randomInt(1, #curR) -- local objId = curR[curIdx] -- table.remove(curR, curIdx) -- return objId -- end -- end -- end -- end -- local tempMatch = {} -- local curCount = 0 -- for i = 1, 3 do -- local objId = getPlayer(i) -- if objId then -- tempMatch[i] = {t = 1, id = objId} -- curCount = curCount + 1 -- end -- end -- -- 正常的玩家不够了 低一档继续 -- if curCount < 3 then -- local level = nil -- if globalCsv.pvp_division[findIdx - 2] then -- level = {globalCsv.pvp_division[findIdx - 2], globalCsv.pvp_division[findIdx - 1] - 1} -- end -- if level then -- local result = getPlayers({level})[1] or {} -- for i = #result, 1, -1 do -- local id = tonumber(result[i]) -- if hadPlayer[id] then -- table.remove(result, i) -- else -- result[i] = id -- hadPlayer[id] = 1 -- end -- end -- if next(result) then -- for i = curCount + 1, 3 do -- local curIdx = math.randomInt(1, #result) -- local objId = result[curIdx] -- table.remove(result, curIdx) -- tempMatch[i] = {t = 1, id = objId} -- curCount = curCount + 1 -- if not next(result) then -- break -- end -- end -- end -- end -- end -- -- 增加机器人 -- if curCount < 3 then -- for i = curCount + 1, 3 do -- while true do -- local id = math.randomInt(1, #csvdb[robotCsv]) -- if not hadRobot[id] then -- hadRobot[id] = 1 -- tempMatch[i] = {t = 2, id = id} -- break -- end -- end -- end -- end -- self:setProperty(mField, tempMatch) -- end function Role:refreshPvpMatchC(score, rank) self:refreshPvpMatch(score, rank, RANK_PVP_COMMON) end function Role:refreshPvpMatchH(score, rank) self:refreshPvpMatch(score, rank, RANK_PVP_HIGHT) end function Role:getPvpDBKey(ptype) local dbList local round if ptype == RANK_PVP_COMMON then dbList = RANK_PVP_COMMON_KEY round = self:getTimeResetRound(TimeReset.PvpRank) elseif ptype == RANK_PVP_HIGHT then dbList = RANK_PVP_HIGHT_KEY round = self:getTimeResetRound(TimeReset.PvpHight) end local idx = 1 if round % 2 == 1 then idx = 2 end return dbList[idx] end -------------------- 跨服竞技场相关 ----------------------- local serverId = tonumber(skynet.getenv("servId")) -- 自己是否参赛 -- 更换头像 名字 队伍 需要上传 function Role:isCrossServerPvpPlayer() if not self:isTimeResetOpen(TimeReset.PvpCross) then self._isCrossServerPvpPlayer = nil return false end if self._isCrossServerPvpPlayer == nil then self:getCrossServerPvpMatchInfo() end return self._isCrossServerPvpPlayer end function Role:changeCrossServerPvpSelfInfo(cType) if not self:isCrossServerPvpPlayer() then return end local change = {id = self:packPvpCrossRoleId(serverId, self:getProperty("id"))} if cType == "name" or cType == "level" or cType == "headId" then change[cType] = self:getProperty(cType) elseif cType == "format" then -- 是否过了时间 local crossTime = skynet.timex() - self:getTimeResetStartTime(TimeReset.PvpCross) + RESET_TIME * 3600 local aday = 3600 * 24 local day = math.ceil(crossTime / aday) -- 当前是第几个比赛日 local ctime = crossTime % aday -- 当前在本天 经过多少时间 if day > globalCsv.pvp_cross_server_day or ctime >= globalCsv.pvp_cross_server_stop_format then return end change.battleV = self:getProperty("pvpTBVH") change.heros = self:getProperty("pvpTSH") change.battleInfo = self:getProperty("pvpTBH") end pcall(skynet.call, pvpd, "lua", "updateRoleInfo", change) end -- 获取当前阵容信息 function Role:getCrossServerPvpMatchInfo() if not self:isTimeResetOpen(TimeReset.PvpCross) then return end local ok, result = pcall(skynet.call, pvpd, "lua", "getMatchInfo") if ok then local cur = result.roleInfo and result.roleInfo[self:packPvpCrossRoleId(serverId, self:getProperty("id"))] self._isCrossServerPvpPlayer = cur and true or false return result else print(result) end end -- 获取角色信息 简略 function Role:getCrossServerPvpRoleInfo() if not self:isTimeResetOpen(TimeReset.PvpCross) then return end local ok, result = pcall(skynet.call, pvpd, "lua", "getRoleInfo") if ok then return result else print(result) end end function Role:getCrossServerPvpRoleInfoDeatil(pIds) if not self:isTimeResetOpen(TimeReset.PvpCross) then return end local ok, result = pcall(skynet.call, pvpd, "lua", "getRoleDetail", pIds) if ok then return result else print(result) end end function Role:getCrossServerPvpMatchRecord(round, matchIdx) if not self:isTimeResetOpen(TimeReset.PvpCross) then return end local ok, result = pcall(skynet.call, pvpd, "lua", "getMatchRecord", round, matchIdx) if ok then return result else print(result) end end function Role:getCrossServerPvpBetInfo() if not self:isTimeResetOpen(TimeReset.PvpCross) then return end local pvpBet = self:getProperty("pvpBet") local back = {} local ok, result = pcall(skynet.call, pvpd, "lua", "getBetInfo") if ok then if result.betInfo then local maxRound = 0 for round, matchIdx in pairs(result.betInfo) do if round > maxRound then maxRound = round end back[round] = { matchIdx = matchIdx, bet = pvpBet[round] } end if maxRound ~= 0 then back[maxRound].betNum = result.betNum end end return back else print(result) end end function Role:setCrossServerPvpBet(idx) if not self:isTimeResetOpen(TimeReset.PvpCross) then return false , 1 end local crossTime = skynet.timex() - self:getTimeResetStartTime(TimeReset.PvpCross) + RESET_TIME * 3600 local aday = 3600 * 24 local day = math.ceil(crossTime / aday) -- 当前是第几个比赛日 local ctime = crossTime % aday -- 当前在本天 经过多少时间 if day > globalCsv.pvp_cross_server_day or ctime >= globalCsv.pvp_cross_server_stop_stake then return false , 2 end local pvpBet = self:getProperty("pvpBet") if pvpBet[day] then return false , 3 end local costNum = self:getProperty("level") * globalCsv.pvp_cross_bet_pre_level local cost = {[ItemId.Gold] = costNum} if not self:checkItemEnough(cost) then return false , 4 end local ok, result = pcall(skynet.call, pvpd, "lua", "setBet", idx, self:getProperty("id"), costNum) if ok then if result then self:costItems(cost, {log = {desc = "crossPvpBet", int1 = day}}) pvpBet[day] = {idx, cost[ItemId.Gold]} self:setProperty("pvpBet", pvpBet) end return result, 5 else print(result) return false , 6 end end function Role:packPvpCrossRoleId(servId, roleId) return roleId * 1000 + servId end function Role:unpackPvpCrossRoleId(tempId) local servId = tempId % 1000 local roleId = math.floor(tempId / 1000) return servId, roleId end -- -- 获取参赛玩家详情 -- function Role: end return RolePvp