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