Files
Dota-Zombie-Invasion/scripts/vscripts/equipment/manager.lua
T
2026-05-29 15:11:31 +07:00

438 lines
18 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__Number = ____lualib.__TS__Number
local __TS__ArrayFind = ____lualib.__TS__ArrayFind
local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys
local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray
local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign
local Set = ____lualib.Set
local __TS__ObjectValues = ____lualib.__TS__ObjectValues
local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter
local ____exports = {}
local ____difficulty_manager = require("difficulty_manager")
local Difficulty = ____difficulty_manager.Difficulty
local ____player_info = require("player_info")
local PlayerInfo = ____player_info.PlayerInfo
local ____store_manager = require("store_manager")
local StoreManager = ____store_manager.StoreManager
local ____modifier_equipment_stats = require("abilities.modifiers.modifier_equipment_stats")
local modifier_equipment_stats = ____modifier_equipment_stats.modifier_equipment_stats
local ____constants = require("equipment.constants")
local EQUIPMENT_MAX_UPGRADE_STAGE = ____constants.EQUIPMENT_MAX_UPGRADE_STAGE
local EQUIPMENT_STATE_VERSION = ____constants.EQUIPMENT_STATE_VERSION
local createEmptyLoadout = ____constants.createEmptyLoadout
local ____balance = require("equipment.balance")
local getUpgradeCostByStage = ____balance.getUpgradeCostByStage
local ____api = require("equipment.api")
local postEquipmentDropToApi = ____api.postEquipmentDropToApi
local loadEquipmentStateFromApi = ____api.loadEquipmentStateFromApi
local saveEquipmentStateToApi = ____api.saveEquipmentStateToApi
local ____roll = require("equipment.roll")
local rollAdditionalStatForStageFive = ____roll.rollAdditionalStatForStageFive
local rollPostMatchItem = ____roll.rollPostMatchItem
local rollRandomUpgradeDeltaPct = ____roll.rollRandomUpgradeDeltaPct
local EQUIPMENT_MODIFIER_NAME = modifier_equipment_stats.name
____exports.EquipmentManager = __TS__Class()
local EquipmentManager = ____exports.EquipmentManager
EquipmentManager.name = "EquipmentManager"
EquipmentManager.____file_path = "scripts/vscripts/equipment/manager.lua"
function EquipmentManager.prototype.____constructor(self)
self.playerState = __TS__New(Map)
self.playerOpLock = __TS__New(Map)
if not IsServer() then
return
end
self:registerEvents()
self:registerLoadHooks()
end
function EquipmentManager.getInstance(self)
if not ____exports.EquipmentManager.instance then
____exports.EquipmentManager.instance = __TS__New(____exports.EquipmentManager)
end
return ____exports.EquipmentManager.instance
end
function EquipmentManager.prototype.registerLoadHooks(self)
ListenToGameEvent(
"player_connect_full",
function(event)
local playerId = event.PlayerID
if playerId == nil or playerId < 0 then
return
end
self:ensureLoaded(playerId)
end,
nil
)
end
function EquipmentManager.prototype.registerEvents(self)
CustomGameEventManager:RegisterListener(
"equipment_request_state",
function(_source, event)
self:ensureLoaded(event.PlayerID, true)
end
)
CustomGameEventManager:RegisterListener(
"equipment_set_active_loadout",
function(_source, event)
self:withPlayerLock(
event.PlayerID,
function()
local state = self:getOrCreateState(event.PlayerID)
local nextIndex = math.floor(__TS__Number(event.loadout_index or 1))
if nextIndex < 1 or nextIndex > 3 then
self:sendActionResult(event.PlayerID, {success = false, message = "Некорректный слот пресета"})
return
end
state.activeLoadoutIndex = nextIndex
self:afterStateChanged(event.PlayerID, state)
end
)
end
)
CustomGameEventManager:RegisterListener(
"equipment_equip_item",
function(_source, event)
self:withPlayerLock(
event.PlayerID,
function()
local state = self:getOrCreateState(event.PlayerID)
local instanceId = tostring(event.instance_id or "")
local item = __TS__ArrayFind(
state.inventory,
function(____, x) return x.instanceId == instanceId end
)
if not item then
self:sendActionResult(event.PlayerID, {success = false, message = "Предмет не найден"})
return
end
local idxRaw = __TS__Number(event.loadout_index or state.activeLoadoutIndex)
local idx = math.floor(idxRaw)
local loadoutIndex = idx >= 1 and idx <= 3 and idx or state.activeLoadoutIndex
self:removeItemFromAllLoadouts(state, instanceId)
state.loadouts[loadoutIndex][item.slotType] = instanceId
self:afterStateChanged(event.PlayerID, state)
end
)
end
)
CustomGameEventManager:RegisterListener(
"equipment_unequip_item",
function(_source, event)
self:withPlayerLock(
event.PlayerID,
function()
local state = self:getOrCreateState(event.PlayerID)
local instanceId = tostring(event.instance_id or "")
local idxRaw = __TS__Number(event.loadout_index or state.activeLoadoutIndex)
local idx = math.floor(idxRaw)
local loadoutIndex = idx >= 1 and idx <= 3 and idx or state.activeLoadoutIndex
for ____, slot in ipairs(__TS__ObjectKeys(state.loadouts[loadoutIndex])) do
if state.loadouts[loadoutIndex][slot] == instanceId then
state.loadouts[loadoutIndex][slot] = nil
end
end
self:afterStateChanged(event.PlayerID, state)
end
)
end
)
CustomGameEventManager:RegisterListener(
"equipment_upgrade_item",
function(_source, event)
self:withPlayerLock(
event.PlayerID,
function()
local state = self:getOrCreateState(event.PlayerID)
local instanceId = tostring(event.instance_id or "")
local item = __TS__ArrayFind(
state.inventory,
function(____, x) return x.instanceId == instanceId end
)
if not item then
self:sendActionResult(event.PlayerID, {success = false, message = "Предмет не найден"})
return
end
if item.upgradeStage >= EQUIPMENT_MAX_UPGRADE_STAGE then
self:sendActionResult(event.PlayerID, {success = false, message = "Предмет уже максимального уровня"})
return
end
local store = StoreManager:getInstance()
local cost = getUpgradeCostByStage(nil, item.rarity, item.upgradeStage)
if not store:removeFreeCurrency(event.PlayerID, cost) then
self:sendActionResult(event.PlayerID, {success = false, message = "Недостаточно free currency"})
return
end
store:saveCurrencyToServer(event.PlayerID)
item.upgradeStage = item.upgradeStage + 1
if item.upgradeStage <= 4 then
for ____, stat in ipairs(item.stats) do
stat.valuePct = stat.valuePct + rollRandomUpgradeDeltaPct(nil)
end
elseif item.upgradeStage == 5 then
local extra = rollAdditionalStatForStageFive(nil, item.stats, item.rarity)
if extra then
local ____item_stats_0 = item.stats
____item_stats_0[#____item_stats_0 + 1] = extra
end
end
self:afterStateChanged(event.PlayerID, state)
end
)
end
)
end
function EquipmentManager.prototype.ensureLoaded(self, playerId, forcePublish)
if forcePublish == nil then
forcePublish = false
end
local hadState = self.playerState:has(playerId)
local ____local = self:getOrCreateState(playerId)
if not hadState or forcePublish then
self:publishState(playerId, ____local)
end
loadEquipmentStateFromApi(
nil,
playerId,
function(____, remote)
if not remote then
return
end
local normalized = self:normalizeState(remote)
self.playerState:set(playerId, normalized)
self:publishState(playerId, normalized)
end
)
end
function EquipmentManager.prototype.rollDropForPlayer(self, playerId, difficultyKey)
if not PlayerResource:IsValidPlayerID(playerId) or PlayerResource:IsFakeClient(playerId) then
return nil
end
local state = self:getOrCreateState(playerId)
local diff = difficultyKey or Difficulty.leader or "normal"
local item = rollPostMatchItem(nil, playerId, diff)
local ____state_inventory_1 = state.inventory
____state_inventory_1[#____state_inventory_1 + 1] = item
self:afterStateChanged(playerId, state, false)
postEquipmentDropToApi(nil, playerId, item)
local player = PlayerResource:GetPlayer(playerId)
if player then
CustomGameEventManager:Send_ServerToPlayer(player, "equipment_drop_received", {item = item})
end
return item
end
function EquipmentManager.prototype.applyEquipmentToHero(self, hero)
if not hero or not hero:IsRealHero() or hero:IsIllusion() then
return
end
local playerId = hero:GetPlayerOwnerID()
if playerId == nil or playerId < 0 then
return
end
local state = self:getOrCreateState(playerId)
local aggregate = self:aggregateStatsForActiveLoadout(state)
hero:AddNewModifier(
hero,
nil,
EQUIPMENT_MODIFIER_NAME,
{
luckPct = tostring(aggregate.luckPct),
strengthPct = tostring(aggregate.strengthPct),
intellectPct = tostring(aggregate.intellectPct),
damagePct = tostring(aggregate.damagePct),
damageReductionPct = tostring(aggregate.damageReductionPct),
spellAmpPct = tostring(aggregate.spellAmpPct)
}
)
end
function EquipmentManager.prototype.reapplyForPlayer(self, playerId)
if not PlayerResource:IsValidPlayerID(playerId) then
return
end
local hero = PlayerResource:GetSelectedHeroEntity(playerId)
if not hero then
return
end
self:applyEquipmentToHero(hero)
end
function EquipmentManager.prototype.getOrCreateState(self, playerId)
local existing = self.playerState:get(playerId)
if existing then
return existing
end
local info = PlayerInfo:GetPlayerInfo(playerId)
local fromInfo = info and info.equipment_state
local state = self:normalizeState(fromInfo)
self.playerState:set(playerId, state)
return state
end
function EquipmentManager.prototype.normalizeState(self, state)
local fallback = {
version = EQUIPMENT_STATE_VERSION,
inventory = {},
activeLoadoutIndex = 1,
loadouts = {
[1] = createEmptyLoadout(nil),
[2] = createEmptyLoadout(nil),
[3] = createEmptyLoadout(nil)
}
}
if not state or type(state) ~= "table" then
return fallback
end
local ____state_activeLoadoutIndex_4 = state.activeLoadoutIndex
if ____state_activeLoadoutIndex_4 == nil then
____state_activeLoadoutIndex_4 = 1
end
local activeRaw = __TS__Number(____state_activeLoadoutIndex_4)
local active = activeRaw >= 1 and activeRaw <= 3 and math.floor(activeRaw) or 1
local ____EQUIPMENT_STATE_VERSION_16 = EQUIPMENT_STATE_VERSION
local ____temp_17 = __TS__ArrayIsArray(state.inventory) and state.inventory or ({})
local ____createEmptyLoadout_result_7 = createEmptyLoadout(nil)
local ____opt_5 = state.loadouts
if ____opt_5 ~= nil then
____opt_5 = ____opt_5[1]
end
local ____TS__ObjectAssign_result_14 = __TS__ObjectAssign({}, ____createEmptyLoadout_result_7, ____opt_5)
local ____createEmptyLoadout_result_10 = createEmptyLoadout(nil)
local ____opt_8 = state.loadouts
if ____opt_8 ~= nil then
____opt_8 = ____opt_8[2]
end
local ____TS__ObjectAssign_result_15 = __TS__ObjectAssign({}, ____createEmptyLoadout_result_10, ____opt_8)
local ____createEmptyLoadout_result_13 = createEmptyLoadout(nil)
local ____opt_11 = state.loadouts
if ____opt_11 ~= nil then
____opt_11 = ____opt_11[3]
end
return {
version = ____EQUIPMENT_STATE_VERSION_16,
inventory = ____temp_17,
activeLoadoutIndex = active,
loadouts = {
[1] = ____TS__ObjectAssign_result_14,
[2] = ____TS__ObjectAssign_result_15,
[3] = __TS__ObjectAssign({}, ____createEmptyLoadout_result_13, ____opt_11)
}
}
end
function EquipmentManager.prototype.withPlayerLock(self, playerId, fn)
if self.playerOpLock:get(playerId) then
self:sendActionResult(playerId, {success = false, message = "Операция уже выполняется, подожди"})
return
end
self.playerOpLock:set(playerId, true)
do
pcall(function()
fn(nil)
end)
do
self.playerOpLock:set(playerId, false)
end
end
end
function EquipmentManager.prototype.afterStateChanged(self, playerId, state, includeActionOk)
if includeActionOk == nil then
includeActionOk = true
end
self.playerState:set(playerId, state)
self:publishState(playerId, state)
saveEquipmentStateToApi(nil, playerId, state)
if includeActionOk then
self:sendActionResult(playerId, {success = true})
end
self:reapplyForPlayer(playerId)
end
function EquipmentManager.prototype.publishState(self, playerId, state)
CustomNetTables:SetTableValue(
"equipment",
tostring(playerId),
state
)
local playerInfo = PlayerInfo:GetPlayerInfo(playerId) or ({})
playerInfo.equipment_state = state
PlayerInfo:UpdatePlayerInfo(playerId, playerInfo)
local player = PlayerResource:GetPlayer(playerId)
if player then
CustomGameEventManager:Send_ServerToPlayer(player, "equipment_state", {state = state})
end
end
function EquipmentManager.prototype.sendActionResult(self, playerId, payload)
local player = PlayerResource:GetPlayer(playerId)
if not player then
return
end
CustomGameEventManager:Send_ServerToPlayer(player, "equipment_action_result", payload)
end
function EquipmentManager.prototype.removeItemFromAllLoadouts(self, state, instanceId)
local loadouts = {state.loadouts[1], state.loadouts[2], state.loadouts[3]}
for ____, loadout in ipairs(loadouts) do
for ____, slot in ipairs(__TS__ObjectKeys(loadout)) do
if loadout[slot] == instanceId then
loadout[slot] = nil
end
end
end
end
function EquipmentManager.prototype.aggregateStatsForActiveLoadout(self, state)
local loadout = state.loadouts[state.activeLoadoutIndex]
local equippedIds = __TS__New(Set)
for ____, value in ipairs(__TS__ObjectValues(loadout)) do
if value then
equippedIds:add(value)
end
end
local equipped = __TS__ArrayFilter(
state.inventory,
function(____, x) return equippedIds:has(x.instanceId) end
)
local result = {
luckPct = 0,
strengthPct = 0,
intellectPct = 0,
damagePct = 0,
damageReductionPct = 0,
spellAmpPct = 0
}
for ____, item in ipairs(equipped) do
for ____, stat in ipairs(item.stats) do
repeat
local ____switch74 = stat.statKey
local ____cond74 = ____switch74 == "luck_pct"
if ____cond74 then
result.luckPct = result.luckPct + stat.valuePct
break
end
____cond74 = ____cond74 or ____switch74 == "strength_pct"
if ____cond74 then
result.strengthPct = result.strengthPct + stat.valuePct
break
end
____cond74 = ____cond74 or ____switch74 == "intellect_pct"
if ____cond74 then
result.intellectPct = result.intellectPct + stat.valuePct
break
end
____cond74 = ____cond74 or ____switch74 == "damage_pct"
if ____cond74 then
result.damagePct = result.damagePct + stat.valuePct
break
end
____cond74 = ____cond74 or ____switch74 == "damage_reduction_pct"
if ____cond74 then
result.damageReductionPct = result.damageReductionPct + stat.valuePct
break
end
____cond74 = ____cond74 or ____switch74 == "spell_amp_pct"
if ____cond74 then
result.spellAmpPct = result.spellAmpPct + stat.valuePct
break
end
until true
end
end
return result
end
return ____exports