RolePvp.lua 6.02 KB

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 = 1000 -- 机器人积分

local function unpackScore(score)
	score = tonumber(score or 0)
	return math.floor(score / PVP_RANK_TIME_SORT_PLACE)
end

local function packScore(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:changePvpScoreCommon(matchId, isWin)
	local roleId = self:getProperty("id")
	local isPlayer = matchId ~= -1
	local redret = redisproxy:pipelining(function(red)
		red:zscore(RANK_PVP_COMMON, roleId)
		red:zrevrank(RANK_PVP_COMMON, roleId)
		if isPlayer then
			red:zscore(RANK_PVP_COMMON, matchId)
		end
	end)
	local myScore = unpackScore(redret[1])
	local oldMyRank = tonumber(redret[2] or -2) + 1
	local matchScore = PVP_RANK_ROBOT_SCORE
	if isPlayer then
		matchScore = unpackScore(redret[3])
	end
	local oldmyScore, oldMatchScore = 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

	myScore = math.max(myScore, 0)
	matchScore = math.max(matchScore, 0)

	local now = skynet.timex()
	redisproxy:pipelining(function(red)
		red:zadd(RANK_PVP_COMMON, packScore(myScore, now), roleId)
		if isPlayer then
			red:zadd(RANK_PVP_COMMON, packScore(matchScore, now), matchId)
		end
		red:zrevrank(RANK_PVP_COMMON, roleId)
	end)
	local myRank
	if isPlayer then
		myRank = tonumber(redret[3] or -2) + 1
	else
		myRank = tonumber(redret[2] or -2) + 1
	end
	self:refreshPvpMatchC(myScore)
	return myScore, matchScore, oldmyScore, oldMatchScore, myRank, oldMyRank
end

function Role:refreshPvpMatchC(score)
	local roleId = self:getProperty("id")
	local score = score or redisproxy:zscore(RANK_PVP_COMMON, roleId) or 0

	local function getPlayers(levels)
		local redret = redisproxy:pipelining(function(red)
			for _, level in ipairs(levels) do
				local low, high = table.unpack(level)
				red:zadd(RANK_PVP_COMMON, low * PVP_RANK_TIME_SORT_PLACE, "std_temp1")
				red:zadd(RANK_PVP_COMMON, high * PVP_RANK_TIME_SORT_PLACE + PVP_RANK_TIME_SORT_PLACE - 1, "std_temp2")
				red:ZREVRANK(RANK_PVP_COMMON, "std_temp1")
				red:ZREVRANK(RANK_PVP_COMMON, "std_temp2")
			end
			red:zrem(RANK_PVP_COMMON, "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(RANK_PVP_COMMON, 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 matchC = self:getProperty("pvpMC")
	local hadPlayer = {[roleId] = 1}
	local hadRobot = {}
	for _, one in pairs(matchC) 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 tempMatchC = {}
	local curCount = 0
	for i = 1, 3 do
		local objId = getPlayer(i)
		if objId then
			tempMatchC[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)
					tempMatchC[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["pvp_robotCsv"])
				if not hadRobot[id] then
					hadRobot[id] = 1
					tempMatchC[i] = {t = 2, id = id}
					break
				end
			end
		end
	end

	self:setProperty("pvpMC", tempMatchC)
end


end


return RolePvp