Files
2026-05-29 15:11:31 +07:00

385 lines
13 KiB
Lua

local ____lualib = require("lualib_bundle")
local __TS__Class = ____lualib.__TS__Class
local __TS__New = ____lualib.__TS__New
local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes
local ____exports = {}
local ____difficulty_manager = require("difficulty_manager")
local Difficulty = ____difficulty_manager.Difficulty
local ____contracts_registry = require("death_sentence.contracts_registry")
local getDeathSentenceItemDropChanceMultiplier = ____contracts_registry.getDeathSentenceItemDropChanceMultiplier
local ____luck = require("utils.luck")
local getLuck = ____luck.getLuck
local rollLuckChance = ____luck.rollLuckChance
local ____get_true_hero_from_entity = require("utils.get_true_hero_from_entity")
local getTrueHeroFromEntity = ____get_true_hero_from_entity.getTrueHeroFromEntity
--- Маркер на CDOTA_Item: дроп из этой системы (KV без стакаемой шаряемости тоже обрабатываем по флагу).
local ITEM_DROP_SYSTEM_MARK = "_item_drop_system_world_loot"
local KV_FULLY_SHAREABLE_STACKING = "ITEM_FULLY_SHAREABLE_STACKING"
--- Скан слотов инвентаря героя (основной + рюкзак + нейтральные слоты при наличии).
local HERO_ITEM_SLOT_SCAN_MAX = 16
____exports.ItemDropSystem = __TS__Class()
local ItemDropSystem = ____exports.ItemDropSystem
ItemDropSystem.name = "ItemDropSystem"
ItemDropSystem.____file_path = "scripts/vscripts/item_drops.lua"
function ItemDropSystem.prototype.____constructor(self)
ListenToGameEvent(
"entity_killed",
function(event) return ____exports.ItemDropSystem:OnEntityKilled(event) end,
nil
)
ListenToGameEvent(
"dota_item_picked_up",
function(event) return ____exports.ItemDropSystem:OnItemPickedUp(event) end,
nil
)
end
function ItemDropSystem.Initialize(self)
if not ____exports.ItemDropSystem.instance then
____exports.ItemDropSystem.instance = __TS__New(____exports.ItemDropSystem)
end
end
function ItemDropSystem.spawnLootOnGround(self, position, itemName, quantity, despawnTime)
if quantity <= 0 then
return
end
local items = {}
local first = CreateItem(itemName, nil, nil)
if not first then
return
end
if quantity > 1 and first:GetInitialCharges() == 1 then
first:SetCurrentCharges(quantity)
items[#items + 1] = first
else
items[#items + 1] = first
do
local i = 1
while i < quantity do
local extra = CreateItem(itemName, nil, nil)
if extra then
items[#items + 1] = extra
end
i = i + 1
end
end
end
for ____, item in ipairs(items) do
item[ITEM_DROP_SYSTEM_MARK] = true
local physicalItem = CreateItemOnPositionForLaunch(position, item)
local dropRadius = RandomFloat(50, 100)
item:LaunchLootInitialHeight(
false,
0,
150,
0.5,
position + RandomVector(dropRadius)
)
if despawnTime and despawnTime > 0 then
Timers:CreateTimer(
despawnTime,
function()
if not physicalItem or not IsValidEntity(physicalItem) then
return
end
local owner = physicalItem:GetOwner()
if not owner then
UTIL_Remove(physicalItem)
end
end
)
end
end
end
function ItemDropSystem.SpawnRandomDropsOnDeath(self, unit, killingHero)
if not unit then
return
end
local position = unit:GetAbsOrigin()
local unitName = unit:GetUnitName()
local dsDropMult = getDeathSentenceItemDropChanceMultiplier(
nil,
Difficulty:getWinningContractSnapshot()
)
local luckHero = killingHero ~= nil and killingHero ~= nil and IsValidEntity(killingHero) and killingHero:IsRealHero() and not killingHero:IsIllusion() and killingHero or nil
for ____, dropItem in ipairs(self.possibleDrops) do
do
if #dropItem.unitTypes > 0 and not __TS__ArrayIncludes(dropItem.unitTypes, unitName) then
goto __continue22
end
local effectiveChance = dropItem.chance >= 100 and 100 or math.max(
0,
math.floor(dropItem.chance * dsDropMult + 1e-9)
)
if effectiveChance <= 0 then
goto __continue22
end
local baseFraction = effectiveChance / 100
local ____temp_0
if luckHero ~= nil then
____temp_0 = rollLuckChance(nil, luckHero, baseFraction)
else
____temp_0 = RandomInt(1, 100) <= effectiveChance
end
local dropped = ____temp_0
if dropped then
local quantity
if type(dropItem.quantity) == "number" then
quantity = dropItem.quantity
else
local q = dropItem.quantity
quantity = RandomInt(q.min, q.max)
if luckHero ~= nil and q.max > q.min then
local luckBonus = math.max(
0,
math.floor(getLuck(nil, luckHero) / 10)
)
quantity = quantity + luckBonus
end
end
____exports.ItemDropSystem:spawnLootOnGround(position, dropItem.itemName, quantity, dropItem.despawnTime)
end
end
::__continue22::
end
end
function ItemDropSystem.readItemShareabilityKv(self, item)
do
local ____try, ____hasReturned, ____returnValue = pcall(function()
local kv = item:GetAbilityKeyValues()
if kv and type(kv) == "table" then
local v = kv.ItemShareability
if v ~= nil and v ~= nil then
return true, tostring(v)
end
end
end)
if ____try and ____hasReturned then
return ____returnValue
end
end
do
local ____try, ____hasReturned, ____returnValue = pcall(function()
local anyItem = item
local ____opt_1 = anyItem.GetAbilityKeyValue
local v2 = ____opt_1 and ____opt_1(anyItem, "ItemShareability")
if v2 ~= nil and v2 ~= nil then
return true, tostring(v2)
end
end)
if ____try and ____hasReturned then
return ____returnValue
end
end
return nil
end
function ItemDropSystem.isFullyShareableStackingKv(self, item)
return ____exports.ItemDropSystem:readItemShareabilityKv(item) == KV_FULLY_SHAREABLE_STACKING
end
function ItemDropSystem.isKnownWorldDropItemName(self, itemName)
for ____, row in ipairs(____exports.ItemDropSystem.possibleDrops) do
if row.itemName == itemName then
return true
end
end
return false
end
function ItemDropSystem.slotQualifiesForPurchaserRefresh(self, it, itemName, includeDropTableName)
if it:GetAbilityName() ~= itemName then
return false
end
local mark = it
if mark[ITEM_DROP_SYSTEM_MARK] == true then
return true
end
if ____exports.ItemDropSystem:isFullyShareableStackingKv(it) then
return true
end
return includeDropTableName and ____exports.ItemDropSystem:isKnownWorldDropItemName(itemName)
end
function ItemDropSystem.applyPurchaserToHeroSlotsForItem(self, hero, itemName, includeDropTableName)
if not hero or not hero:IsRealHero() or not itemName then
return
end
do
local slot = 0
while slot < HERO_ITEM_SLOT_SCAN_MAX do
do
local it = hero:GetItemInSlot(slot)
if not it or it:IsNull() then
goto __continue47
end
if not ____exports.ItemDropSystem:slotQualifiesForPurchaserRefresh(it, itemName, includeDropTableName) then
goto __continue47
end
do
pcall(function()
local ____this_4
____this_4 = it
local ____opt_3 = ____this_4.SetPurchaser
if ____opt_3 ~= nil then
____opt_3(____this_4, hero)
end
end)
end
end
::__continue47::
slot = slot + 1
end
end
end
function ItemDropSystem.schedulePurchaserRefreshAfterPickup(self, hero, itemName, includeDropTableName)
local function run()
return ____exports.ItemDropSystem:applyPurchaserToHeroSlotsForItem(hero, itemName, includeDropTableName)
end
run(nil)
Timers:CreateTimer(
0,
function()
run(nil)
return nil
end
)
Timers:CreateTimer(
0.06,
function()
run(nil)
return nil
end
)
Timers:CreateTimer(
0.15,
function()
run(nil)
return nil
end
)
end
function ItemDropSystem.OnItemPickedUp(self, event)
if not IsServer() then
return
end
local heroIndex = event.HeroEntityIndex
local itemIndex = event.ItemEntityIndex
if heroIndex == nil or heroIndex == nil or itemIndex == nil or itemIndex == nil then
return
end
local hero = EntIndexToHScript(heroIndex)
local item = EntIndexToHScript(itemIndex)
if not hero or not hero:IsRealHero() or not item or not IsValidEntity(item) then
return
end
local itemName = item:GetAbilityName()
if not itemName then
return
end
local mark = item
local fromWorldDrop = mark[ITEM_DROP_SYSTEM_MARK] == true
local stackingShareable = ____exports.ItemDropSystem:isFullyShareableStackingKv(item)
if not fromWorldDrop and not stackingShareable then
return
end
____exports.ItemDropSystem:schedulePurchaserRefreshAfterPickup(hero, itemName, fromWorldDrop)
end
function ItemDropSystem.OnEntityKilled(self, event)
if not IsServer() then
return
end
local killedUnit = EntIndexToHScript(event.entindex_killed)
if not killedUnit or not killedUnit:IsBaseNPC() then
return
end
local attackerIndex = event.entindex_attacker
local ____temp_5
if attackerIndex ~= nil and attackerIndex ~= nil and attackerIndex > 0 then
____temp_5 = EntIndexToHScript(attackerIndex)
else
____temp_5 = nil
end
local attackerEnt = ____temp_5
local ____attackerEnt_6
if attackerEnt then
____attackerEnt_6 = getTrueHeroFromEntity(nil, attackerEnt)
else
____attackerEnt_6 = nil
end
local killingHero = ____attackerEnt_6
____exports.ItemDropSystem:SpawnRandomDropsOnDeath(killedUnit, killingHero)
end
function ItemDropSystem.getInstance(self)
if not ____exports.ItemDropSystem.instance then
____exports.ItemDropSystem.instance = __TS__New(____exports.ItemDropSystem)
end
return ____exports.ItemDropSystem.instance
end
ItemDropSystem.possibleDrops = {
{
itemName = "item_meat",
chance = 50,
quantity = {min = 1, max = 3},
unitTypes = {"npc_pig"},
despawnTime = 15
},
{
itemName = "item_milk",
chance = 50,
quantity = {min = 1, max = 3},
unitTypes = {"npc_sheep"},
despawnTime = 15
},
{
itemName = "item_wolf_claw",
chance = 35,
quantity = {min = 1, max = 2},
unitTypes = {"npc_wolf"},
despawnTime = 15
},
{
itemName = "item_ent_heart",
chance = 80,
quantity = {min = 1, max = 2},
unitTypes = {"npc_ent"},
despawnTime = 15
},
{
itemName = "item_frog_paw",
chance = 20,
quantity = {min = 1, max = 4},
unitTypes = {"npc_frogman_magi"},
despawnTime = 25
},
{
itemName = "item_frog_paw",
chance = 15,
quantity = {min = 1, max = 4},
unitTypes = {"npc_frop_tadpole"},
despawnTime = 25
},
{
itemName = "item_frog_paw",
chance = 10,
quantity = {min = 1, max = 4},
unitTypes = {"npc_small_frog_froglet"},
despawnTime = 25
},
{
itemName = "item_frog_paw",
chance = 5,
quantity = {min = 1, max = 2},
unitTypes = {"npc_mini_frog"},
despawnTime = 25
},
{itemName = "item_poison", chance = 75, quantity = 1, unitTypes = {"npc_venomancer_brute", "npc_ravenous_woodfang", "npc_lycosidae_stalker"}},
{itemName = "item_spider_legs_custom", chance = 40, quantity = {min = 1, max = 6}, unitTypes = {"npc_ravenous_woodfang", "npc_lycosidae_stalker"}},
{itemName = "item_aghanims_shard_roshan", chance = 100, quantity = 1, unitTypes = {"npc_witch"}},
{itemName = "item_lycan_horn", chance = 100, quantity = 1, unitTypes = {"npc_boss_lycan"}},
{itemName = "item_wooden_katana", chance = 100, quantity = 1, unitTypes = {"npc_sakura_tree"}},
{
itemName = "item_egg",
chance = 20,
quantity = 1,
unitTypes = {"npc_chicken"},
despawnTime = 25
}
}
return ____exports