728 lines
22 KiB
Lua
728 lines
22 KiB
Lua
local ____lualib = require("lualib_bundle")
|
||
local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite
|
||
local ____exports = {}
|
||
local ARSENAL_RARITY_ORDER = {
|
||
"common",
|
||
"rare",
|
||
"epic",
|
||
"legendary",
|
||
"mythic"
|
||
}
|
||
local ASCENDED_STAT_QUALITY = "ascended"
|
||
local ARSENAL_RARITY_MULTIPLIER = {
|
||
common = 1,
|
||
rare = 1.2,
|
||
epic = 1.45,
|
||
legendary = 1.75,
|
||
mythic = 2.1,
|
||
ascended = 2.55
|
||
}
|
||
local BASE_STAT_COUNT = 2
|
||
local ADDITIONAL_STAT_COUNT = 4
|
||
local HIDDEN_FINAL_STAT_INDEX = 6
|
||
local HIDDEN_FINAL_UNLOCK_LEVEL = 1
|
||
local MAX_UPGRADE_LEVEL = 5
|
||
local PER_LEVEL_GROWTH = 0.12
|
||
local MYTHIC_BEST_LINE_CHANCE_PCT = 24
|
||
local MYTHIC_WEAK_LINE_CHANCE_PCT = 10
|
||
--- С вероятностью N% новая строка копирует ключ уже существующего (ниже — реже дубли одного и того же стата на предмете).
|
||
local ARSENAL_REPEAT_EXISTING_STAT_CHANCE_PCT = 26
|
||
local MAX_RARITY_INDEX = #ARSENAL_RARITY_ORDER - 1
|
||
local MAX_RARITY_CAP_APPROACH_PER_STEP = 0.22
|
||
local ADDITIONAL_SLOTS_BY_ITEM_QUALITY = {
|
||
common = 0,
|
||
rare = 1,
|
||
epic = 2,
|
||
legendary = 3,
|
||
mythic = 4
|
||
}
|
||
--- Диапазоны с учётом 6 слотов экипировки и роста от редкости/уровня (см. Panorama `getTemplateForStatKey`).
|
||
--
|
||
-- `upgradeGrowthMin` / `upgradeGrowthMax` — диапазон для **`k`** в формуле итога:
|
||
-- `значение ∝ effectiveBase × rarityMult × (1 + k × lineBonus)`.
|
||
-- Это **не** «+N к тултипу» и **не** проценты вида «15 = 15%»; типичные **`k`** — **доли 0.08…0.14**
|
||
-- (при `lineBonus = 5` множитель прокачки около **1.4…1.7**).
|
||
local ARSENAL_STAT_POOL = {
|
||
{
|
||
key = "bonus_damage",
|
||
min = 12,
|
||
max = 64,
|
||
upgradeGrowthMin = 0.1,
|
||
upgradeGrowthMax = 0.14
|
||
},
|
||
{
|
||
key = "attack_speed",
|
||
min = 5,
|
||
max = 22,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.13
|
||
},
|
||
{
|
||
key = "bonus_armor",
|
||
min = 2,
|
||
max = 8,
|
||
upgradeGrowthMin = 0.1,
|
||
upgradeGrowthMax = 0.14
|
||
},
|
||
{
|
||
key = "max_health",
|
||
min = 70,
|
||
max = 240,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.12
|
||
},
|
||
{
|
||
key = "max_mana",
|
||
min = 50,
|
||
max = 180,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.12
|
||
},
|
||
{
|
||
key = "health_regen",
|
||
min = 0.4,
|
||
max = 3.2,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.12
|
||
},
|
||
{
|
||
key = "mana_regen",
|
||
min = 0.2,
|
||
max = 1.8,
|
||
decimals = 2,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.12
|
||
},
|
||
{
|
||
key = "move_speed",
|
||
min = 6,
|
||
max = 12,
|
||
upgradeGrowthMin = 0.08,
|
||
upgradeGrowthMax = 0.12
|
||
},
|
||
{
|
||
key = "magic_resist",
|
||
min = 0.2,
|
||
max = 1.2,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.04,
|
||
upgradeGrowthMax = 0.1
|
||
},
|
||
{
|
||
key = "spell_amp",
|
||
min = 1,
|
||
max = 5,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.08,
|
||
upgradeGrowthMax = 0.13
|
||
},
|
||
{
|
||
key = "all_stats",
|
||
min = 2,
|
||
max = 5,
|
||
upgradeGrowthMin = 0.045,
|
||
upgradeGrowthMax = 0.07
|
||
},
|
||
{
|
||
key = "bonus_strength",
|
||
min = 4,
|
||
max = 16,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.13
|
||
},
|
||
{
|
||
key = "bonus_agility",
|
||
min = 4,
|
||
max = 16,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.13
|
||
},
|
||
{
|
||
key = "bonus_intellect",
|
||
min = 4,
|
||
max = 16,
|
||
upgradeGrowthMin = 0.09,
|
||
upgradeGrowthMax = 0.13
|
||
},
|
||
{
|
||
key = "all_stats_pct",
|
||
min = 0.2,
|
||
max = 1.1,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.04,
|
||
upgradeGrowthMax = 0.065
|
||
},
|
||
{
|
||
key = "strength_pct",
|
||
min = 0.8,
|
||
max = 10,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.05,
|
||
upgradeGrowthMax = 0.08
|
||
},
|
||
{
|
||
key = "agility_pct",
|
||
min = 0.8,
|
||
max = 10,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.05,
|
||
upgradeGrowthMax = 0.08
|
||
},
|
||
{
|
||
key = "intellect_pct",
|
||
min = 0.8,
|
||
max = 10,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.05,
|
||
upgradeGrowthMax = 0.08
|
||
},
|
||
{
|
||
key = "luck",
|
||
min = 0.5,
|
||
max = 2.5,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.08,
|
||
upgradeGrowthMax = 0.14
|
||
},
|
||
{
|
||
key = "outgoing_damage_pct",
|
||
min = 0.8,
|
||
max = 3.5,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.06,
|
||
upgradeGrowthMax = 0.1
|
||
},
|
||
{
|
||
key = "incoming_damage_reduction_pct",
|
||
min = 0.5,
|
||
max = 2,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.06,
|
||
upgradeGrowthMax = 0.1
|
||
},
|
||
{
|
||
key = "attack_speed_pct",
|
||
min = 1.5,
|
||
max = 6,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.07,
|
||
upgradeGrowthMax = 0.11
|
||
},
|
||
{
|
||
key = "move_speed_pct",
|
||
min = 1,
|
||
max = 4,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.07,
|
||
upgradeGrowthMax = 0.11
|
||
},
|
||
{
|
||
key = "base_damage_pct",
|
||
min = 0.8,
|
||
max = 3.5,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.07,
|
||
upgradeGrowthMax = 0.11
|
||
},
|
||
{
|
||
key = "crit_mult",
|
||
min = 1,
|
||
max = 8,
|
||
decimals = 1,
|
||
upgradeGrowthMin = 0.07,
|
||
upgradeGrowthMax = 0.11
|
||
},
|
||
{key = "battle_level", min = 1, max = 1, decimals = 0}
|
||
}
|
||
local function roundTo(self, value, decimals)
|
||
if decimals == nil then
|
||
decimals = 0
|
||
end
|
||
if decimals <= 0 then
|
||
return math.floor(value + 0.5)
|
||
end
|
||
local mul = math.pow(10, decimals)
|
||
return math.floor(value * mul + 0.5) / mul
|
||
end
|
||
local function randomTemplateIndex(self)
|
||
return RandomInt(1, #ARSENAL_STAT_POOL) - 1
|
||
end
|
||
local function inferSlotFromItemName(self, itemName)
|
||
local n = string.lower(itemName)
|
||
if (string.find(n, "_weapon_", nil, true) or 0) - 1 >= 0 then
|
||
return "weapon"
|
||
end
|
||
if (string.find(n, "_armor_", nil, true) or 0) - 1 >= 0 then
|
||
return "armor"
|
||
end
|
||
if (string.find(n, "_helmet_", nil, true) or 0) - 1 >= 0 then
|
||
return "helmet"
|
||
end
|
||
if (string.find(n, "_boots_", nil, true) or 0) - 1 >= 0 then
|
||
return "boots"
|
||
end
|
||
if (string.find(n, "_necklace_", nil, true) or 0) - 1 >= 0 then
|
||
return "necklace"
|
||
end
|
||
return "ring"
|
||
end
|
||
local function getSlotWeightForStat(self, slot, key)
|
||
if slot == "ring" then
|
||
if key == "max_mana" or key == "magic_resist" or key == "spell_amp" then
|
||
return 2.5
|
||
end
|
||
if key == "mana_regen" then
|
||
return 2.2
|
||
end
|
||
if key == "max_health" then
|
||
return 1.4
|
||
end
|
||
if key == "all_stats_pct" then
|
||
return 1.55
|
||
end
|
||
if key == "all_stats" then
|
||
return 1.15
|
||
end
|
||
if key == "intellect_pct" or key == "strength_pct" or key == "agility_pct" or key == "bonus_intellect" or key == "bonus_strength" or key == "bonus_agility" then
|
||
return 1.3
|
||
end
|
||
elseif slot == "weapon" then
|
||
if key == "bonus_damage" or key == "attack_speed" or key == "base_damage_pct" then
|
||
return 2.6
|
||
end
|
||
if key == "attack_speed_pct" or key == "outgoing_damage_pct" or key == "crit_mult" then
|
||
return 1.8
|
||
end
|
||
if key == "strength_pct" or key == "agility_pct" or key == "bonus_strength" or key == "bonus_agility" then
|
||
return 1.5
|
||
end
|
||
if key == "all_stats_pct" then
|
||
return 1.25
|
||
end
|
||
elseif slot == "armor" then
|
||
if key == "health_regen" or key == "max_health" then
|
||
return 1.85
|
||
end
|
||
if key == "mana_regen" or key == "bonus_armor" then
|
||
return 1.55
|
||
end
|
||
if key == "all_stats_pct" then
|
||
return 1.55
|
||
end
|
||
if key == "all_stats" then
|
||
return 1.2
|
||
end
|
||
if key == "strength_pct" or key == "bonus_strength" then
|
||
return 1.6
|
||
end
|
||
elseif slot == "helmet" then
|
||
if key == "health_regen" then
|
||
return 1.65
|
||
end
|
||
if key == "all_stats_pct" then
|
||
return 1.72
|
||
end
|
||
if key == "all_stats" then
|
||
return 1.25
|
||
end
|
||
if key == "intellect_pct" or key == "bonus_intellect" then
|
||
return 1.8
|
||
end
|
||
elseif slot == "boots" then
|
||
if key == "all_stats_pct" then
|
||
return 1.38
|
||
end
|
||
if key == "agility_pct" or key == "bonus_agility" then
|
||
return 1.6
|
||
end
|
||
elseif slot == "necklace" then
|
||
if key == "mana_regen" or key == "spell_amp" then
|
||
return 1.75
|
||
end
|
||
if key == "all_stats_pct" then
|
||
return 1.62
|
||
end
|
||
if key == "all_stats" then
|
||
return 1.2
|
||
end
|
||
if key == "intellect_pct" or key == "bonus_intellect" then
|
||
return 1.5
|
||
end
|
||
end
|
||
return 1
|
||
end
|
||
local function randomTemplateIndexForSlot(self, slot)
|
||
local totalWeight = 0
|
||
for ____, t in ipairs(ARSENAL_STAT_POOL) do
|
||
totalWeight = totalWeight + getSlotWeightForStat(nil, slot, t.key)
|
||
end
|
||
if totalWeight <= 0 then
|
||
return randomTemplateIndex(nil)
|
||
end
|
||
local roll = RandomFloat(0, totalWeight)
|
||
local passed = 0
|
||
do
|
||
local i = 0
|
||
while i < #ARSENAL_STAT_POOL do
|
||
passed = passed + getSlotWeightForStat(nil, slot, ARSENAL_STAT_POOL[i + 1].key)
|
||
if roll <= passed then
|
||
return i
|
||
end
|
||
i = i + 1
|
||
end
|
||
end
|
||
return #ARSENAL_STAT_POOL - 1
|
||
end
|
||
local function findTemplateIndexByKey(self, key)
|
||
do
|
||
local i = 0
|
||
while i < #ARSENAL_STAT_POOL do
|
||
if ARSENAL_STAT_POOL[i + 1].key == key then
|
||
return i
|
||
end
|
||
i = i + 1
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
--- Реже, чем раньше: часть строк может взять тот же стат, что уже выпал (случайная из набранных).
|
||
-- Иначе — обычный взвешенный ролл по слоту.
|
||
local function randomTemplateIndexForSlotWithStackBias(self, slot, existingLines)
|
||
if #existingLines == 0 or RandomInt(1, 100) > ARSENAL_REPEAT_EXISTING_STAT_CHANCE_PCT then
|
||
return randomTemplateIndexForSlot(nil, slot)
|
||
end
|
||
local linePick = RandomInt(1, #existingLines) - 1
|
||
local key = existingLines[linePick + 1].key
|
||
local idx = findTemplateIndexByKey(nil, key)
|
||
if idx == nil then
|
||
return randomTemplateIndexForSlot(nil, slot)
|
||
end
|
||
return idx
|
||
end
|
||
local function rollStatBase(self, template, slotIndex, contractDropStatBias)
|
||
if contractDropStatBias == nil then
|
||
contractDropStatBias = 0
|
||
end
|
||
local roll01 = RandomFloat(0, 1)
|
||
local exponent = slotIndex < BASE_STAT_COUNT and 1.35 or 1.75
|
||
if contractDropStatBias > 0 then
|
||
exponent = exponent * (1 - 0.45 * contractDropStatBias)
|
||
end
|
||
local biased = math.pow(roll01, exponent)
|
||
local raw = template.min + (template.max - template.min) * biased
|
||
return roundTo(nil, raw, template.decimals or 0)
|
||
end
|
||
local function rollStatBaseInBand(self, template, fromRatio, toRatio)
|
||
local clampedFrom = math.max(
|
||
0,
|
||
math.min(1, fromRatio)
|
||
)
|
||
local clampedTo = math.max(
|
||
clampedFrom,
|
||
math.min(1, toRatio)
|
||
)
|
||
local ratio = RandomFloat(clampedFrom, clampedTo)
|
||
local raw = template.min + (template.max - template.min) * ratio
|
||
return roundTo(nil, raw, template.decimals or 0)
|
||
end
|
||
--- Случайный множитель роста строки по шаблону; без min/max в шаблоне — undefined (берётся PER_LEVEL_GROWTH).
|
||
local function rollUpgradeGrowthForTemplate(self, template)
|
||
local minG = template.upgradeGrowthMin
|
||
local maxG = template.upgradeGrowthMax
|
||
if minG == nil or maxG == nil then
|
||
return nil
|
||
end
|
||
if not __TS__NumberIsFinite(minG) or not __TS__NumberIsFinite(maxG) then
|
||
return nil
|
||
end
|
||
local lo = math.min(minG, maxG)
|
||
local hi = math.max(minG, maxG)
|
||
return roundTo(
|
||
nil,
|
||
RandomFloat(lo, hi),
|
||
4
|
||
)
|
||
end
|
||
local function clampRarityIndex(self, index)
|
||
if index < 0 then
|
||
return 0
|
||
end
|
||
if index >= #ARSENAL_RARITY_ORDER then
|
||
return #ARSENAL_RARITY_ORDER - 1
|
||
end
|
||
return index
|
||
end
|
||
local function getRarityIndex(self, quality)
|
||
do
|
||
local i = 0
|
||
while i < #ARSENAL_RARITY_ORDER do
|
||
if ARSENAL_RARITY_ORDER[i + 1] == quality then
|
||
return i
|
||
end
|
||
i = i + 1
|
||
end
|
||
end
|
||
return 0
|
||
end
|
||
local function elevateRarityBySteps(self, baseQuality, steps)
|
||
if steps <= 0 then
|
||
return baseQuality
|
||
end
|
||
local nextIndex = clampRarityIndex(
|
||
nil,
|
||
getRarityIndex(nil, baseQuality) + steps
|
||
)
|
||
return ARSENAL_RARITY_ORDER[nextIndex + 1]
|
||
end
|
||
local function capStatQualityByItemQuality(self, statQuality, itemQuality)
|
||
local statIndex = getRarityIndex(nil, statQuality)
|
||
local itemIndex = getRarityIndex(nil, itemQuality)
|
||
return ARSENAL_RARITY_ORDER[math.min(statIndex, itemIndex) + 1]
|
||
end
|
||
local function getTemplateByKey(self, key)
|
||
for ____, t in ipairs(ARSENAL_STAT_POOL) do
|
||
if t.key == key then
|
||
return t
|
||
end
|
||
end
|
||
return nil
|
||
end
|
||
local function getStatQualityByBaseValue(self, stat)
|
||
local tpl = getTemplateByKey(nil, stat.key)
|
||
if not tpl then
|
||
return "common"
|
||
end
|
||
local span = tpl.max - tpl.min
|
||
if span <= 0 then
|
||
return "common"
|
||
end
|
||
local ratio = (stat.base - tpl.min) / span
|
||
local clamped = math.max(
|
||
0,
|
||
math.min(1, ratio)
|
||
)
|
||
local rawTier = math.floor(clamped * #ARSENAL_RARITY_ORDER)
|
||
return ARSENAL_RARITY_ORDER[clampRarityIndex(nil, rawTier) + 1]
|
||
end
|
||
local function getEffectiveStatQuality(self, item, statIndex, baseQuality)
|
||
if item.quality == "mythic" and statIndex == HIDDEN_FINAL_STAT_INDEX and math.floor(item.upgradeLevel) >= MAX_UPGRADE_LEVEL then
|
||
return ASCENDED_STAT_QUALITY
|
||
end
|
||
return capStatQualityByItemQuality(nil, baseQuality, item.quality)
|
||
end
|
||
local function getBaseAfterCapApproach(self, stat, extraMaxRaritySteps)
|
||
if extraMaxRaritySteps <= 0 then
|
||
return stat.base
|
||
end
|
||
local tpl = getTemplateByKey(nil, stat.key)
|
||
if not tpl or tpl.max <= stat.base then
|
||
return stat.base
|
||
end
|
||
local approachFactor = 1 - math.pow(1 - MAX_RARITY_CAP_APPROACH_PER_STEP, extraMaxRaritySteps)
|
||
local shifted = stat.base + (tpl.max - stat.base) * approachFactor
|
||
return roundTo(nil, shifted, tpl.decimals or 0)
|
||
end
|
||
local function getActiveAdditionalCount(self, itemQuality)
|
||
return ADDITIONAL_SLOTS_BY_ITEM_QUALITY[itemQuality] or 0
|
||
end
|
||
local function getAdditionalUpgradeTargetIndex(self, item, levelStep, activeAdditional)
|
||
if levelStep <= 0 then
|
||
return nil
|
||
end
|
||
if levelStep == HIDDEN_FINAL_UNLOCK_LEVEL then
|
||
return HIDDEN_FINAL_STAT_INDEX
|
||
end
|
||
local poolSize = math.min(ADDITIONAL_STAT_COUNT, activeAdditional) + 1
|
||
if poolSize <= 0 then
|
||
return nil
|
||
end
|
||
local seed = levelStep * 97 + poolSize * 31
|
||
do
|
||
local offset = 0
|
||
while offset < poolSize do
|
||
local idx = offset == poolSize - 1 and HIDDEN_FINAL_STAT_INDEX or BASE_STAT_COUNT + offset
|
||
local line = item.stats[idx + 1]
|
||
local baseInt = math.floor((line and line.base or 0) * 100 + 0.5)
|
||
seed = seed + ((idx + 1) * 17 + baseInt)
|
||
offset = offset + 1
|
||
end
|
||
end
|
||
local offset = math.floor(math.abs(seed)) % poolSize
|
||
return offset == poolSize - 1 and HIDDEN_FINAL_STAT_INDEX or BASE_STAT_COUNT + offset
|
||
end
|
||
function ____exports.contractRewardMultToDropStatBias01(self, rewardMultiplier)
|
||
local minM = 3
|
||
local maxM = 10
|
||
local span = maxM - minM
|
||
if span <= 0 then
|
||
return 0
|
||
end
|
||
return math.max(
|
||
0,
|
||
math.min(1, (rewardMultiplier - minM) / span)
|
||
)
|
||
end
|
||
function ____exports.createInitialItemState(self, itemName, quality, context)
|
||
local bias = math.max(
|
||
0,
|
||
math.min(1, context and context.contractDropStatBias or 0)
|
||
)
|
||
local mythicBandLift = bias * 0.035
|
||
local slot = inferSlotFromItemName(nil, itemName)
|
||
local stats = {}
|
||
local totalStatLines = BASE_STAT_COUNT + ADDITIONAL_STAT_COUNT + 1
|
||
local mythicBestLines = 0
|
||
if quality == "mythic" then
|
||
mythicBestLines = 1
|
||
if RandomInt(1, 100) <= MYTHIC_BEST_LINE_CHANCE_PCT then
|
||
mythicBestLines = 2
|
||
end
|
||
end
|
||
do
|
||
local i = 0
|
||
while i < BASE_STAT_COUNT + ADDITIONAL_STAT_COUNT + 1 do
|
||
local templateIndex = randomTemplateIndexForSlotWithStackBias(nil, slot, stats)
|
||
local template = ARSENAL_STAT_POOL[templateIndex + 1]
|
||
local rolledBase
|
||
if quality == "mythic" then
|
||
local guaranteedBestCut = totalStatLines - mythicBestLines
|
||
if i >= guaranteedBestCut then
|
||
rolledBase = rollStatBaseInBand(
|
||
nil,
|
||
template,
|
||
0.8,
|
||
math.min(1, 0.97 + mythicBandLift)
|
||
)
|
||
elseif RandomInt(1, 100) <= MYTHIC_WEAK_LINE_CHANCE_PCT then
|
||
rolledBase = rollStatBaseInBand(
|
||
nil,
|
||
template,
|
||
0.05,
|
||
math.min(1, 0.59 + mythicBandLift * 0.5)
|
||
)
|
||
else
|
||
rolledBase = rollStatBaseInBand(
|
||
nil,
|
||
template,
|
||
0.6,
|
||
math.min(1, 0.79 + mythicBandLift)
|
||
)
|
||
end
|
||
else
|
||
rolledBase = rollStatBase(nil, template, i, bias)
|
||
end
|
||
local rolledGrowth = rollUpgradeGrowthForTemplate(nil, template)
|
||
local row = {key = template.key, base = rolledBase}
|
||
if rolledGrowth ~= nil then
|
||
row.upgradeGrowth = rolledGrowth
|
||
end
|
||
stats[#stats + 1] = row
|
||
i = i + 1
|
||
end
|
||
end
|
||
return {itemName = itemName, quality = quality, upgradeLevel = 0, stats = stats}
|
||
end
|
||
function ____exports.createArsenalItemInstance(self, instanceId, serial, itemName, quality, rollContext)
|
||
local core = ____exports.createInitialItemState(nil, itemName, quality, rollContext)
|
||
return {
|
||
instanceId = instanceId,
|
||
serial = serial,
|
||
itemName = core.itemName,
|
||
quality = core.quality,
|
||
upgradeLevel = core.upgradeLevel,
|
||
stats = core.stats
|
||
}
|
||
end
|
||
function ____exports.getEffectiveItemStats(self, item)
|
||
local result = {}
|
||
local level = math.max(
|
||
0,
|
||
math.min(
|
||
MAX_UPGRADE_LEVEL,
|
||
math.floor(item.upgradeLevel)
|
||
)
|
||
)
|
||
local activeAdditional = getActiveAdditionalCount(nil, item.quality)
|
||
local lineLevelBonus = {}
|
||
do
|
||
local i = 0
|
||
while i < #item.stats do
|
||
lineLevelBonus[i + 1] = 0
|
||
i = i + 1
|
||
end
|
||
end
|
||
do
|
||
local i = 0
|
||
while i < BASE_STAT_COUNT and i < #item.stats do
|
||
lineLevelBonus[i + 1] = level
|
||
i = i + 1
|
||
end
|
||
end
|
||
do
|
||
local step = 1
|
||
while step <= level do
|
||
local target = getAdditionalUpgradeTargetIndex(nil, item, step, activeAdditional)
|
||
if target ~= nil and target < #item.stats then
|
||
lineLevelBonus[target + 1] = (lineLevelBonus[target + 1] or 0) + 1
|
||
end
|
||
step = step + 1
|
||
end
|
||
end
|
||
do
|
||
local i = 0
|
||
while i < #item.stats do
|
||
do
|
||
if i >= BASE_STAT_COUNT and i < BASE_STAT_COUNT + ADDITIONAL_STAT_COUNT then
|
||
local localIndex = i - BASE_STAT_COUNT
|
||
if localIndex >= activeAdditional then
|
||
goto __continue106
|
||
end
|
||
end
|
||
if i == HIDDEN_FINAL_STAT_INDEX and level < HIDDEN_FINAL_UNLOCK_LEVEL then
|
||
goto __continue106
|
||
end
|
||
local rolledQuality = getStatQualityByBaseValue(nil, item.stats[i + 1])
|
||
local rarityBonusSteps = lineLevelBonus[i + 1] or 0
|
||
local upgradedQuality = elevateRarityBySteps(nil, rolledQuality, rarityBonusSteps)
|
||
local maxRarityStepsSpent = math.max(
|
||
0,
|
||
rarityBonusSteps - (MAX_RARITY_INDEX - getRarityIndex(nil, rolledQuality))
|
||
)
|
||
local statQuality = getEffectiveStatQuality(nil, item, i, upgradedQuality)
|
||
local effectiveBase = getBaseAfterCapApproach(nil, item.stats[i + 1], maxRarityStepsSpent)
|
||
local qualityMult = ARSENAL_RARITY_MULTIPLIER[statQuality]
|
||
local lineBonus = lineLevelBonus[i + 1] or 0
|
||
local statKey = item.stats[i + 1].key
|
||
if statKey == "battle_level" then
|
||
result[#result + 1] = {
|
||
key = statKey,
|
||
value = math.max(
|
||
0,
|
||
math.floor(item.stats[i + 1].base) + lineBonus
|
||
)
|
||
}
|
||
goto __continue106
|
||
end
|
||
local tpl = getTemplateByKey(nil, statKey)
|
||
local outDecimals = tpl and tpl.decimals or 0
|
||
local perLevelGrowth = item.stats[i + 1].upgradeGrowth or PER_LEVEL_GROWTH
|
||
local growthMult = 1 + perLevelGrowth * lineBonus
|
||
result[#result + 1] = {
|
||
key = statKey,
|
||
value = roundTo(nil, effectiveBase * qualityMult * growthMult, outDecimals)
|
||
}
|
||
end
|
||
::__continue106::
|
||
i = i + 1
|
||
end
|
||
end
|
||
return result
|
||
end
|
||
function ____exports.getItemVisibleStatsCount(self, item)
|
||
local activeAdditional = getActiveAdditionalCount(nil, item.quality)
|
||
local hasHidden = math.floor(item.upgradeLevel) >= HIDDEN_FINAL_UNLOCK_LEVEL and 1 or 0
|
||
return BASE_STAT_COUNT + activeAdditional + hasHidden
|
||
end
|
||
return ____exports
|