initial commit
This commit is contained in:
@@ -0,0 +1,384 @@
|
||||
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
|
||||
Reference in New Issue
Block a user