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

333 lines
12 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
local ____lualib = require("lualib_bundle")
local Set = ____lualib.Set
local __TS__New = ____lualib.__TS__New
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
local ____exports = {}
local grantContractCreepAbility
function grantContractCreepAbility(self, unit, abilityName)
if not IsValidEntity(unit) or not unit:IsAlive() then
return
end
if unit:FindAbilityByName(abilityName) then
return
end
do
pcall(function()
local ab = unit:AddAbility(abilityName)
if ab ~= nil then
local maxLevel = ab:GetMaxLevel()
ab:SetLevel(maxLevel > 0 and maxLevel or 1)
end
end)
end
end
--- Сколько вариантов названия «приговор» в локализации (#ds_sentence_title_001 …).
____exports.DEATH_SENTENCE_TITLE_INDEX_MAX = 100
--- Восстановление прочности контракта до максимума за донат-осколки (магазин).
____exports.DEATH_SENTENCE_CONTRACT_REPAIR_DONATE_COST = 25
--- Только усложнения, дающие крипу способность из wave_creep_abilities (без чистых статов / глобальных модификаторов).
____exports.DEATH_SENTENCE_COMPLICATION_POOL = {
"ds_complication_explode",
"ds_complication_zombie_virus",
"ds_complication_ghost_evasive",
"ds_complication_phasing_march",
"ds_complication_zombie_armor_decress",
"ds_complication_toxin",
"ds_complication_full_brutality",
"ds_complication_desperate_vampirism",
"ds_complication_head_leap",
"ds_complication_bone_armor"
}
--- Сколько случайных «дебаффов» волн (усложнений) по редкости приговора.
function ____exports.getDeathSentenceComplicationPickCount(self, rarity)
if rarity == "common" then
return 0
end
if rarity == "rare" then
return 1
end
if rarity == "epic" then
return 2
end
if rarity == "legendary" then
return 3
end
return 4
end
--- Шанс (1…100), что при появлении врага на него повесится одна строка усложнения из приговора.
-- Броски по строкам независимы — одному юниту может достаться сразу несколько абилок.
____exports.DEATH_SENTENCE_CONTRACT_CREEP_ABILITY_SPAWN_CHANCE_PCT = 25
--- Юниты ивентов (виспы, рыбалка, бомбы) — без пассивок усложнения приговора при спавне.
local DEATH_SENTENCE_CONTRACT_BUFF_BLOCKED_UNITS = __TS__New(Set, {"npc_wisps", "npc_fish_1", "npc_fish_2", "npc_bomb"})
--- Усложнение приговора → пассивка крипа из wave_creep_abilities (только то, что вешается при спавне).
local DEATH_SENTENCE_COMPLICATION_CREEP_ABILITY = {
ds_complication_explode = "zombie_death_explosion",
ds_complication_zombie_virus = "zombie_virus",
ds_complication_ghost_evasive = "ghost_evasive",
ds_complication_phasing_march = "wave_phasing_march",
ds_complication_zombie_armor_decress = "zombie_armor_decress",
ds_complication_toxin = "toxin",
ds_complication_full_brutality = "wave_full_brutality",
ds_complication_desperate_vampirism = "wave_desperate_vampirism",
ds_complication_head_leap = "contract_head_leap",
ds_complication_bone_armor = "bone_armor"
}
local function roundMult2(self, x)
return math.floor(x * 100 + 0.5) / 100
end
local function rollRarity(self)
local r = RandomInt(1, 100)
if r <= 40 then
return "common"
end
if r <= 70 then
return "rare"
end
if r <= 90 then
return "epic"
end
if r <= 98 then
return "legendary"
end
return "mythic"
end
local function rollRewardMultiplierForRarity(self, r)
if r == "common" then
return roundMult2(
nil,
RandomFloat(3, 4)
)
end
if r == "rare" then
return roundMult2(
nil,
RandomFloat(4, 5)
)
end
if r == "epic" then
return roundMult2(
nil,
RandomFloat(5, 6)
)
end
if r == "legendary" then
return roundMult2(
nil,
RandomFloat(6, 7)
)
end
return roundMult2(
nil,
RandomFloat(7, 9)
)
end
--- Детерминированный индекс названия для старых записей без titleIndex.
function ____exports.inferDeathSentenceTitleIndexFromInstanceId(self, instanceId)
local h = 0
do
local i = 0
while i < #instanceId do
local ch = string.byte(instanceId, i + 1) or 0
h = (h * 31 + ch) % ____exports.DEATH_SENTENCE_TITLE_INDEX_MAX
i = i + 1
end
end
return h + 1
end
--- Детерминированная прочность 1…5 для старых записей без поля (без RNG при каждом GET).
function ____exports.inferDeathSentenceDurabilityFromInstanceId(self, instanceId)
local h = 0
do
local i = 0
while i < #instanceId do
local ch = string.byte(instanceId, i + 1) or 0
h = (h * 31 + ch) % 1000000
i = i + 1
end
end
return h % 5 + 1
end
function ____exports.rollDeathSentenceContractDurability(self)
return RandomInt(1, 5)
end
function ____exports.normalizeDeathSentenceContractDurability(self, raw, instanceId)
local n = type(raw) == "number" and raw or tonumber(raw)
if type(n) == "number" and __TS__NumberIsFinite(n) then
local f = math.floor(n)
if f >= 1 and f <= 5 then
return f
end
end
return ____exports.inferDeathSentenceDurabilityFromInstanceId(nil, instanceId)
end
--- Макс. прочность из JSON; если поля нет или битое — не ниже текущей `currentDurability`.
function ____exports.normalizeDeathSentenceContractDurabilityMax(self, rawMax, currentDurability, instanceId)
local n = type(rawMax) == "number" and rawMax or tonumber(rawMax)
local m
if type(n) == "number" and __TS__NumberIsFinite(n) then
local f = math.floor(n)
if f >= 1 and f <= 5 then
m = f
else
m = currentDurability
end
else
m = currentDurability
end
return math.max(m, currentDurability)
end
function ____exports.normalizeDeathSentenceTitleIndex(self, raw, instanceId)
local n = type(raw) == "number" and raw or tonumber(raw)
if type(n) ~= "number" or not __TS__NumberIsFinite(n) then
return ____exports.inferDeathSentenceTitleIndexFromInstanceId(nil, instanceId)
end
local f = math.floor(n)
if f < 1 or f > ____exports.DEATH_SENTENCE_TITLE_INDEX_MAX then
return ____exports.inferDeathSentenceTitleIndexFromInstanceId(nil, instanceId)
end
return f
end
--- Пыль (dust_currency) за разбор приговора.
function ____exports.getDeathSentenceDismantleShardReward(self, rarity)
local base = {
common = 15,
rare = 35,
epic = 70,
legendary = 120,
mythic = 200
}
return base[rarity] or 15
end
--- Сколько секунд вычитается из дня за один слот усложнения «короткий день».
____exports.DEATH_SENTENCE_SHORT_DAY_SEC_PER_STACK = 60
local SCARCE_LOOT_DROP_FACTOR_PER_STACK = 0.5
--- Множитель шанса выпадения предметов с юнитов (см. ItemDropSystem): каждый слот `ds_complication_scarce_loot` даёт ×0.5.
function ____exports.getDeathSentenceItemDropChanceMultiplier(self, instance)
if not instance then
return 1
end
local stacks = 0
for ____, cid in ipairs(instance.complicationIds) do
if cid == "ds_complication_scarce_loot" then
stacks = stacks + 1
end
end
if stacks <= 0 then
return 1
end
return math.pow(SCARCE_LOOT_DROP_FACTOR_PER_STACK, stacks)
end
local function rollComplicationIdsForRarity(self, rarity)
local need = ____exports.getDeathSentenceComplicationPickCount(nil, rarity)
if need <= 0 then
return {}
end
local out = {}
while #out < need do
local pool = {unpack(____exports.DEATH_SENTENCE_COMPLICATION_POOL)}
do
local i = #pool - 1
while i > 0 do
local j = RandomInt(0, i)
local tmp = pool[i + 1]
pool[i + 1] = pool[j + 1]
pool[j + 1] = tmp
i = i - 1
end
end
do
local k = 0
while k < #pool and #out < need do
out[#out + 1] = pool[k + 1]
k = k + 1
end
end
end
return out
end
--- Один экземпляр в общем ростере (слот в инвентаре).
function ____exports.generateDeathSentenceContractInstance(self, slotIndex)
local rarity = rollRarity(nil)
local d = ____exports.rollDeathSentenceContractDurability(nil)
return {
instanceId = (("ds_ci_" .. tostring(slotIndex)) .. "_") .. tostring(RandomInt(10000, 99999999)),
serial = slotIndex + 1,
titleIndex = RandomInt(1, ____exports.DEATH_SENTENCE_TITLE_INDEX_MAX),
rarity = rarity,
rewardMultiplier = rollRewardMultiplierForRarity(nil, rarity),
traitId = "none",
complicationIds = rollComplicationIdsForRarity(nil, rarity),
durability = d,
durabilityMax = d
}
end
--- Создание экземпляра контракта с принудительной редкостью (для наград конца матча).
function ____exports.generateDeathSentenceContractInstanceWithRarity(self, slotIndex, rarity)
local d = ____exports.rollDeathSentenceContractDurability(nil)
return {
instanceId = (("ds_ci_" .. tostring(slotIndex)) .. "_") .. tostring(RandomInt(10000, 99999999)),
serial = slotIndex + 1,
titleIndex = RandomInt(1, ____exports.DEATH_SENTENCE_TITLE_INDEX_MAX),
rarity = rarity,
rewardMultiplier = rollRewardMultiplierForRarity(nil, rarity),
traitId = "none",
complicationIds = rollComplicationIdsForRarity(nil, rarity),
durability = d,
durabilityMax = d
}
end
--- После настройки длины дня в лобби: каждый слот `ds_complication_short_day` у выигравшего приговора
-- уменьшает длительность следующего дня на `DEATH_SENTENCE_SHORT_DAY_SEC_PER_STACK` секунд (не ниже порога менеджера).
function ____exports.applyDeathSentenceContractDayDurationAdjustments(self, instance)
if not IsServer() or not instance then
return
end
local stacks = 0
for ____, cid in ipairs(instance.complicationIds) do
if cid == "ds_complication_short_day" then
stacks = stacks + 1
end
end
if stacks <= 0 then
return
end
do
pcall(function()
local ____require_result_0 = require("DayNightCycleManager")
local DayNightCycleManager = ____require_result_0.DayNightCycleManager
DayNightCycleManager:getInstance():adjustNextDayDurationBy(-____exports.DEATH_SENTENCE_SHORT_DAY_SEC_PER_STACK * stacks)
end)
end
end
--- После базового скейла сложности в GameMode (враги).
function ____exports.applyDeathSentenceContractOnEnemySpawn(self, unit, instance)
if not IsServer() or not IsValidEntity(unit) then
return
end
if unit:GetTeam() == DOTA_TEAM_GOODGUYS then
return
end
if not instance then
return
end
local unitName = unit:GetUnitName()
if unitName and DEATH_SENTENCE_CONTRACT_BUFF_BLOCKED_UNITS:has(unitName) then
return
end
local chance = ____exports.DEATH_SENTENCE_CONTRACT_CREEP_ABILITY_SPAWN_CHANCE_PCT
for ____, cid in ipairs(instance.complicationIds) do
do
local abilityName = DEATH_SENTENCE_COMPLICATION_CREEP_ABILITY[cid]
if not abilityName then
goto __continue60
end
if RandomInt(1, 100) > chance then
goto __continue60
end
grantContractCreepAbility(nil, unit, abilityName)
end
::__continue60::
end
end
return ____exports