403 lines
14 KiB
Lua
403 lines
14 KiB
Lua
local ____lualib = require("lualib_bundle")
|
|
local __TS__Class = ____lualib.__TS__Class
|
|
local Map = ____lualib.Map
|
|
local __TS__New = ____lualib.__TS__New
|
|
local __TS__ArrayFind = ____lualib.__TS__ArrayFind
|
|
local __TS__ArrayFindIndex = ____lualib.__TS__ArrayFindIndex
|
|
local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray
|
|
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
|
|
local __TS__NumberToFixed = ____lualib.__TS__NumberToFixed
|
|
local __TS__Number = ____lualib.__TS__Number
|
|
local __TS__ObjectValues = ____lualib.__TS__ObjectValues
|
|
local ____exports = {}
|
|
local ____api_helper = require("api_helper")
|
|
local encodeApiBody = ____api_helper.encodeApiBody
|
|
local setApiHeaders = ____api_helper.setApiHeaders
|
|
local ____server_config = require("server_config")
|
|
local SERVER_CONFIG = ____server_config.SERVER_CONFIG
|
|
local MAX_CONTRACT_SLOTS = 4
|
|
local MAX_CONTRACTS_PER_PLAYER = 24
|
|
local CONTRACT_DEBUFF_POOL = {
|
|
"Герои слабее на 20%",
|
|
"Герои слабее на 35%",
|
|
"Герои теряют 30% скорости атаки",
|
|
"Герои получают +20% входящего урона",
|
|
"Крипы получают +30% скорости передвижения",
|
|
"Снижение лечения героев на 40%",
|
|
"Кулдауны способностей героев +20%"
|
|
}
|
|
local CONTRACT_TITLE_POOL = {
|
|
"Смертельный приговор",
|
|
"Пакт отчаяния",
|
|
"Кровавый кодекс",
|
|
"Ночь без надежды",
|
|
"Предел боли"
|
|
}
|
|
local function randomIntInclusive(self, min, max)
|
|
return RandomInt(min, max)
|
|
end
|
|
local function debuffsArrayToMap(self, lines)
|
|
local result = {}
|
|
do
|
|
local i = 0
|
|
while i < #lines do
|
|
result[tostring(i)] = lines[i + 1]
|
|
i = i + 1
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
____exports.ContractsManager = __TS__Class()
|
|
local ContractsManager = ____exports.ContractsManager
|
|
ContractsManager.name = "ContractsManager"
|
|
ContractsManager.____file_path = "scripts/vscripts/contracts_manager.lua"
|
|
function ContractsManager.prototype.____constructor(self)
|
|
self.contractsByPlayer = __TS__New(Map)
|
|
self.activeSlots = {}
|
|
if not IsServer() then
|
|
return
|
|
end
|
|
CustomGameEventManager:RegisterListener(
|
|
"contracts_request_inventory",
|
|
function(_source, payload)
|
|
local playerId = payload.PlayerID
|
|
if playerId == nil or playerId < 0 then
|
|
return
|
|
end
|
|
self:loadContractsFromServer(playerId, true)
|
|
end
|
|
)
|
|
ListenToGameEvent(
|
|
"player_connect_full",
|
|
function(event)
|
|
local playerId = event.PlayerID
|
|
if playerId == nil or playerId < 0 then
|
|
return
|
|
end
|
|
Timers:CreateTimer(
|
|
1,
|
|
function()
|
|
self:loadContractsFromServer(playerId, true)
|
|
return nil
|
|
end
|
|
)
|
|
end,
|
|
nil
|
|
)
|
|
end
|
|
function ContractsManager.getInstance(self)
|
|
if not ____exports.ContractsManager.instance then
|
|
____exports.ContractsManager.instance = __TS__New(____exports.ContractsManager)
|
|
end
|
|
return ____exports.ContractsManager.instance
|
|
end
|
|
function ContractsManager.prototype.getActiveSlots(self)
|
|
return self.activeSlots
|
|
end
|
|
function ContractsManager.prototype.clearActiveSlots(self)
|
|
self.activeSlots = {}
|
|
end
|
|
function ContractsManager.prototype.getContractForPlayer(self, playerId, contractId)
|
|
local list = self.contractsByPlayer:get(playerId) or ({})
|
|
return __TS__ArrayFind(
|
|
list,
|
|
function(____, it) return it.id == contractId end
|
|
)
|
|
end
|
|
function ContractsManager.prototype.proposePlayerContract(self, playerId, contractId)
|
|
local contract = self:getContractForPlayer(playerId, contractId)
|
|
if not contract then
|
|
return false
|
|
end
|
|
local sameContractIdx = __TS__ArrayFindIndex(
|
|
self.activeSlots,
|
|
function(____, it) return it.contractId == contractId end
|
|
)
|
|
if sameContractIdx >= 0 then
|
|
self.activeSlots[sameContractIdx + 1] = {
|
|
contractId = contract.id,
|
|
ownerPlayerId = playerId,
|
|
title = contract.title,
|
|
statMultiplier = contract.statMultiplier,
|
|
rewardBonusPct = contract.rewardBonusPct,
|
|
debuffs = {unpack(contract.debuffs)}
|
|
}
|
|
return true
|
|
end
|
|
local existingByPlayerIdx = __TS__ArrayFindIndex(
|
|
self.activeSlots,
|
|
function(____, it) return it.ownerPlayerId == playerId end
|
|
)
|
|
if existingByPlayerIdx >= 0 then
|
|
self.activeSlots[existingByPlayerIdx + 1] = {
|
|
contractId = contract.id,
|
|
ownerPlayerId = playerId,
|
|
title = contract.title,
|
|
statMultiplier = contract.statMultiplier,
|
|
rewardBonusPct = contract.rewardBonusPct,
|
|
debuffs = {unpack(contract.debuffs)}
|
|
}
|
|
return true
|
|
end
|
|
if #self.activeSlots >= MAX_CONTRACT_SLOTS then
|
|
return false
|
|
end
|
|
local ____self_activeSlots_0 = self.activeSlots
|
|
____self_activeSlots_0[#____self_activeSlots_0 + 1] = {
|
|
contractId = contract.id,
|
|
ownerPlayerId = playerId,
|
|
title = contract.title,
|
|
statMultiplier = contract.statMultiplier,
|
|
rewardBonusPct = contract.rewardBonusPct,
|
|
debuffs = {unpack(contract.debuffs)}
|
|
}
|
|
return true
|
|
end
|
|
function ContractsManager.prototype.getContractById(self, contractId)
|
|
return __TS__ArrayFind(
|
|
self.activeSlots,
|
|
function(____, it) return it.contractId == contractId end
|
|
)
|
|
end
|
|
function ContractsManager.prototype.shouldGrantAfterWin(self, modeType, difficultyKey)
|
|
return modeType == "contract" or difficultyKey == "impossible"
|
|
end
|
|
function ContractsManager.prototype.grantProgressiveContractsToWinners(self, nextTierBase)
|
|
do
|
|
local playerId = 0
|
|
while playerId < DOTA_MAX_TEAM_PLAYERS do
|
|
do
|
|
local pid = playerId
|
|
if not PlayerResource:IsValidPlayerID(pid) or not PlayerResource:IsValidPlayer(pid) or PlayerResource:IsFakeClient(pid) then
|
|
goto __continue29
|
|
end
|
|
self:grantOneContract(pid, nextTierBase)
|
|
end
|
|
::__continue29::
|
|
playerId = playerId + 1
|
|
end
|
|
end
|
|
end
|
|
function ContractsManager.prototype.getHighestTierForPlayer(self, playerId)
|
|
local list = self.contractsByPlayer:get(playerId) or ({})
|
|
local maxTier = 0
|
|
for ____, contract in ipairs(list) do
|
|
if contract.tier > maxTier then
|
|
maxTier = contract.tier
|
|
end
|
|
end
|
|
return maxTier
|
|
end
|
|
function ContractsManager.prototype.grantOneContract(self, playerId, minTier)
|
|
local nextTier = math.max(1, minTier)
|
|
local generated = self:generateRandomContract(nextTier)
|
|
local list = self.contractsByPlayer:get(playerId) or ({})
|
|
if #list >= MAX_CONTRACTS_PER_PLAYER then
|
|
table.remove(list, 1)
|
|
end
|
|
list[#list + 1] = generated
|
|
self.contractsByPlayer:set(playerId, list)
|
|
self:persistContractsToServer(playerId)
|
|
self:sendInventoryToPlayer(playerId)
|
|
local steamId = PlayerResource:GetSteamAccountID(playerId)
|
|
if not steamId then
|
|
return
|
|
end
|
|
local req = CreateHTTPRequest(
|
|
"POST",
|
|
((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/contracts/grant"
|
|
)
|
|
setApiHeaders(nil, req)
|
|
req:SetHTTPRequestRawPostBody(
|
|
"application/json",
|
|
encodeApiBody(
|
|
nil,
|
|
{
|
|
contract = self:toApiContract(generated),
|
|
min_tier = minTier
|
|
}
|
|
)
|
|
)
|
|
req:Send(function(_res) return nil end)
|
|
end
|
|
function ContractsManager.prototype.loadContractsFromServer(self, playerId, syncToClient)
|
|
local steamId = PlayerResource:GetSteamAccountID(playerId)
|
|
if not steamId then
|
|
if syncToClient then
|
|
self:sendInventoryToPlayer(playerId)
|
|
end
|
|
return
|
|
end
|
|
local request = CreateHTTPRequest(
|
|
"GET",
|
|
((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/contracts"
|
|
)
|
|
setApiHeaders(nil, request)
|
|
request:Send(function(response)
|
|
if response.StatusCode < 200 or response.StatusCode >= 300 then
|
|
if syncToClient then
|
|
self:sendInventoryToPlayer(playerId)
|
|
end
|
|
return
|
|
end
|
|
local raw = {json.decode(response.Body or "null")}
|
|
local arr = __TS__ArrayIsArray(raw) and raw or (__TS__ArrayIsArray(raw and raw.contracts) and (raw and raw.contracts) or ({}))
|
|
local parsed = {}
|
|
for ____, item in ipairs(arr) do
|
|
local contract = self:fromApiContract(item)
|
|
if contract then
|
|
parsed[#parsed + 1] = contract
|
|
end
|
|
end
|
|
self.contractsByPlayer:set(playerId, parsed)
|
|
if syncToClient then
|
|
self:sendInventoryToPlayer(playerId)
|
|
end
|
|
end)
|
|
end
|
|
function ContractsManager.prototype.persistContractsToServer(self, playerId)
|
|
local steamId = PlayerResource:GetSteamAccountID(playerId)
|
|
if not steamId then
|
|
return
|
|
end
|
|
local list = self.contractsByPlayer:get(playerId) or ({})
|
|
local request = CreateHTTPRequest(
|
|
"PUT",
|
|
((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/contracts"
|
|
)
|
|
setApiHeaders(nil, request)
|
|
request:SetHTTPRequestRawPostBody(
|
|
"application/json",
|
|
encodeApiBody(
|
|
nil,
|
|
{contracts = __TS__ArrayMap(
|
|
list,
|
|
function(____, it) return self:toApiContract(it) end
|
|
)}
|
|
)
|
|
)
|
|
request:Send(function(_res) return nil end)
|
|
end
|
|
function ContractsManager.prototype.sendInventoryToPlayer(self, playerId)
|
|
local player = PlayerResource:GetPlayer(playerId)
|
|
if not player then
|
|
return
|
|
end
|
|
local contracts = self.contractsByPlayer:get(playerId) or ({})
|
|
local payload = {}
|
|
for ____, contract in ipairs(contracts) do
|
|
payload[contract.id] = {
|
|
id = contract.id,
|
|
tier = contract.tier,
|
|
title = contract.title,
|
|
stat_multiplier = contract.statMultiplier,
|
|
reward_bonus_pct = contract.rewardBonusPct,
|
|
debuffs = debuffsArrayToMap(nil, contract.debuffs)
|
|
}
|
|
end
|
|
CustomGameEventManager:Send_ServerToPlayer(player, "contracts_inventory_update", {contracts = payload})
|
|
end
|
|
function ContractsManager.prototype.generateRandomContract(self, tier)
|
|
local id = "contract_" .. DoUniqueString("cid")
|
|
local titleBase = CONTRACT_TITLE_POOL[randomIntInclusive(nil, 0, #CONTRACT_TITLE_POOL - 1) + 1]
|
|
local statMultiplier = __TS__Number(__TS__NumberToFixed(
|
|
4 + tier * 0.5 + RandomFloat(0, 1.2),
|
|
2
|
|
))
|
|
local rewardBonusPct = randomIntInclusive(nil, 10 + tier * 2, 30 + tier * 4)
|
|
local debuffCount = randomIntInclusive(nil, 1, 3)
|
|
local picked = {}
|
|
do
|
|
local i = 0
|
|
while i < debuffCount do
|
|
picked[#picked + 1] = CONTRACT_DEBUFF_POOL[randomIntInclusive(nil, 0, #CONTRACT_DEBUFF_POOL - 1) + 1]
|
|
i = i + 1
|
|
end
|
|
end
|
|
return {
|
|
id = id,
|
|
tier = tier,
|
|
title = (titleBase .. " T") .. tostring(tier),
|
|
statMultiplier = statMultiplier,
|
|
rewardBonusPct = rewardBonusPct,
|
|
debuffs = picked
|
|
}
|
|
end
|
|
function ContractsManager.prototype.fromApiContract(self, item)
|
|
local ____tostring_6 = tostring
|
|
local ____item_id_5 = item.id
|
|
if ____item_id_5 == nil then
|
|
____item_id_5 = ""
|
|
end
|
|
local id = ____tostring_6(____item_id_5)
|
|
if id == "" then
|
|
return nil
|
|
end
|
|
local ____tonumber_9 = tonumber
|
|
local ____tostring_8 = tostring
|
|
local ____item_tier_7 = item.tier
|
|
if ____item_tier_7 == nil then
|
|
____item_tier_7 = "1"
|
|
end
|
|
local tier = ____tonumber_9(____tostring_8(____item_tier_7)) or 1
|
|
local ____tostring_11 = tostring
|
|
local ____item_title_10 = item.title
|
|
if ____item_title_10 == nil then
|
|
____item_title_10 = "Смертельный приговор"
|
|
end
|
|
local title = ____tostring_11(____item_title_10)
|
|
local ____tonumber_15 = tonumber
|
|
local ____tostring_14 = tostring
|
|
local ____item_stat_multiplier_12 = item.stat_multiplier
|
|
if ____item_stat_multiplier_12 == nil then
|
|
____item_stat_multiplier_12 = item.statMultiplier
|
|
end
|
|
local ____item_stat_multiplier_12_13 = ____item_stat_multiplier_12
|
|
if ____item_stat_multiplier_12_13 == nil then
|
|
____item_stat_multiplier_12_13 = "4"
|
|
end
|
|
local statMultiplier = ____tonumber_15(____tostring_14(____item_stat_multiplier_12_13)) or 4
|
|
local ____tonumber_19 = tonumber
|
|
local ____tostring_18 = tostring
|
|
local ____item_reward_bonus_pct_16 = item.reward_bonus_pct
|
|
if ____item_reward_bonus_pct_16 == nil then
|
|
____item_reward_bonus_pct_16 = item.rewardBonusPct
|
|
end
|
|
local ____item_reward_bonus_pct_16_17 = ____item_reward_bonus_pct_16
|
|
if ____item_reward_bonus_pct_16_17 == nil then
|
|
____item_reward_bonus_pct_16_17 = "15"
|
|
end
|
|
local rewardBonusPct = ____tonumber_19(____tostring_18(____item_reward_bonus_pct_16_17)) or 15
|
|
local rawDebuffs = item.debuffs
|
|
local debuffs = {}
|
|
if __TS__ArrayIsArray(rawDebuffs) then
|
|
for ____, value in ipairs(rawDebuffs) do
|
|
debuffs[#debuffs + 1] = tostring(value)
|
|
end
|
|
elseif rawDebuffs and type(rawDebuffs) == "table" then
|
|
for ____, value in ipairs(__TS__ObjectValues(rawDebuffs)) do
|
|
debuffs[#debuffs + 1] = tostring(value)
|
|
end
|
|
end
|
|
return {
|
|
id = id,
|
|
tier = tier,
|
|
title = title,
|
|
statMultiplier = statMultiplier,
|
|
rewardBonusPct = rewardBonusPct,
|
|
debuffs = debuffs
|
|
}
|
|
end
|
|
function ContractsManager.prototype.toApiContract(self, contract)
|
|
return {
|
|
id = contract.id,
|
|
tier = contract.tier,
|
|
title = contract.title,
|
|
stat_multiplier = contract.statMultiplier,
|
|
reward_bonus_pct = contract.rewardBonusPct,
|
|
debuffs = contract.debuffs
|
|
}
|
|
end
|
|
____exports.Contracts = ____exports.ContractsManager:getInstance()
|
|
return ____exports
|