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_ROBOT_SCORE = globalCsv.pvp_base_score -- 机器人积分 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_ROBOT_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 - scoreChange 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]) return table.unpack(result) 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, 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 - scoreChange 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 then --刷新段位奖励 local newTime, newReward = self:calculatePvpHGift(oldMyDivision) self:updateProperties({ pvpHGTime = newTime, pvpHGift = newReward, }) end self:refreshPvpMatchH(result[1]) return table.unpack(result) end function Role:refreshPvpMatch(score, 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) self:refreshPvpMatch(score, RANK_PVP_COMMON) end function Role:refreshPvpMatchH(score) self:refreshPvpMatch(score, 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 end return RolePvp