RolePvp.lua 7.75 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 = 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 roleId = self:getProperty("id")
	local isPlayer = matchId ~= -1
	local redret = redisproxy:pipelining(function(red)
		red:zscore(rankKey, roleId)
		red:zrevrank(rankKey, roleId)
		if isPlayer then
			red:zscore(rankKey, 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(rankKey, self:packPvpScore(myScore, now), roleId)
		if isPlayer then
			red:zadd(rankKey, self:packPvpScore(matchScore, now), matchId)
		end
		red:zrevrank(rankKey, 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:changePvpScoreHigh(matchId, isWin)
	local function changeScoreCallback(myScore, matchScore)
		local myMinScore = 0
		local matchMinScore = 0
		for _, division in ipairs(csvdb["pvp_group_divisionCsv"]) do
			if myScore >= division.division then
				myMinScore = division.division 
			end
			if matchScore >= division.division then
				matchMinScore = division.division 
			end
		end
		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, myMinScore), math.max(matchScore, matchMinScore)
	end

	local result = self:changePvpScore(RANK_PVP_HIGHT, changeScoreCallback, matchId)
	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 roleId = self:getProperty("id")
	score =  score or self:unpackPvpScore(redisproxy:zscore(rankKey, 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(rankKey, low * PVP_RANK_TIME_SORT_PLACE, "std_temp1")
				red:zadd(rankKey, high * PVP_RANK_TIME_SORT_PLACE + PVP_RANK_TIME_SORT_PLACE - 1, "std_temp2")
				red:ZREVRANK(rankKey, "std_temp1")
				red:ZREVRANK(rankKey, "std_temp2")
			end
			red:zrem(rankKey, "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(rankKey, 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

end


return RolePvp