PvpAction.lua 9.91 KB
local ipairs = ipairs
local table = table
local math = math
local redisproxy = redisproxy
local MsgPack = MsgPack
local httpc = require("http.httpc")
local remoteUrl = skynet.getenv("codeurl")


local _M = {}
local _pvpBattleInfoCacheC = {}  --查询列表 缓存pvp战斗相关数据缓存
local _pvpStartBattleCache = nil -- 

local _pvpRecordInfoCache = {} -- 记录缓存
local _pvpRecordBattleInfoCache = {} -- 记录战斗数据缓存
local _revengeRecord = {} -- 复仇对单人1分钟间隔
local RevengeWaitTime = 60

function _M.formatCommonRpc(agent , data)
	local role = agent.role
	local roleId = role:getProperty("id")
	local msg = MsgPack.unpack(data)
	local pvpTC = role:getProperty("pvpTC")
	for slot, heroId in pairs(msg.heros or {}) do
		if not role.heros[heroId] then
			return
		end
	end
	if not msg.heros or not next(msg.heros) then
		return
	end

	table.clear(pvpTC)
	pvpTC.heros = {}
	for slot, heroId in pairs(msg.heros) do
		pvpTC.heros[slot] = heroId
	end
	pvpTC.leader = msg.leader
	
	role:savePvpCTeam(pvpTC)
	SendPacket(actionCodes.Pvp_formatCommonRpc, '')
	return true
end


local function getMatchInfo(role, pvpList, battleCache)
	table.clear(battleCache)
	local redret = redisproxy:pipelining(function(red)
		for _, info in ipairs(pvpList) do
			if info.t == 1 then
				red:zscore(RANK_PVP_COMMON, info.id)
			end
		end
	end)

	local matches = {}
	local curIdx = 1
	for idx, info in ipairs(pvpList) do
		local curInfo = {idx = idx}
		if info.t == 1 then --玩家
			curInfo.roleId = info.id
			curInfo.score = role:unpackPvpScore(redret[curIdx] or 0)
			curIdx = curIdx + 1
			-- name, level, headId, battleV, heros
			local online, roleInfo = rpcRole(curInfo.roleId, "pvpCInfo")
			for k , v in pairs(roleInfo) do
				if k == "battleInfo" then
					battleCache[curInfo.roleId] = v
				else
					curInfo[k] = v
				end
			end
		elseif info.t == 2 then  	-- robot
			curInfo.robot = info.id
		end
		table.insert(matches, curInfo)
	end
	return matches
end

-- 获取pvp信息
function _M.infoRpc(agent, data)
	local role = agent.role
	local roleId = role:getProperty("id")
	local msg = MsgPack.unpack(data)
	local ptype = msg.ptype or 1

	local response = {}
	if ptype == 1 then -- 普通pvp
		local redret = redisproxy:pipelining(function(red)
			red:zscore(RANK_PVP_COMMON, roleId)
			red:zrevrank(RANK_PVP_COMMON, roleId)
		end)
		local score = role:unpackPvpScore(redret[1] or 0)
		local rank = tonumber(redret[2] or -2) + 1  --排名 1 - ...  -1 未上榜 没打过pvp
		if rank == -1 then
			score = globalCsv.pvp_base_score
		end
		local pvpMC = role:getProperty("pvpMC")
		if not next(pvpMC) then --没有分配过对手
			role:refreshPvpMatchC(score)
			pvpMC = role:getProperty("pvpMC")
		end
		if not next(pvpMC) then return end
		
		response.rank = rank
		response.score = score
		response.matches = getMatchInfo(role, pvpMC, _pvpBattleInfoCacheC)

	elseif ptype == 2 then -- 高级pvp
		return
	else
		return
	end
	SendPacket(actionCodes.Pvp_infoRpc, MsgPack.pack(response))
	return true
end

function _M.refreshMatchCRpc(agent, data)
	local role = agent.role
	local msg = MsgPack.unpack(data)
	role:refreshPvpMatchC()

	local pvpMC = role:getProperty("pvpMC")
	local matches = getMatchInfo(role, pvpMC, _pvpBattleInfoCacheC)

	SendPacket(actionCodes.Pvp_refreshMatchCRpc, MsgPack.pack({matches = matches}))
	return true
end

function _M.buyCountRpc(agent, data)
	local role = agent.role
	local msg = MsgPack.unpack(data)
	local count = msg.count

	if math.illegalNum(count, 1, math.huge) then
		return 1
	end

	local cost = {[ItemId.Diamond] = globalCsv.pvp_buy_cost * count}
	if not role:checkItemEnough(cost) then return 2 end
	role:costItems(cost)
	role:award({[ItemId.PvpKey] = count})

	SendPacket(actionCodes.Pvp_buyCountRpc, '')
	return true
end

function _M.startBattleRpc(agent, data)
	local role = agent.role
	local msg = MsgPack.unpack(data)

	local idx = msg.idx
	local revenge = msg.revenge

	local pvpTC = role:getProperty("pvpTC")
	if not pvpTC.heros or not next(pvpTC.heros) then return 1 end

	local matchInfo, result, key, wait

	local now = skynet.timex()
	if revenge then  --复仇
		local temp = _pvpRecordInfoCache[idx]
		if not temp then return 2 end

		if not _revengeRecord[temp.id] or now >= _revengeRecord[temp.id] then
			if temp.t == 1 then
				matchInfo = _pvpRecordBattleInfoCache[temp.id]
			elseif temp.t == 2 then
				matchInfo = {robot = temp.id}
			end
		else
			result = 1
			wait = _revengeRecord[temp.id] - now
		end
	else  --打正常
		local pvpMC = role:getProperty("pvpMC")
		if not pvpMC[idx] then return 3 end
		if pvpMC[idx].t == 1 then
			matchInfo = _pvpBattleInfoCacheC[pvpMC[idx].id]
		elseif pvpMC[idx].t == 2 then
			matchInfo = {robot = pvpMC[idx].id}
		end
	end

	if not result and not matchInfo then return 4 end

	if not result then
		-- 次数扣一波
		local pvpFree = role.dailyData:getProperty("pvpFree")
		if pvpFree >= globalCsv.pvp_battle_free_count then
			local cost = {[ItemId.PvpKey] = 1}
			if not role:checkItemEnough(cost) then return 5 end
			role:costItems(cost)
		else
			role.dailyData:updateProperty({field = "pvpFree", delta = 1})
		end

		key = tostring(math.random())
		_pvpStartBattleCache = {idx = idx, key = key, revenge = revenge}

		role:checkTaskEnter("PvpBattle")
	end

	SendPacket(actionCodes.Pvp_startBattleRpc, MsgPack.pack({matchInfo = matchInfo, key = key, result = result, wait = wait}))
	return true
end

function _M.endBattleRpc(agent, data)
	local role = agent.role
	local roleId = role:getProperty("id")
	local msg = MsgPack.unpack(data)

	if not msg.key or not _pvpStartBattleCache or msg.key ~= _pvpStartBattleCache.key then 
		return 1
	end

	if not msg.idx or msg.idx ~= _pvpStartBattleCache.idx then
		return 2
	end

	local isWin = msg.starNum and msg.starNum > 0
	local now = skynet.timex()

	local revenge = _pvpStartBattleCache.revenge
	local match
	if revenge then
		match = _pvpRecordInfoCache[msg.idx]
		_revengeRecord[match.id] = now + RevengeWaitTime  -- 1分钟内不能再打
	else
		local pvpMC = role:getProperty("pvpMC")
		match = pvpMC[msg.idx]
	end
	
	if not match then return end

	local temp = string.randWeight(csvdb["player_expCsv"][role:getProperty("level")].pvpBonus, true)
	local reward = role:award({[temp[1]] = temp[2]})
	local myScore, matchScore, oldmyScore, oldMatchScore, myRank, oldMyRank = role:changePvpScoreCommon(match.t == 1 and match.id or -1, isWin)

	_pvpBattleInfoCacheC = {}  --重新发阵容了 没毛病
	_pvpRecordInfoCache = {} -- 记录刷新了
	_pvpRecordBattleInfoCache = {} -- 取新纪录的时候搞
	_pvpStartBattleCache = nil

	-- 请求上传录像
	local params = {
		["roleid"] = roleId,
		["key"] = "zhaolugame20191016",
		["time"] = now,
	}
	local status, body = httpc.get(remoteUrl, "/applyvideo?" .. httpGetFormatData(params), {}, {})
	local video = nil
	if tonumber(status) == 200 then
		local result = json.decode(body)
		video = result.name
	else
		skynet.error("applyvideo", "error", status, body, content)
	end

	-- 加入战斗记录
	redisproxy:pipelining(function(red)
		local dbKey = string.format(RECORD_PVP_COMMON, roleId)
		red:lpush(dbKey, MsgPack.pack({
			id = match.id,
			t = match.t,
			win = isWin,
			time = now,
			video = video,
			sdelta = myScore - oldmyScore,
		}))
		red:ltrim(dbKey, 0, 9)
		-- 对方加入战斗记录
		if match.t == 1 then
			dbKey = string.format(RECORD_PVP_COMMON, match.id)
			red:lpush(dbKey, MsgPack.pack({
				id = roleId,
				t = 1,
				win = not isWin,
				time = now,
				video = video,
				sdelta = matchScore - oldMatchScore,
			}))
			red:ltrim(dbKey, 0, 9)
		end
	end)

	if isWin then
		role:checkTaskEnter("PvpWin", {score = myScore})
	end
	
	SendPacket(actionCodes.Pvp_endBattleRpc, MsgPack.pack({
		reward = reward,
		myScore = myScore,
		matchScore = matchScore,
		oldmyScore = oldmyScore,
		oldMatchScore = oldMatchScore,
		myRank = myRank,
		oldMyRank = oldMyRank,
		video = video,
	}))
	return true
end

function _M.rankListRpc(agent, data)
	local role = agent.role
	local roleId = role:getProperty("id")
	local msg = MsgPack.unpack(data)
	local ptype = msg.ptype or 1

	local response = {}
	if ptype == 1 then -- 普通pvp
		local redret = redisproxy:pipelining(function(red)
			red:zscore(RANK_PVP_COMMON, roleId)
			red:zrevrank(RANK_PVP_COMMON, roleId)
			red:zrevrange(RANK_PVP_COMMON, 0, 99, "WITHSCORES")
		end)
		local score = role:unpackPvpScore(redret[1] or 0)
		local rank = tonumber(redret[2] or -2) + 1  --排名 1 - ...  -1 未上榜 没打过pvp
		if rank == -1 then
			score = globalCsv.pvp_base_score
		end
		local rankList = {}
		for i = 1, #redret[3], 2 do
			local roleId = tonumber(redret[3][i])
			local score = role:unpackPvpScore(redret[3][i + 1])
			local online, curInfo = rpcRole(roleId, "friendSInfo")
			curInfo.score = score
			curInfo.roleId = roleId
			table.insert(rankList, curInfo)
		end
		
		response.score = score
		response.rank = rank
		response.rankList = rankList

	elseif ptype == 2 then -- 高级pvp
		return
	else
		return
	end
	SendPacket(actionCodes.Pvp_rankListRpc, MsgPack.pack(response))
	return true
end

function _M.recordListRpc(agent, data)
	local role = agent.role
	local roleId = role:getProperty("id")
	local msg = MsgPack.unpack(data)
	local ptype = msg.ptype or 1

	local recordList = {}
	local now = skynet.timex()
	if ptype == 1 then -- 普通pvp
		local rlist = redisproxy:lrange(string.format(RECORD_PVP_COMMON, roleId), 0 , 9)
		local tempList = {}
		for _, temp in ipairs(rlist) do
			local one = MsgPack.unpack(temp)
			if now - one.time <= globalCsv.pvp_record_keep_time then -- 大于一天的弃之
				table.insert(tempList, one)
			end
		end
		_pvpRecordInfoCache = tempList

		recordList = getMatchInfo(role, tempList, _pvpRecordBattleInfoCache)
		for idx, info in ipairs(recordList) do
			local temp = tempList[idx]
			info.win = temp.win
			info.time = temp.time
			info.video = temp.video
			info.sdelta = temp.sdelta
		end
		
	elseif ptype == 2 then -- 高级pvp
		return
	else
		return
	end
	SendPacket(actionCodes.Pvp_recordListRpc, MsgPack.pack({list = recordList}))
	return true
end

return _M