local ____lualib = require("lualib_bundle") local __TS__Number = ____lualib.__TS__Number local __TS__NumberIsFinite = ____lualib.__TS__NumberIsFinite local __TS__Class = ____lualib.__TS__Class local __TS__New = ____lualib.__TS__New local __TS__StringReplace = ____lualib.__TS__StringReplace local __TS__ObjectEntries = ____lualib.__TS__ObjectEntries local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray local __TS__StringTrim = ____lualib.__TS__StringTrim local __TS__Spread = ____lualib.__TS__Spread local __TS__StringIncludes = ____lualib.__TS__StringIncludes local __TS__StringSplit = ____lualib.__TS__StringSplit local __TS__Delete = ____lualib.__TS__Delete local __TS__ArrayFindIndex = ____lualib.__TS__ArrayFindIndex local __TS__ArrayFind = ____lualib.__TS__ArrayFind local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes local Set = ____lualib.Set local __TS__Iterator = ____lualib.__TS__Iterator local __TS__ParseInt = ____lualib.__TS__ParseInt local __TS__NumberIsNaN = ____lualib.__TS__NumberIsNaN local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys local __TS__ArrayFilter = ____lualib.__TS__ArrayFilter local __TS__ArrayIndexOf = ____lualib.__TS__ArrayIndexOf local __TS__ArraySplice = ____lualib.__TS__ArraySplice local __TS__SparseArrayNew = ____lualib.__TS__SparseArrayNew local __TS__SparseArrayPush = ____lualib.__TS__SparseArrayPush local __TS__SparseArraySpread = ____lualib.__TS__SparseArraySpread local __TS__ObjectAssign = ____lualib.__TS__ObjectAssign local __TS__ArraySort = ____lualib.__TS__ArraySort local __TS__ArrayMap = ____lualib.__TS__ArrayMap local __TS__ArraySlice = ____lualib.__TS__ArraySlice local __TS__ObjectValues = ____lualib.__TS__ObjectValues local __TS__ArrayFrom = ____lualib.__TS__ArrayFrom local __TS__TypeOf = ____lualib.__TS__TypeOf local ____exports = {} local ____crystal_currency = require("crystal_currency") local CrystalCurrency = ____crystal_currency.CrystalCurrency local ____modifier_stats_multiplier = require("modifiers.modifier_stats_multiplier") local invalidateStatsMultiplierSumCache = ____modifier_stats_multiplier.invalidateStatsMultiplierSumCache local ____server_config = require("server_config") local SERVER_CONFIG = ____server_config.SERVER_CONFIG local ____api_helper = require("api_helper") local setApiHeaders = ____api_helper.setApiHeaders local ____card_catalog = require("card_catalog") local DEFAULT_DECK_CARD_IDS = ____card_catalog.DEFAULT_DECK_CARD_IDS local DECK_BUILDER_SLOT_CAPACITY = ____card_catalog.DECK_BUILDER_SLOT_CAPACITY local getDeckBuilderUnlockRequiredMessage = ____card_catalog.getDeckBuilderUnlockRequiredMessage local ____real_lobby_player = require("utils.real_lobby_player") local isRealLobbyPlayer = ____real_lobby_player.isRealLobbyPlayer local ____card_slag = require("cards.card_slag") local isEmptySelectionCardId = ____card_slag.isEmptySelectionCardId local isSlagCardId = ____card_slag.isSlagCardId local SLAG_CARD_ID = ____card_slag.SLAG_CARD_ID local ____modifier_card_cursed = require("cards.modifier_card_cursed") local addCursedStack = ____modifier_card_cursed.addCursedStack local ____modifier_card_greed = require("cards.modifier_card_greed") local isGreedPoolCardId = ____modifier_card_greed.isGreedPoolCardId local registerHiddenGreedCard = ____modifier_card_greed.registerHiddenGreedCard local updateGreedForHero = ____modifier_card_greed.updateGreedForHero local DEFAULT_DECK_CARD_WEIGHT = 1 local CARD_UPGRADE_MAX_LEVEL = 3 local function isMirrorProtectedCardId(self, cardId) do local function ____catch() return true, false end local ____try, ____hasReturned, ____returnValue = pcall(function() local card80 = require("cards.examples.card_80") local ____opt_0 = card80.isFrostmourneMirrorProtectedCardId return true, (____opt_0 and ____opt_0( card80, math.floor(__TS__Number(cardId)) )) == true end) if not ____try then ____hasReturned, ____returnValue = ____catch() end if ____hasReturned then return ____returnValue end end end --- Качество карт ____exports.CardQuality = CardQuality or ({}) ____exports.CardQuality.COMMON = 1 ____exports.CardQuality[____exports.CardQuality.COMMON] = "COMMON" ____exports.CardQuality.RARE = 2 ____exports.CardQuality[____exports.CardQuality.RARE] = "RARE" ____exports.CardQuality.EPIC = 3 ____exports.CardQuality[____exports.CardQuality.EPIC] = "EPIC" ____exports.CardQuality.LEGENDARY = 4 ____exports.CardQuality[____exports.CardQuality.LEGENDARY] = "LEGENDARY" ____exports.CardQuality.MYTHIC = 5 ____exports.CardQuality[____exports.CardQuality.MYTHIC] = "MYTHIC" --- Карта 79 «Эхо выбора»: нельзя дублировать пустые, Фростморн/осколки и мифические карты. local function isCard79RepeatBlockedCardId(self, cardId) local id = math.floor(__TS__Number(cardId)) if not __TS__NumberIsFinite(id) or id <= 0 then return true end if isEmptySelectionCardId(nil, id) or isMirrorProtectedCardId(nil, id) then return true end local cardData = ____exports.CardSystem.cardData[id] return (cardData and cardData.quality) == ____exports.CardQuality.MYTHIC end --- Базовый класс для карт ____exports.CardBase = __TS__Class() local CardBase = ____exports.CardBase CardBase.name = "CardBase" CardBase.____file_path = "scripts/vscripts/cards/CardSystem.lua" function CardBase.prototype.____constructor(self, hero, cardKey) self.modifiers = nil self.id = hero:GetPlayerID() self.cardKey = cardKey self:OnCreated() self:ModifierRefresh() end function CardBase.prototype.OnCreated(self) end function CardBase.prototype.OnDestroy(self) if self.modifiers and not self.modifiers:IsNull() then self.modifiers:Destroy() end end function CardBase.prototype.OnTransfer(self) end function CardBase.prototype.GetHero(self) local player = PlayerResource:GetPlayer(self.id) return player:GetAssignedHero() end function CardBase.prototype.GetPlayer(self) return PlayerResource:GetPlayer(self.id) end function CardBase.prototype.GetCardLevelSnapshot(self) local player = self:GetPlayer() local cardId = __TS__Number(self.cardKey) if not player or not __TS__NumberIsFinite(cardId) or cardId <= 0 then return 1 end local ____opt_4 = player.cardSystem local level = ____opt_4 and ____opt_4:GetCardLevel(math.floor(cardId)) return __TS__NumberIsFinite(level) and math.max( 1, math.floor(level) ) or 1 end function CardBase.prototype.ModifierRefresh(self) local hero = self:GetHero() local modName = self:GetModifierName() local reusedExistingModifier = false if self.modifiers and not self.modifiers:IsNull() then self.modifiers:Destroy() self.modifiers = nil end if modName then if IsServer() then local existing = hero:FindModifierByName(modName) if existing and not existing:IsNull() then local currentStacks = math.max( 0, math.floor(existing:GetStackCount() or 0) ) existing:SetStackCount(currentStacks + 1) existing:ForceRefresh() hero:CalculateStatBonus(true) self.modifiers = nil reusedExistingModifier = true end end if not reusedExistingModifier then self.modifiers = hero:AddNewModifier( hero, getModifierSourceAbility(nil, hero), modName, {card_level = self:GetCardLevelSnapshot()} ) end end if IsServer() then local cardId = __TS__Number(self.cardKey) if isGreedPoolCardId(nil, cardId) then local player = self:GetPlayer() updateGreedForHero(nil, hero, player and player.cardSystem) end end end function CardBase.prototype.IsHidden(self) return false end --- Регистрация класса карты function ____exports.RegisterCard(self, target) local className = target.name ____exports.CardSystem.cardTypes[className] = target end --- Парсинг значений в описании карты function ____exports.ParseCardDescription(self, description, values) if not values then return description end local parsedDescription = description for ____, ____value in ipairs(__TS__ObjectEntries(values)) do local key = ____value[1] local value = ____value[2] local pattern = __TS__New(RegExp, ("%" .. key) .. "%", "g") parsedDescription = __TS__StringReplace( parsedDescription, pattern, tostring(value) ) end return parsedDescription end --- Основная система карт ____exports.CardSystem = __TS__Class() local CardSystem = ____exports.CardSystem CardSystem.name = "CardSystem" CardSystem.____file_path = "scripts/vscripts/cards/CardSystem.lua" function CardSystem.prototype.____constructor(self, playerId) self.playerItems = {} self.items = {} self.itemsId = {} self.decks = {} self.activeDeckIndex = 0 self.cardPieces = {} self.newCardPieces = {} self.rerollCost = 0 self.cardsTaken = 0 self.cardSelectionQueue = {} self.isShowingCardSelection = false self.currentSelectionSource = "unknown" self.currentSelectionSourceChain = {} self.currentSelectionToken = 0 self.poolEntriesById = {} self.poolNextEntrySeq = 1 self.rerollQualityFilter = nil self.guaranteedQualityRerolls = {} self.card49FreeRerollCharges = 0 self.card6FreeRerollCharges = 0 self.card88FreeRerollCharges = 0 self.card6FreeRerollLevelGranted = false self.card51MorningGoldEarned = 0 self.repeatNextSelectedCardOnce = false self.cardLevelsLoaded = false self.cardLevelsWaitTimerStarted = false self.playerId = playerId self.pool = pool(nil) self:Init() Timers:CreateTimer( 1, function() self:SyncPool() return nil end ) end function CardSystem.getCardDeckSlots(self, cardId) local cardData = ____exports.CardSystem.cardData[cardId] local configured = __TS__Number(cardData and cardData.deck_slots) if __TS__NumberIsFinite(configured) and configured > 0 then return math.floor(configured) end return 1 end function CardSystem.getDeckUsedSlots(self, cards) local total = 0 for ____, id in ipairs(cards) do total = total + ____exports.CardSystem:getCardDeckSlots(id) end return total end function CardSystem.canAddCardToDeckSlots(self, cards, cardId, capacity) return ____exports.CardSystem:getDeckUsedSlots(cards) + ____exports.CardSystem:getCardDeckSlots(cardId) <= capacity end function CardSystem.prototype.ForceDeckBuilderSync(self) self:LoadDecks() self:SyncDecks() self:SyncCardData() self:SyncPool() end function CardSystem.prototype.Init(self) local restoredFromRuntimeState = self:RestoreRuntimeStateIfPresent() if not restoredFromRuntimeState then Timers:CreateTimer( 1, function() self:LoadDecks() self:loadCardLevelsFromServer() self:InitCardPool() end ) else self:loadCardLevelsFromServer() self:SyncDecks() self:SyncPool() self:SyncActiveCards() self:SyncRerollCost() end self:SyncDecks() self:SyncCardData() self:SyncRerollCost() CustomGameEventManager:RegisterListener( "card_selected", function(_, event) if self.playerId ~= event.PlayerID then return end self:OnChooseCard(event.PlayerID, event.index, event.selection_token) end ) CustomGameEventManager:RegisterListener( "card_reroll", function(_, event) if self.playerId ~= event.PlayerID then return end self:RerollCards(event.PlayerID) end ) CustomGameEventManager:RegisterListener( "deck_new", function(_, event) if self.playerId ~= event.PlayerID then return end self:CreateNewDeck(event.name) end ) CustomGameEventManager:RegisterListener( "deck_save", function(_, event) if self.playerId ~= event.PlayerID then return end if event.index == nil or event.data == nil and event.cards == nil then return end local cardsData = event.cards or event.data if not cardsData then return end self:SaveDeck(event.index, cardsData, event.name) end ) CustomGameEventManager:RegisterListener( "deck_delete", function(_, event) if self.playerId ~= event.PlayerID then return end self:DeleteDeck(event.index) end ) CustomGameEventManager:RegisterListener( "deck_activate", function(_, event) if self.playerId ~= event.PlayerID then return end self:ActivateDeck(event.index) end ) CustomGameEventManager:RegisterListener( "reset_card_pool", function(_, event) if self.playerId ~= event.PlayerID then return end self:ResetCardPool() end ) CustomGameEventManager:RegisterListener( "hide_card_selection", function(_, event) if self.playerId ~= event.PlayerID then return end self:HideCardSelection() end ) CustomGameEventManager:RegisterListener( "init_card_system", function(_, event) if self.playerId ~= event.PlayerID then return end self:SyncDecks() self:SyncPool() end ) CustomGameEventManager:RegisterListener( "request_pool_sync", function(_, event) if self.playerId ~= event.PlayerID then return end self:SyncPool() end ) CustomGameEventManager:RegisterListener( "request_available_cards", function(_, event) if self.playerId ~= event.PlayerID then return end self:LoadDecks() self:loadCardLevelsFromServer() self:SyncDecks() self:SyncCardData() self:SyncPool() end ) CustomGameEventManager:RegisterListener( "card_craft", function(_, event) if self.playerId ~= event.PlayerID then return end self:CraftOrUpgradeCard(event.cardId, event.allowDebris) end ) ListenToGameEvent( "game_rules_state_change", function() local state = GameRules:State_Get() if state == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then self:InitCardPool() end end, nil ) end function CardSystem.prototype.RestoreRuntimeStateIfPresent(self) local key = tostring(self.playerId) local snapshot = ____exports.CardSystem.runtimeStateByPlayerId[key] if not snapshot then return false end self.decks = snapshot.decks or ({}) self.activeDeckIndex = snapshot.activeDeckIndex or 0 self.itemsId = __TS__ArrayIsArray(snapshot.itemsId) and ({unpack(snapshot.itemsId)}) or ({}) self.poolEntriesById = {} self.poolNextEntrySeq = math.max( 1, math.floor(__TS__Number(snapshot.poolNextEntrySeq or 1)) ) self.cardsTaken = math.max( 0, math.floor(__TS__Number(snapshot.cardsTaken) or 0) ) self.rerollCost = math.max( 0, math.floor(__TS__Number(snapshot.rerollCost) or 0) ) self.card49FreeRerollCharges = math.max( 0, math.floor(__TS__Number(snapshot.card49FreeRerollCharges) or 0) ) self.card6FreeRerollCharges = math.max( 0, math.floor(__TS__Number(snapshot.card6FreeRerollCharges) or 0) ) self.card88FreeRerollCharges = math.max( 0, math.floor(__TS__Number(snapshot.card88FreeRerollCharges) or 0) ) self.card6FreeRerollLevelGranted = snapshot.card6FreeRerollLevelGranted == true self.card51MorningGoldEarned = math.max( 0, math.floor(__TS__Number(snapshot.card51MorningGoldEarned) or 0) ) self.pool:clear() if __TS__ArrayIsArray(snapshot.poolData) then for ____, item in ipairs(snapshot.poolData) do do local entryId = __TS__StringTrim(tostring(item.entry_id or "")) local id = math.floor(__TS__Number(item.index)) local weight = math.floor(__TS__Number(item.weight)) if #entryId <= 0 or not __TS__NumberIsFinite(id) or id <= 0 or not __TS__NumberIsFinite(weight) or weight <= 0 then goto __continue75 end self.poolEntriesById[entryId] = { entryId = entryId, cardId = id, weight = weight, sourceChain = __TS__ArrayIsArray(item.source_chain) and ({__TS__Spread(item.source_chain)}) or ({}) } end ::__continue75:: end end self:RebuildPoolFromEntries() return true end function CardSystem.prototype.CaptureRuntimeState(self) local poolData = {} for entryId in pairs(self.poolEntriesById) do do local entry = self.poolEntriesById[entryId] if not entry or entry.weight <= 0 then goto __continue79 end poolData[#poolData + 1] = { entry_id = entry.entryId, index = entry.cardId, weight = math.floor(entry.weight), source_chain = entry.sourceChain and #entry.sourceChain > 0 and ({unpack(entry.sourceChain)}) or nil } end ::__continue79:: end local state = { poolData = poolData, poolNextEntrySeq = self.poolNextEntrySeq, itemsId = {unpack(self.itemsId)}, cardsTaken = self.cardsTaken, rerollCost = self.rerollCost, card49FreeRerollCharges = self.card49FreeRerollCharges, card6FreeRerollCharges = self.card6FreeRerollCharges, card88FreeRerollCharges = self.card88FreeRerollCharges, card6FreeRerollLevelGranted = self.card6FreeRerollLevelGranted, card51MorningGoldEarned = self.card51MorningGoldEarned, decks = self.decks, activeDeckIndex = self.activeDeckIndex } ____exports.CardSystem.runtimeStateByPlayerId[tostring(self.playerId)] = state end function CardSystem.prototype.NormalizeSourceToken(self, sourceRaw) local source = __TS__StringTrim(tostring(sourceRaw or "unknown")) return #source > 0 and source or "unknown" end function CardSystem.prototype.IsNonGameplaySourceToken(self, tokenRaw) local token = string.lower(self:NormalizeSourceToken(tokenRaw)) return token == "unknown" or token == "deck_initial_pool" or token == "manual_pool_add" or token == "store_purchase_card" or token == "duplicate_current_pool" or token == "duplicate_unselected_current_options" or token == "threshold_empty_card_bonus" end function CardSystem.prototype.BuildSourceChain(self, sourceRaw, baseChain) local chain = {} local function pushToken(____, tokenRaw) local token = self:NormalizeSourceToken(tokenRaw) if self:IsNonGameplaySourceToken(token) then return end local prev = #chain > 0 and chain[#chain] or nil if prev ~= token then chain[#chain + 1] = token end end if baseChain and #baseChain > 0 then for ____, token in ipairs(baseChain) do pushToken(nil, token) end end local normalizedSource = self:NormalizeSourceToken(sourceRaw) if __TS__StringIncludes(normalizedSource, "->") then local tokens = __TS__StringSplit(normalizedSource, "->") for ____, token in ipairs(tokens) do pushToken(nil, token) end else pushToken(nil, normalizedSource) end return chain end function CardSystem.prototype.SerializeSourceChain(self, chain) if not chain or #chain <= 0 then return "" end return table.concat(chain, " -> ") end function CardSystem.prototype.SetCurrentSelectionSource(self, sourceRaw, sourceChain) local source = self:NormalizeSourceToken(sourceRaw) self.currentSelectionSource = source self.currentSelectionSourceChain = sourceChain and #sourceChain > 0 and ({unpack(sourceChain)}) or self:BuildSourceChain(source) self.currentSelectionToken = self.currentSelectionToken + 1 end function CardSystem.prototype.BuildPoolEntrySourceChain(self, sourceRaw, sourceChain) local chainBase = sourceChain and #sourceChain > 0 and sourceChain or self.effectSourceChainContext return self:BuildSourceChain(sourceRaw, chainBase) end function CardSystem.prototype.CreatePoolEntry(self, cardId, weight, sourceRaw, sourceChain) local normalizedCardId = math.floor(__TS__Number(cardId)) local normalizedWeight = math.floor(__TS__Number(weight)) if not __TS__NumberIsFinite(normalizedCardId) or normalizedCardId <= 0 or not __TS__NumberIsFinite(normalizedWeight) or normalizedWeight <= 0 then return nil end local ____self_10, ____poolNextEntrySeq_11 = self, "poolNextEntrySeq" local ____self_poolNextEntrySeq_12 = ____self_10[____poolNextEntrySeq_11] ____self_10[____poolNextEntrySeq_11] = ____self_poolNextEntrySeq_12 + 1 local entryId = "e" .. tostring(____self_poolNextEntrySeq_12) local chain = self:BuildPoolEntrySourceChain(sourceRaw, sourceChain) self.poolEntriesById[entryId] = {entryId = entryId, cardId = normalizedCardId, weight = normalizedWeight, sourceChain = chain} return entryId end function CardSystem.prototype.RebuildPoolFromEntries(self) self.pool:clear() for entryId in pairs(self.poolEntriesById) do do local entry = self.poolEntriesById[entryId] if not entry or entry.weight <= 0 then goto __continue102 end self.pool:add(entry.cardId, entry.weight) end ::__continue102:: end end function CardSystem.prototype.GetPoolEntrySourceChain(self, entryId) if not entryId then return nil end local entry = self.poolEntriesById[entryId] if not entry or not entry.sourceChain or #entry.sourceChain <= 0 then return nil end return {unpack(entry.sourceChain)} end function CardSystem.prototype.PickPoolEntryIdForCardId(self, cardId) local candidates = {} local total = 0 for entryId in pairs(self.poolEntriesById) do do local entry = self.poolEntriesById[entryId] if not entry or entry.cardId ~= cardId or entry.weight <= 0 then goto __continue109 end candidates[#candidates + 1] = entry total = total + entry.weight end ::__continue109:: end if #candidates <= 0 or total <= 0 then return nil end local roll = RandomInt(1, total) local cursor = 0 for ____, entry in ipairs(candidates) do cursor = cursor + entry.weight if roll <= cursor then return entry.entryId end end local ____opt_13 = candidates[1] return ____opt_13 and ____opt_13.entryId end function CardSystem.prototype.SelectEntryIdsForCards(self, cardIds) local remainingByEntryId = {} for entryId in pairs(self.poolEntriesById) do local entry = self.poolEntriesById[entryId] if entry and entry.weight > 0 then remainingByEntryId[entryId] = entry.weight end end local selected = {} for ____, cardId in ipairs(cardIds) do do if not __TS__NumberIsFinite(cardId) or cardId <= 0 or cardId == 404 then selected[#selected + 1] = nil goto __continue120 end local candidates = {} local total = 0 for entryId in pairs(remainingByEntryId) do do local left = remainingByEntryId[entryId] local entry = self.poolEntriesById[entryId] if not entry or entry.cardId ~= cardId or left <= 0 then goto __continue122 end candidates[#candidates + 1] = {entryId = entryId, weight = left} total = total + left end ::__continue122:: end if #candidates <= 0 or total <= 0 then selected[#selected + 1] = nil goto __continue120 end local roll = RandomInt(1, total) local cursor = 0 local picked = candidates[1].entryId for ____, candidate in ipairs(candidates) do cursor = cursor + candidate.weight if roll <= cursor then picked = candidate.entryId break end end selected[#selected + 1] = picked remainingByEntryId[picked] = math.max(0, (remainingByEntryId[picked] or 0) - 1) end ::__continue120:: end return selected end function CardSystem.prototype.ConsumePoolEntryWeight(self, entryId, amount) if amount == nil then amount = 1 end if not entryId then return end local entry = self.poolEntriesById[entryId] if not entry then return end entry.weight = math.max( 0, entry.weight - math.max( 1, math.floor(amount) ) ) if entry.weight <= 0 then __TS__Delete(self.poolEntriesById, entryId) end end function CardSystem.prototype.ReinitCardPool(self) self:InitCardPool() end function CardSystem.prototype.GetCardsTaken(self) return self.cardsTaken end function CardSystem.prototype.GetActiveCardCount(self) return #self.itemsId end function CardSystem.prototype.GetActiveCardCountExcludingInherent(self) local n = 0 for ____, id in ipairs(self.itemsId) do do local cardData = ____exports.CardSystem.cardData[id] if cardData and cardData.inherent == true then goto __continue138 end n = n + 1 end ::__continue138:: end return n end function CardSystem.prototype.GetActiveCardCopies(self, cardId) local fromItems = 0 for ____, id in ipairs(self.itemsId) do if id == cardId then fromItems = fromItems + 1 end end local fromModifiers = 0 local ____opt_15 = self:GetPlayer() local hero = ____opt_15 and ____opt_15:GetAssignedHero() if hero and IsValidEntity(hero) and hero:IsRealHero() then local modName = "modifier_card_" .. tostring(cardId) do local i = 0 while i < hero:GetModifierCount() do if hero:GetModifierNameByIndex(i) == modName then fromModifiers = fromModifiers + 1 end i = i + 1 end end end return math.max(fromItems, fromModifiers) end function CardSystem.prototype.GetCardLevel(self, cardId) local key = tostring(cardId) local ____opt_17 = self.cardPieces[key] local levelRaw = __TS__Number(____opt_17 and ____opt_17.level or 1) return math.max( 1, math.min( CARD_UPGRADE_MAX_LEVEL, math.floor(levelRaw) ) ) end function CardSystem.prototype.ResetCardPool(self) self.cardsTaken = 0 self:InitCardPool() end function CardSystem.prototype.InitCardPool(self) self.pool:clear() self.poolEntriesById = {} self.poolNextEntrySeq = 1 self.items = {} self.itemsId = {} self.rerollCost = 0 self.cardsTaken = 0 local activeDeckKey = tostring(self.activeDeckIndex) local activeDeck = self.decks[activeDeckKey] local normalizedCards = self:normalizeDeckCards(activeDeck) if #normalizedCards > 0 then self.decks[activeDeckKey] = { name = self:resolveDeckName(activeDeck, self.activeDeckIndex), cards = {unpack(normalizedCards)} } self:SyncDecks() end do local i = 0 while i < #normalizedCards do local cardId = normalizedCards[i + 1] local cardData = ____exports.CardSystem.cardData[cardId] if cardData and not cardData.disabled then if cardData.inherent then self:AddCard( tostring(cardId), nil, {skipCurseStack = true} ) else self:CreatePoolEntry(cardId, 1, "deck_initial_pool") end end i = i + 1 end end self:RebuildPoolFromEntries() self:SyncPool() self:SyncActiveCards() self:SyncRerollCost() Timers:CreateTimer( 0.5, function() self:SyncPool() return nil end ) end function CardSystem.prototype.HideCardSelection(self) self.playerItems = {} self:SyncPlayerItems() self:SyncRerollCost() self.isShowingCardSelection = false self:SyncSelectionQueue() self:SyncSelectionQueue() self.cardSelectionQueue = {} self:SyncSelectionQueue() end function CardSystem.prototype.ScheduleCard68CursedPick(self, excludeOptionIds, attempt) if attempt == nil then attempt = 0 end local maxAttempts = 48 local delay = attempt == 0 and 0.12 or 0.05 Timers:CreateTimer( delay, function() if not IsServer() then return nil end if self.isShowingCardSelection or #self.cardSelectionQueue > 0 then if attempt < maxAttempts then self:ScheduleCard68CursedPick(excludeOptionIds, attempt + 1) end return nil end do local function ____catch(err) print("[CardSystem] card_68 cursed pick: " .. tostring(err)) end local ____try, ____hasReturned, ____returnValue = pcall(function() local card68 = require("cards.examples.card_68") local slots = card68.CARD_68_OPTION_SLOTS or 3 local poolCards = self:GetPoolCards() local ____opt_19 = self:GetPlayer() local hero = ____opt_19 and ____opt_19:GetAssignedHero() local hasCard69 = hero and IsValidEntity(hero) and hero:IsRealHero() and hero:HasModifier("modifier_card_69") local ____hasCard69_25 if hasCard69 then local ____opt_21 = card68.buildAnyOfferIdsFromPool ____hasCard69_25 = ____opt_21 and ____opt_21(card68, poolCards, slots, excludeOptionIds) or ({}) else local ____opt_23 = card68.buildCursedOfferIdsFromPool ____hasCard69_25 = ____opt_23 and ____opt_23(card68, poolCards, slots, excludeOptionIds) or ({}) end local offerIds = ____hasCard69_25 if #offerIds <= 0 then print(((("[CardSystem] card_68: в колоде нет подходящих карт для выбора (игрок " .. tostring(self.playerId)) .. ", card69=") .. (hasCard69 and "yes" or "no")) .. ").") return true, nil end self:ShowCardSelectionExactOptions(offerIds, hasCard69 and "card_68_pool_any_pick" or "card_68_cursed_pick") end) if not ____try then ____hasReturned, ____returnValue = ____catch(____hasReturned) end if ____hasReturned then return ____returnValue end end return nil end ) end function CardSystem.prototype.ProcessNextCardSelection(self) if not self.cardLevelsLoaded then self:StartWaitingForCardLevelsIfNeeded() return end if #self.cardSelectionQueue == 0 then return end local nextSelection = table.remove(self.cardSelectionQueue, 1) self:SyncSelectionQueue() if nextSelection.exactOptionIds ~= nil and #nextSelection.exactOptionIds > 0 then self:ProcessCardSelectionExactIds(nextSelection.exactOptionIds, nextSelection.source, nextSelection.sourceChain, nextSelection.baseCount) elseif nextSelection.forcedCardId ~= nil then self:ProcessCardSelectionWithForcedCard( nextSelection.count, nextSelection.source, nextSelection.forcedCardId, nextSelection.sourceChain, nextSelection.baseCount ) else self:ProcessCardSelection( nextSelection.count, nextSelection.source, nextSelection.qualityFilter, nextSelection.sourceChain, nextSelection.baseCount ) end end function CardSystem.prototype.GetCardSelectionQueueSize(self) return #self.cardSelectionQueue end function CardSystem.prototype.ClearCardSelectionQueue(self) self.cardSelectionQueue = {} end function CardSystem.prototype.IsShowingCardSelection(self) return self.isShowingCardSelection end function CardSystem.prototype.SetRerollQualityFilter(self, qualityFilter) self.rerollQualityFilter = qualityFilter if qualityFilter then else end end function CardSystem.prototype.GetRerollQualityFilter(self) return self.rerollQualityFilter end function CardSystem.prototype.AddGuaranteedQualityRerolls(self, quality, rerollsCount, cardsPerReroll, fallbackBehavior) if fallbackBehavior == nil then fallbackBehavior = "skip" end print("[CardSystem.AddGuaranteedQualityRerolls] ===== ДОБАВЛЕНИЕ В СИСТЕМУ =====") print("[CardSystem.AddGuaranteedQualityRerolls] Игрок: " .. tostring(self.playerId)) print("[CardSystem.AddGuaranteedQualityRerolls] Качество: " .. tostring(quality)) print("[CardSystem.AddGuaranteedQualityRerolls] Количество реролов: " .. tostring(rerollsCount)) print("[CardSystem.AddGuaranteedQualityRerolls] Карт за рерол: " .. tostring(cardsPerReroll)) print("[CardSystem.AddGuaranteedQualityRerolls] Поведение при отсутствии: " .. fallbackBehavior) print("[CardSystem.AddGuaranteedQualityRerolls] Текущее количество гарантированных реролов: " .. tostring(#self.guaranteedQualityRerolls)) local existingIndex = __TS__ArrayFindIndex( self.guaranteedQualityRerolls, function(____, item) return item.quality == quality end ) if existingIndex ~= -1 then print("[CardSystem.AddGuaranteedQualityRerolls] ✅ Найдены существующие реролы для качества " .. tostring(quality)) print("[CardSystem.AddGuaranteedQualityRerolls] Было реролов: " .. tostring(self.guaranteedQualityRerolls[existingIndex + 1].rerollsLeft)) local ____self_guaranteedQualityRerolls_index_26, ____rerollsLeft_27 = self.guaranteedQualityRerolls[existingIndex + 1], "rerollsLeft" ____self_guaranteedQualityRerolls_index_26[____rerollsLeft_27] = ____self_guaranteedQualityRerolls_index_26[____rerollsLeft_27] + rerollsCount print("[CardSystem.AddGuaranteedQualityRerolls] Стало реролов: " .. tostring(self.guaranteedQualityRerolls[existingIndex + 1].rerollsLeft)) else print("[CardSystem.AddGuaranteedQualityRerolls] ✅ Создаем новые реролы для качества " .. tostring(quality)) local ____self_guaranteedQualityRerolls_28 = self.guaranteedQualityRerolls ____self_guaranteedQualityRerolls_28[#____self_guaranteedQualityRerolls_28 + 1] = {quality = quality, rerollsLeft = rerollsCount, cardsPerReroll = cardsPerReroll, fallbackBehavior = fallbackBehavior} print("[CardSystem.AddGuaranteedQualityRerolls] Добавлен новый элемент в очередь") end print("[CardSystem.AddGuaranteedQualityRerolls] Итоговое количество гарантированных реролов: " .. tostring(#self.guaranteedQualityRerolls)) print("[CardSystem.AddGuaranteedQualityRerolls] ==========================================") end function CardSystem.prototype.GetGuaranteedQualityRerolls(self, quality) local item = __TS__ArrayFind( self.guaranteedQualityRerolls, function(____, item) return item.quality == quality end ) return item and item.rerollsLeft or 0 end function CardSystem.prototype.GetGuaranteedCardsPerReroll(self, quality) local item = __TS__ArrayFind( self.guaranteedQualityRerolls, function(____, item) return item.quality == quality end ) return item and item.cardsPerReroll or 0 end function CardSystem.prototype.GetAllGuaranteedQualityRerolls(self) return {unpack(self.guaranteedQualityRerolls)} end function CardSystem.prototype.ClearGuaranteedQualityRerolls(self) self.guaranteedQualityRerolls = {} end function CardSystem.prototype.RefreshCard49DailyFreeReroll(self) if self:HasActiveCard(49) then self.card49FreeRerollCharges = math.min(____exports.CardSystem.CARD_49_FREE_STACK_CAP, self.card49FreeRerollCharges + 1) self:SyncRerollCost() end end function CardSystem.prototype.TryGrantCard6Level2FreeRerolls(self) if self.card6FreeRerollLevelGranted then return end if self:GetCardLevel(____exports.CardSystem.CARD_6_ID) < 2 then return end local grant = math.max( 0, math.floor(self:getCardValueByLevel(____exports.CardSystem.CARD_6_ID, "free_reroll_charges", 2)) ) if grant <= 0 then return end self.card6FreeRerollLevelGranted = true self.card6FreeRerollCharges = math.min(____exports.CardSystem.CARD_6_FREE_STACK_CAP, self.card6FreeRerollCharges + grant) self:SyncRerollCost() end function CardSystem.prototype.GetCard6MinSelectionCount(self) if not self:HasActiveCard(____exports.CardSystem.CARD_6_ID) then return 0 end return math.max( 0, math.floor(self:getCardValueByLevel(____exports.CardSystem.CARD_6_ID, "min_card_choice", 0)) ) end function CardSystem.prototype.getCardValueByLevel(self, cardId, key, fallback) local ____opt_29 = self:GetPlayer() local hero = ____opt_29 and ____opt_29:GetAssignedHero() local resolver = require("cards.card_value_resolver") local ____opt_31 = resolver.getCardValueByLevel return ____opt_31 and ____opt_31( resolver, cardId, hero, key, fallback ) or fallback end function CardSystem.prototype.GetCard6FreeRerollChargesForUse(self) if not self:HasActiveCard(____exports.CardSystem.CARD_6_ID) or self:GetCardLevel(____exports.CardSystem.CARD_6_ID) < 2 then return 0 end return math.max( 0, math.floor(self.card6FreeRerollCharges) ) end function CardSystem.prototype.GetCard88FreeRerollChargesForUse(self) if not self:HasActiveCard(____exports.CardSystem.CARD_88_ID) then return 0 end return math.max( 0, math.floor(self.card88FreeRerollCharges) ) end function CardSystem.prototype.TryGrantCard88FreeRerollsFromSlag(self, slagCount) if slagCount == nil then slagCount = 1 end if not self:HasActiveCard(____exports.CardSystem.CARD_88_ID) then return end local safeSlagCount = math.max( 0, math.floor(slagCount) ) if safeSlagCount <= 0 then return end local grantPerSlag = math.max( 0, math.floor(self:getCardValueByLevel(____exports.CardSystem.CARD_88_ID, "free_rerolls_on_slag", 2)) ) local grant = grantPerSlag * safeSlagCount if grant <= 0 then return end self.card88FreeRerollCharges = math.min(____exports.CardSystem.CARD_88_FREE_STACK_CAP, self.card88FreeRerollCharges + grant) self:SyncRerollCost() end function CardSystem.prototype.ResolveSelectionQualityFilter(self, source, qualityFilter) if source == "modifier_card_6_bonus_selection" and self:HasActiveCard(____exports.CardSystem.CARD_6_ID) and self:GetCardLevel(____exports.CardSystem.CARD_6_ID) >= 3 then return {____exports.CardQuality.LEGENDARY, ____exports.CardQuality.MYTHIC} end return qualityFilter end function CardSystem.prototype.ShowCardSelection(self, count, source, qualityFilter) if source == nil then source = "unknown" end local resolvedQualityFilter = self:ResolveSelectionQualityFilter(source, qualityFilter) local baseCount = math.max( 1, math.floor(count) ) local resolvedCount = self:ResolveCardSelectionCount(baseCount) local sourceChain = self:BuildSourceChain(source, self.effectSourceChainContext) print("[CardSystem.ShowCardSelection] ===== ПОКАЗ ВЫБОРА КАРТ =====") print("[CardSystem.ShowCardSelection] Игрок: " .. tostring(self.playerId)) print("[CardSystem.ShowCardSelection] Запрошено карт: " .. tostring(count)) print("[CardSystem.ShowCardSelection] Итоговое количество карт: " .. tostring(resolvedCount)) print("[CardSystem.ShowCardSelection] Источник: " .. source) print("[CardSystem.ShowCardSelection] Фильтр качества: " .. (resolvedQualityFilter and table.concat(resolvedQualityFilter, ", ") or "нет")) print("[CardSystem.ShowCardSelection] Уже показываем выбор: " .. tostring(self.isShowingCardSelection)) print("[CardSystem.ShowCardSelection] Размер пула карт: " .. tostring(self.pool.len)) if not self.cardLevelsLoaded then print("[CardSystem.ShowCardSelection] ⏳ Уровни карт ещё не загружены, откладываем показ в очередь") self:AddToCardSelectionQueue( resolvedCount, baseCount, source, resolvedQualityFilter, nil, nil, sourceChain ) self:StartWaitingForCardLevelsIfNeeded() return end if resolvedQualityFilter then end if self.isShowingCardSelection then print(("[CardSystem.ShowCardSelection] ⚠️ Добавляем в очередь выбор из " .. tostring(resolvedCount)) .. " карт") self:AddToCardSelectionQueue( resolvedCount, baseCount, source, resolvedQualityFilter, nil, nil, sourceChain ) return end print(("[CardSystem.ShowCardSelection] ✅ Показываем выбор из " .. tostring(resolvedCount)) .. " карт") self:ProcessCardSelection( resolvedCount, source, resolvedQualityFilter, sourceChain, baseCount ) print("[CardSystem.ShowCardSelection] ======================================") end function CardSystem.prototype.ShowCardSelectionWithExtraChoices(self, baseCount, extraCount, source, qualityFilter) if source == nil then source = "unknown" end local normalizedBase = math.max( 1, math.floor(baseCount) ) local normalizedExtra = math.max( 0, math.floor(extraCount) ) self:ShowCardSelection(normalizedBase + normalizedExtra, source, qualityFilter) end function CardSystem.prototype.GetPassiveCardSelectionExtraChoices(self) local extra = 0 if self:HasActiveCard(6) then extra = extra + 1 end return extra end function CardSystem.prototype.BuildOptionSourceChainWithCard6Bonus(self, baseChain, optionIndex, baseCount, source) local chain = baseChain and ({unpack(baseChain)}) or ({}) local normalizedBase = math.max( 1, math.floor(baseCount) ) local isCard6TriggeredSelection = source == "modifier_card_6_bonus_selection" local shouldAppendCard6 = self:HasActiveCard(6) and (isCard6TriggeredSelection or optionIndex >= normalizedBase) if shouldAppendCard6 then return self:BuildSourceChain("modifier_card_6_bonus_selection", chain) end return #chain > 0 and chain or nil end function CardSystem.prototype.ResolveCardSelectionCount(self, count) local baseCount = math.max( 1, math.floor(count) ) local desiredCount = baseCount + self:GetPassiveCardSelectionExtraChoices() local minByCard6 = self:GetCard6MinSelectionCount() if minByCard6 > 0 then desiredCount = math.max(desiredCount, minByCard6) end local maxByPool = self:GetMaxSelectableOptionsByPool() if maxByPool > 0 then return math.max( 1, math.min(desiredCount, maxByPool) ) end return desiredCount end function CardSystem.prototype.GetMaxSelectableOptionsByPool(self) if not self.pool then return 0 end local total = __TS__Number(self.pool.totalProportion or 0) if not __TS__NumberIsFinite(total) or total <= 0 then return 0 end return math.max( 1, math.floor(total) ) end function CardSystem.prototype.SkipsDawnCardSelection(self) return self:HasActiveCard(58) end function CardSystem.prototype.HasActiveCard(self, cardId) if __TS__ArrayIncludes(self.itemsId, cardId) then return true end local player = self:GetPlayer() local hero = player and player:GetAssignedHero() if not hero then return false end return hero:HasModifier("modifier_card_" .. tostring(cardId)) end function CardSystem.prototype.DuplicateCurrentPool(self) local snapshot = {} if self.pool and self.pool.mappingTable ~= nil then for key in pairs(self.pool.mappingTable) do local item = self.pool.mappingTable[key] local cardId = tonumber(key) local weight = item and item.proportion or 0 if cardId and weight > 0 and not isMirrorProtectedCardId(nil, cardId) then snapshot[#snapshot + 1] = {cardId = cardId, weight = weight} end end end for ____, entry in ipairs(snapshot) do self:CreatePoolEntry(entry.cardId, entry.weight, "duplicate_current_pool") end self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.ScheduleRepeatNextSelectedCardOnce(self) self.repeatNextSelectedCardOnce = true end function CardSystem.prototype.HasRepeatNextSelectedCardScheduled(self) return self.repeatNextSelectedCardOnce end function CardSystem.prototype.DuplicateUnselectedCurrentOptions(self, selectedCardId, optionsSnapshot) local options = optionsSnapshot or self.playerItems if not options then return end local selected = math.floor(__TS__Number(selectedCardId)) local uniqueOptionIds = __TS__New(Set) for key in pairs(options) do do local ____math_floor_39 = math.floor local ____opt_37 = options[key] local optionId = ____math_floor_39(__TS__Number(____opt_37 and ____opt_37.index)) if not __TS__NumberIsFinite(optionId) or optionId <= 0 then goto __continue238 end if isEmptySelectionCardId(nil, optionId) or optionId == selected or isMirrorProtectedCardId(nil, optionId) then goto __continue238 end uniqueOptionIds:add(optionId) end ::__continue238:: end for ____, optionId in __TS__Iterator(uniqueOptionIds) do local currentWeight = self.pool:getWeightPrize(optionId) if currentWeight > 0 then self:CreatePoolEntry(optionId, 1, "duplicate_unselected_current_options") end end self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.AddToCardSelectionQueue(self, count, baseCount, source, qualityFilter, forcedCardId, exactOptionIds, sourceChain) local priority = #self.cardSelectionQueue local queueItem = { count = exactOptionIds ~= nil and #exactOptionIds > 0 and #exactOptionIds or count, baseCount = math.max( 1, math.floor(baseCount) ), priority = priority, source = source, sourceChain = sourceChain, qualityFilter = qualityFilter, forcedCardId = forcedCardId, exactOptionIds = exactOptionIds } local ____self_cardSelectionQueue_40 = self.cardSelectionQueue ____self_cardSelectionQueue_40[#____self_cardSelectionQueue_40 + 1] = queueItem self:SyncSelectionQueue() if qualityFilter then end end function CardSystem.prototype.StartWaitingForCardLevelsIfNeeded(self) if self.cardLevelsWaitTimerStarted then return end self.cardLevelsWaitTimerStarted = true Timers:CreateTimer( 0.25, function() if not self.cardLevelsLoaded then return 0.25 end self.cardLevelsWaitTimerStarted = false self:ProcessNextCardSelection() return nil end ) end function CardSystem.prototype.ShowCardSelectionWithForcedCard(self, forcedCardId, count, source) if count == nil then count = 3 end if source == nil then source = "forced_card" end local sourceChain = self:BuildSourceChain(source, self.effectSourceChainContext) local data = ____exports.CardSystem.cardData[forcedCardId] if data == nil then print("[CardSystem.ShowCardSelectionWithForcedCard] неизвестный id " .. tostring(forcedCardId)) return end if data.disabled == true and forcedCardId ~= 404 then print(("[CardSystem.ShowCardSelectionWithForcedCard] карта " .. tostring(forcedCardId)) .. " отключена") return end local resolvedCount = self:ResolveCardSelectionCount(count) if not self.cardLevelsLoaded then self:AddToCardSelectionQueue( resolvedCount, math.max( 1, math.floor(count) ), source, nil, forcedCardId, nil, sourceChain ) self:StartWaitingForCardLevelsIfNeeded() return end if self.isShowingCardSelection then self:AddToCardSelectionQueue( resolvedCount, math.max( 1, math.floor(count) ), source, nil, forcedCardId, nil, sourceChain ) return end self:ProcessCardSelectionWithForcedCard( resolvedCount, source, forcedCardId, sourceChain, math.max( 1, math.floor(count) ) ) end function CardSystem.prototype.BuildRandomNonInherentCatalogIds(self, count, excludeIds, qualityFilter) local slots = math.max( 1, math.floor(count) ) local exclude = __TS__New(Set, excludeIds) local candidates = {} for ____, key in ipairs(__TS__ObjectKeys(____exports.CardSystem.cardData)) do do local id = __TS__ParseInt(key, 10) if __TS__NumberIsNaN(id) or id == 404 then goto __continue257 end if exclude:has(id) then goto __continue257 end local def = ____exports.CardSystem.cardData[id] if not def or def.disabled == true then goto __continue257 end if qualityFilter ~= nil and def.quality ~= qualityFilter then goto __continue257 end if def.inherent == true then goto __continue257 end candidates[#candidates + 1] = id end ::__continue257:: end local shuffled = {unpack(candidates)} do local i = #shuffled - 1 while i > 0 do local j = math.floor(math.random() * (i + 1)) local tmp = shuffled[i + 1] shuffled[i + 1] = shuffled[j + 1] shuffled[j + 1] = tmp i = i - 1 end end local result = {} local uniqueTake = math.min(slots, #shuffled) do local i = 0 while i < uniqueTake do result[#result + 1] = shuffled[i + 1] i = i + 1 end end while #result < slots do if #candidates == 0 then result[#result + 1] = 404 else local r = candidates[math.floor(math.random() * #candidates) + 1] result[#result + 1] = r end end return result end function CardSystem.prototype.BuildRandomLegendaryNonInherentIds(self, count, excludeIds) return self:BuildRandomNonInherentCatalogIds(count, excludeIds, ____exports.CardQuality.LEGENDARY) end function CardSystem.prototype.ProcessCardSelectionExactIds(self, ids, source, sourceChain, baseCount) if baseCount == nil then baseCount = #ids end self.isShowingCardSelection = true self:SetCurrentSelectionSource(source, sourceChain) self:SyncSelectionQueue() self:SyncPool() local cardSelection = {} local optionEntryIds = self:SelectEntryIdsForCards(ids) do local i = 0 while i < #ids do local rawId = ids[i + 1] local def = ____exports.CardSystem.cardData[rawId] local safeId = def ~= nil and def.disabled ~= true and rawId or 404 local ____temp_41 if safeId ~= 404 then ____temp_41 = optionEntryIds[i + 1] else ____temp_41 = nil end local entryId = ____temp_41 local optionSourceChain = self:BuildOptionSourceChainWithCard6Bonus( self:GetPoolEntrySourceChain(entryId), i, baseCount, source ) cardSelection[tostring(i + 1)] = { index = safeId, entry_id = entryId, source_chain = optionSourceChain and self:SerializeSourceChain(optionSourceChain) or nil, source_chain_items = optionSourceChain and ({unpack(optionSourceChain)}) or nil, selection_token = self.currentSelectionToken } i = i + 1 end end self.playerItems = cardSelection self:SyncPlayerItems() self:SyncSelectionSource() self:SyncRerollCost() end function CardSystem.prototype.ShowLegendaryNonInherentCatalogSelection(self, requestedSlots, source, excludeOptionIds) if requestedSlots == nil then requestedSlots = 3 end if source == nil then source = "legendary_catalog_pick" end if excludeOptionIds == nil then excludeOptionIds = {} end local resolved = self:ResolveCardSelectionCount(requestedSlots) local ids = self:BuildRandomLegendaryNonInherentIds(resolved, excludeOptionIds) if not self.cardLevelsLoaded then self:AddToCardSelectionQueue( resolved, math.max( 1, math.floor(requestedSlots) ), source, nil, nil, ids, self:BuildSourceChain(source, self.effectSourceChainContext) ) self:StartWaitingForCardLevelsIfNeeded() return end if self.isShowingCardSelection then self:AddToCardSelectionQueue( resolved, math.max( 1, math.floor(requestedSlots) ), source, nil, nil, ids, self:BuildSourceChain(source, self.effectSourceChainContext) ) return end self:ProcessCardSelectionExactIds( ids, source, self:BuildSourceChain(source, self.effectSourceChainContext), math.max( 1, math.floor(requestedSlots) ) ) end function CardSystem.prototype.ShowAnyNonInherentCatalogSelection(self, requestedSlots, source, excludeOptionIds) if requestedSlots == nil then requestedSlots = 3 end if source == nil then source = "any_catalog_pick" end if excludeOptionIds == nil then excludeOptionIds = {} end local resolved = self:ResolveCardSelectionCount(requestedSlots) local ids = self:BuildRandomNonInherentCatalogIds(resolved, excludeOptionIds) if not self.cardLevelsLoaded then self:AddToCardSelectionQueue( resolved, math.max( 1, math.floor(requestedSlots) ), source, nil, nil, ids, self:BuildSourceChain(source, self.effectSourceChainContext) ) self:StartWaitingForCardLevelsIfNeeded() return end if self.isShowingCardSelection then self:AddToCardSelectionQueue( resolved, math.max( 1, math.floor(requestedSlots) ), source, nil, nil, ids, self:BuildSourceChain(source, self.effectSourceChainContext) ) return end self:ProcessCardSelectionExactIds( ids, source, self:BuildSourceChain(source, self.effectSourceChainContext), math.max( 1, math.floor(requestedSlots) ) ) end function CardSystem.prototype.ShowCardSelectionExactOptions(self, optionIds, source) if source == nil then source = "exact_options" end local normalized = {} for ____, raw in ipairs(optionIds) do do local id = math.floor(__TS__Number(raw)) if not __TS__NumberIsFinite(id) or id <= 0 then goto __continue279 end if id == 404 then normalized[#normalized + 1] = 404 goto __continue279 end local def = ____exports.CardSystem.cardData[id] if def == nil or def.disabled == true then goto __continue279 end normalized[#normalized + 1] = id end ::__continue279:: end if #normalized <= 0 then return end if not self.cardLevelsLoaded then self:AddToCardSelectionQueue( #normalized, #normalized, source, nil, nil, normalized, self:BuildSourceChain(source, self.effectSourceChainContext) ) self:StartWaitingForCardLevelsIfNeeded() return end if self.isShowingCardSelection then self:AddToCardSelectionQueue( #normalized, #normalized, source, nil, nil, normalized, self:BuildSourceChain(source, self.effectSourceChainContext) ) return end self:ProcessCardSelectionExactIds( normalized, source, self:BuildSourceChain(source, self.effectSourceChainContext), #normalized ) end function CardSystem.prototype.BuildForcedCardOptionIds(self, forcedCardId, totalSlots) local slots = math.max( 1, math.floor(totalSlots) ) local fromPool = __TS__ArrayFilter( self:GetPoolCards(), function(____, id) return id ~= forcedCardId end ) local result = {forcedCardId} local needMore = slots - 1 if needMore > 0 then if #fromPool == 0 then do local i = 0 while i < needMore do result[#result + 1] = 404 i = i + 1 end end else local extra = self:DrawRandomFromSubsetRespectingWeights(fromPool, needMore) for ____, id in ipairs(extra) do result[#result + 1] = id end end end do local i = #result - 1 while i > 0 do local j = math.floor(math.random() * (i + 1)) local tmp = result[i + 1] result[i + 1] = result[j + 1] result[j + 1] = tmp i = i - 1 end end return result end function CardSystem.prototype.ProcessCardSelectionWithForcedCard(self, count, source, forcedCardId, sourceChain, baseCount) if baseCount == nil then baseCount = count end self.isShowingCardSelection = true self:SetCurrentSelectionSource(source, sourceChain) self:SyncSelectionQueue() self:SyncPool() local ids = self:BuildForcedCardOptionIds(forcedCardId, count) local cardSelection = {} local optionEntryIds = self:SelectEntryIdsForCards(ids) do local i = 0 while i < #ids do local safeId = ids[i + 1] local ____temp_42 if safeId ~= 404 then ____temp_42 = optionEntryIds[i + 1] else ____temp_42 = nil end local entryId = ____temp_42 local optionSourceChain = self:BuildOptionSourceChainWithCard6Bonus( self:GetPoolEntrySourceChain(entryId), i, baseCount, source ) cardSelection[tostring(i + 1)] = { index = safeId, entry_id = entryId, source_chain = optionSourceChain and self:SerializeSourceChain(optionSourceChain) or nil, source_chain_items = optionSourceChain and ({unpack(optionSourceChain)}) or nil, selection_token = self.currentSelectionToken } i = i + 1 end end self.playerItems = cardSelection self:SyncPlayerItems() self:SyncSelectionSource() self:SyncRerollCost() end function CardSystem.prototype.ProcessCardSelection(self, count, source, qualityFilter, sourceChain, baseCount) if baseCount == nil then baseCount = count end print("[CardSystem.ProcessCardSelection] ===== ОБРАБОТКА ВЫБОРА КАРТ =====") print("[CardSystem.ProcessCardSelection] Игрок: " .. tostring(self.playerId)) print("[CardSystem.ProcessCardSelection] Запрошено карт: " .. tostring(count)) print("[CardSystem.ProcessCardSelection] Источник: " .. source) print("[CardSystem.ProcessCardSelection] Фильтр качества: " .. (qualityFilter and table.concat(qualityFilter, ", ") or "нет")) if qualityFilter then end self.isShowingCardSelection = true self:SetCurrentSelectionSource(source, sourceChain) print("[CardSystem.ProcessCardSelection] Флаг isShowingCardSelection установлен в true") self:SyncSelectionQueue() self:SyncPool() print("[CardSystem.ProcessCardSelection] Размер пула после синхронизации: " .. tostring(self.pool.len)) if self.pool.len == 0 then print(("[CardSystem.ProcessCardSelection] ⚠️ Пул пустой, показываем " .. tostring(count)) .. " карт 404") local cardSelection = {} do local i = 0 while i < count do cardSelection[tostring(i + 1)] = {index = 404} i = i + 1 end end self.playerItems = cardSelection self:SyncPlayerItems() self:SyncSelectionSource() self:SyncRerollCost() self.isShowingCardSelection = false self:SyncSelectionQueue() print("[CardSystem.ProcessCardSelection] ==========================================") return end print("[CardSystem.ProcessCardSelection] Получаем карты с фильтром качества...") local isReroll = source == "reroll" local ____isReroll_43 if isReroll then ____isReroll_43 = nil else ____isReroll_43 = qualityFilter end local effectiveQualityFilter = ____isReroll_43 local cards = self:GetCardsWithQualityFilter(count, effectiveQualityFilter, isReroll) print(((("[CardSystem.ProcessCardSelection] Получено карт: " .. tostring(#cards)) .. " (запрошено: ") .. tostring(count)) .. ")") local cardSelection = {} local optionEntryIds = self:SelectEntryIdsForCards(cards) do local i = 0 while i < #cards do local cardId = cards[i + 1] local ____temp_44 if cardId ~= 404 then ____temp_44 = optionEntryIds[i + 1] else ____temp_44 = nil end local entryId = ____temp_44 local optionSourceChain = self:BuildOptionSourceChainWithCard6Bonus( self:GetPoolEntrySourceChain(entryId), i, baseCount, source ) cardSelection[tostring(i + 1)] = { index = cardId, entry_id = entryId, source_chain = optionSourceChain and self:SerializeSourceChain(optionSourceChain) or nil, source_chain_items = optionSourceChain and ({unpack(optionSourceChain)}) or nil, selection_token = self.currentSelectionToken } i = i + 1 end end self.playerItems = cardSelection self:SyncPlayerItems() self:SyncSelectionSource() if source ~= "reroll" then self:SetRerollQualityFilter(qualityFilter) end self:SyncRerollCost() end function CardSystem.prototype.DrawRandomCardIdsRespectingPoolWeights(self, count, excludedIds, strictExcludeIds) if strictExcludeIds == nil then strictExcludeIds = false end if count <= 0 then return {} end if self.pool.len == 0 then local out = {} do local i = 0 while i < count do out[#out + 1] = 404 i = i + 1 end end return out end local raw = self:DrawBiasedCardIdsFromPool(count, nil, excludedIds, strictExcludeIds) while #raw < count do raw[#raw + 1] = 404 end return raw end function CardSystem.prototype.DrawRandomFromSubsetRespectingWeights(self, allowed, count, excludedIds, strictExcludeIds) if strictExcludeIds == nil then strictExcludeIds = false end if count <= 0 then return {} end local raw = self:DrawBiasedCardIdsFromPool(count, allowed, excludedIds, strictExcludeIds) if #raw <= 0 then local out = {} do local i = 0 while i < count do out[#out + 1] = 404 i = i + 1 end end return out end while #raw < count do raw[#raw + 1] = 404 end return raw end function CardSystem.prototype.BuildPoolRemainingWeights(self, allowed, excludedIds) local allowedSet = allowed and #allowed > 0 and __TS__New(Set, allowed) or nil local remaining = {} if self.pool and self.pool.mappingTable ~= nil then for key in pairs(self.pool.mappingTable) do do local cardId = __TS__ParseInt(key) if not __TS__NumberIsFinite(cardId) or cardId <= 0 then goto __continue316 end if allowedSet and not allowedSet:has(cardId) then goto __continue316 end local weight = self.pool:getWeightPrize(cardId) if weight > 0 then remaining[tostring(cardId)] = math.floor(weight) end end ::__continue316:: end end return remaining end function CardSystem.prototype.DrawCardIdsFromRemaining(self, count, remaining, allowed, excludedIds, strictExcludeIds) if strictExcludeIds == nil then strictExcludeIds = false end if count <= 0 then return {} end local allowedSet = allowed and #allowed > 0 and __TS__New(Set, allowed) or nil local batchExcluded = {} if excludedIds then for ____, raw in ipairs(excludedIds) do local id = math.floor(__TS__Number(raw)) if __TS__NumberIsFinite(id) and id > 0 then batchExcluded[id] = true end end end local result = {} do local pick = 0 while pick < count do local totalWeight = 0 local entries = {} local hasNonExcludedCandidates = false for ____, ____value in ipairs(__TS__ObjectEntries(remaining)) do local cardIdRaw = ____value[1] local leftRaw = ____value[2] do local cardId = __TS__Number(cardIdRaw) local left = __TS__Number(leftRaw) if not __TS__NumberIsFinite(cardId) or not __TS__NumberIsFinite(left) or left <= 0 then goto __continue328 end if allowedSet and not allowedSet:has(cardId) then goto __continue328 end if not batchExcluded[cardId] then hasNonExcludedCandidates = true end end ::__continue328:: end for ____, ____value in ipairs(__TS__ObjectEntries(remaining)) do local cardIdRaw = ____value[1] local leftRaw = ____value[2] do local cardId = __TS__Number(cardIdRaw) local left = __TS__Number(leftRaw) if not __TS__NumberIsFinite(cardId) or not __TS__NumberIsFinite(left) or left <= 0 then goto __continue333 end if allowedSet and not allowedSet:has(cardId) then goto __continue333 end if batchExcluded[cardId] and (strictExcludeIds or hasNonExcludedCandidates) then goto __continue333 end local pickWeight = math.max( 1, math.floor(left) ) entries[#entries + 1] = {cardId = cardId, pickWeight = pickWeight} totalWeight = totalWeight + pickWeight end ::__continue333:: end if totalWeight <= 0 or #entries <= 0 then break end local roll = RandomInt(1, totalWeight) local cursor = 0 local selectedCardId = entries[1].cardId for ____, entry in ipairs(entries) do cursor = cursor + entry.pickWeight if roll <= cursor then selectedCardId = entry.cardId break end end result[#result + 1] = selectedCardId local key = tostring(selectedCardId) remaining[key] = math.max(0, (remaining[key] or 0) - 1) pick = pick + 1 end end return result end function CardSystem.prototype.DrawBiasedCardIdsFromPool(self, count, allowed, excludedIds, strictExcludeIds) if strictExcludeIds == nil then strictExcludeIds = false end if count <= 0 then return {} end local remaining = self:BuildPoolRemainingWeights(allowed, excludedIds) return self:DrawCardIdsFromRemaining( count, remaining, allowed, excludedIds, strictExcludeIds ) end function CardSystem.prototype.GetCardsWithQualityFilter(self, count, qualityFilter, isReroll, excludedIds, strictExcludeIds) if isReroll == nil then isReroll = false end if strictExcludeIds == nil then strictExcludeIds = false end print("[CardSystem.GetCardsWithQualityFilter] ===== ПОЛУЧЕНИЕ КАРТ С ФИЛЬТРОМ =====") print("[CardSystem.GetCardsWithQualityFilter] Игрок: " .. tostring(self.playerId)) print("[CardSystem.GetCardsWithQualityFilter] Запрошено карт: " .. tostring(count)) print("[CardSystem.GetCardsWithQualityFilter] Фильтр качества: " .. (qualityFilter and table.concat(qualityFilter, ", ") or "нет")) print("[CardSystem.GetCardsWithQualityFilter] Это рерол: " .. tostring(isReroll)) print("[CardSystem.GetCardsWithQualityFilter] Гарантированных реролов: " .. tostring(#self.guaranteedQualityRerolls)) if isReroll and #self.guaranteedQualityRerolls > 0 then print("[CardSystem.GetCardsWithQualityFilter] ✅ Используем гарантированные карты качества") local result = self:GetRerollCardsWithGuaranteedQuality(count, qualityFilter, excludedIds, strictExcludeIds) print(("[CardSystem.GetCardsWithQualityFilter] Возвращаем " .. tostring(#result)) .. " карт из гарантированных") return result end if not qualityFilter or #qualityFilter == 0 then print("[CardSystem.GetCardsWithQualityFilter] ✅ Случайный выбор с учётом весов в пуле (randomCard)") local result = self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) print(("[CardSystem.GetCardsWithQualityFilter] Возвращаем " .. tostring(#result)) .. " карт") print(("[CardSystem.GetCardsWithQualityFilter] ID карт: [" .. table.concat(result, ", ")) .. "]") return result end local filteredCards = self:GetPoolCardIdsMatchingQualities(qualityFilter) if #filteredCards == 0 then local fallbackQuality = self:GetNextAvailableQuality(self:GetHighestQualityInFilter(qualityFilter)) if fallbackQuality ~= nil then print((("[CardSystem.GetCardsWithQualityFilter] Нет карт по фильтру [" .. table.concat(qualityFilter, ", ")) .. "] — даунгрейд до ") .. tostring(fallbackQuality)) return self:GetCardsWithQualityFilter( count, {fallbackQuality}, isReroll, excludedIds, strictExcludeIds ) end print("[CardSystem.GetCardsWithQualityFilter] Нет карт по фильтру и даунгрейда — общий пул") return self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) end local remaining = self:BuildPoolRemainingWeights(nil, excludedIds) local result = self:DrawCardIdsFromRemaining( count, remaining, filteredCards, excludedIds, strictExcludeIds ) while #result < count do result[#result + 1] = 404 end result = self:FillSelectionGapsWithLowerQuality(result, qualityFilter, excludedIds, remaining) print(("[CardSystem.GetCardsWithQualityFilter] По фильтру качества: [" .. table.concat(result, ", ")) .. "]") return result end function CardSystem.prototype.GetPoolCardIdsMatchingQualities(self, qualities) local allowed = __TS__New(Set, qualities) local filteredCards = {} for ____, cardId in ipairs(self:GetPoolCards()) do local cardData = ____exports.CardSystem.cardData[cardId] if cardData and allowed:has(cardData.quality) then filteredCards[#filteredCards + 1] = cardId end end return filteredCards end function CardSystem.prototype.GetHighestQualityInFilter(self, qualityFilter) local best = qualityFilter[1] for ____, quality in ipairs(qualityFilter) do local bestIndex = __TS__ArrayIndexOf(____exports.CardSystem.QUALITY_FALLBACK_ORDER, best) local qualityIndex = __TS__ArrayIndexOf(____exports.CardSystem.QUALITY_FALLBACK_ORDER, quality) if qualityIndex >= 0 and (bestIndex < 0 or qualityIndex < bestIndex) then best = quality end end return best end function CardSystem.prototype.GetLowestRarityInFilter(self, qualityFilter) local lowest = qualityFilter[1] for ____, quality in ipairs(qualityFilter) do local lowestIndex = __TS__ArrayIndexOf(____exports.CardSystem.QUALITY_FALLBACK_ORDER, lowest) local qualityIndex = __TS__ArrayIndexOf(____exports.CardSystem.QUALITY_FALLBACK_ORDER, quality) if qualityIndex >= 0 and (lowestIndex < 0 or qualityIndex > lowestIndex) then lowest = quality end end return lowest end function CardSystem.prototype.FillSelectionGapsWithLowerQuality(self, selection, qualityFilter, excludedIds, remaining) local stillMissing = #__TS__ArrayFilter( selection, function(____, id) return id == 404 end ) if stillMissing <= 0 then return selection end local filled = {unpack(selection)} local poolRemaining = remaining or self:BuildPoolRemainingWeights(nil, excludedIds) local fallbackQuality = self:GetNextAvailableQuality(self:GetLowestRarityInFilter(qualityFilter)) while stillMissing > 0 and fallbackQuality ~= nil do do local fallbackPool = self:GetPoolCardIdsMatchingQualities({fallbackQuality}) if #fallbackPool <= 0 then fallbackQuality = self:GetNextAvailableQuality(fallbackQuality) goto __continue365 end local replacements = self:DrawCardIdsFromRemaining(stillMissing, poolRemaining, fallbackPool, excludedIds) local replaceIndex = 0 do local i = 0 while i < #filled do if filled[i + 1] == 404 and replaceIndex < #replacements and replacements[replaceIndex + 1] ~= 404 then filled[i + 1] = replacements[replaceIndex + 1] replaceIndex = replaceIndex + 1 end i = i + 1 end end stillMissing = #__TS__ArrayFilter( filled, function(____, id) return id == 404 end ) if stillMissing <= 0 then break end fallbackQuality = self:GetNextAvailableQuality(fallbackQuality) end ::__continue365:: end return filled end function CardSystem.prototype.GetNextAvailableQuality(self, targetQuality) local targetIndex = __TS__ArrayIndexOf(____exports.CardSystem.QUALITY_FALLBACK_ORDER, targetQuality) if targetIndex == -1 then return nil end do local i = targetIndex + 1 while i < #____exports.CardSystem.QUALITY_FALLBACK_ORDER do local quality = ____exports.CardSystem.QUALITY_FALLBACK_ORDER[i + 1] if #self:GetPoolCardIdsMatchingQualities({quality}) > 0 then return quality end i = i + 1 end end return nil end function CardSystem.prototype.GetRerollCardsWithGuaranteedQuality(self, count, qualityFilter, excludedIds, strictExcludeIds) if strictExcludeIds == nil then strictExcludeIds = false end print("[CardSystem.GetRerollCardsWithGuaranteedQuality] ===== ОБРАБОТКА РЕРОЛА =====") print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Игрок: " .. tostring(self.playerId)) print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Запрошено карт: " .. tostring(count)) print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Гарантированных реролов: " .. tostring(#self.guaranteedQualityRerolls)) do local i = 0 while i < #self.guaranteedQualityRerolls do local reroll = self.guaranteedQualityRerolls[i + 1] print((((((("[CardSystem.GetRerollCardsWithGuaranteedQuality] Рерол " .. tostring(i)) .. ": качество=") .. tostring(reroll.quality)) .. ", осталось=") .. tostring(reroll.rerollsLeft)) .. ", карт=") .. tostring(reroll.cardsPerReroll)) i = i + 1 end end local priorityQuality = __TS__ArrayFind( self.guaranteedQualityRerolls, function(____, item) return item.rerollsLeft > 0 end ) if not priorityQuality then print("[CardSystem.GetRerollCardsWithGuaranteedQuality] ❌ Нет доступных гарантированных реролов") print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем обычные случайные карты (веса пула)") local result = self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем " .. tostring(#result)) .. " случайных карт") print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] ID карт: [" .. table.concat(result, ", ")) .. "]") return result end print("[CardSystem.GetRerollCardsWithGuaranteedQuality] ✅ Найден приоритетный рерол:") print("[CardSystem.GetRerollCardsWithGuaranteedQuality] - Качество: " .. tostring(priorityQuality.quality)) print("[CardSystem.GetRerollCardsWithGuaranteedQuality] - Осталось реролов: " .. tostring(priorityQuality.rerollsLeft)) print("[CardSystem.GetRerollCardsWithGuaranteedQuality] - Карт за рерол: " .. tostring(priorityQuality.cardsPerReroll)) print("[CardSystem.GetRerollCardsWithGuaranteedQuality] - Поведение при отсутствии: " .. priorityQuality.fallbackBehavior) priorityQuality.rerollsLeft = priorityQuality.rerollsLeft - 1 print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Счетчик реролов уменьшен до: " .. tostring(priorityQuality.rerollsLeft)) if priorityQuality.rerollsLeft <= 0 then local index = __TS__ArrayIndexOf(self.guaranteedQualityRerolls, priorityQuality) if index > -1 then __TS__ArraySplice(self.guaranteedQualityRerolls, index, 1) print("[CardSystem.GetRerollCardsWithGuaranteedQuality] ✅ Удален использованный рерол из массива") print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Осталось гарантированных реролов: " .. tostring(#self.guaranteedQualityRerolls)) end end local allCards = self:GetPoolCards() local qualityCards = {} for ____, cardId in ipairs(allCards) do local cardData = ____exports.CardSystem.cardData[cardId] if cardData and cardData.quality == priorityQuality.quality then qualityCards[#qualityCards + 1] = cardId end end if #qualityCards == 0 then repeat local ____switch385 = priorityQuality.fallbackBehavior local skipResult, delayResult, fallbackQuality, replaceResult, defaultResult local ____cond385 = ____switch385 == "skip" if ____cond385 then print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Пропускаем рерол (skip)") skipResult = self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем " .. tostring(#skipResult)) .. " случайных карт (skip)") return skipResult end ____cond385 = ____cond385 or ____switch385 == "delay" if ____cond385 then print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Откладываем рерол (delay)") delayResult = self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем " .. tostring(#delayResult)) .. " случайных карт (delay)") return delayResult end ____cond385 = ____cond385 or ____switch385 == "replace" if ____cond385 then fallbackQuality = self:GetNextAvailableQuality(priorityQuality.quality) if fallbackQuality ~= nil then local fallbackCards = {} for ____, cardId in ipairs(allCards) do local cardData = ____exports.CardSystem.cardData[cardId] if cardData and cardData.quality == fallbackQuality then fallbackCards[#fallbackCards + 1] = cardId end end if #fallbackCards > 0 then print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Заменяем на качество " .. tostring(fallbackQuality)) local takeFromFallback = math.min(priorityQuality.cardsPerReroll, count) local selectedCards = self:DrawRandomFromSubsetRespectingWeights(fallbackCards, takeFromFallback, excludedIds, strictExcludeIds) local remainingCards = self:DrawRandomCardIdsRespectingPoolWeights(count - #selectedCards, excludedIds, strictExcludeIds) local ____array_45 = __TS__SparseArrayNew(unpack(selectedCards)) __TS__SparseArrayPush( ____array_45, unpack(remainingCards) ) local result = {__TS__SparseArraySpread(____array_45)} priorityQuality.rerollsLeft = priorityQuality.rerollsLeft - 1 print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем " .. tostring(#result)) .. " карт (replace)") return result end end print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Нет доступных карт для замены, используем обычный выбор") replaceResult = self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем " .. tostring(#replaceResult)) .. " случайных карт (replace fallback)") return replaceResult end do print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Неизвестное поведение, используем обычный выбор") defaultResult = self:DrawRandomCardIdsRespectingPoolWeights(count, excludedIds, strictExcludeIds) print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] Возвращаем " .. tostring(#defaultResult)) .. " случайных карт (default)") return defaultResult end until true end local fromQualityCount = math.min(priorityQuality.cardsPerReroll, count) local fromQuality = self:DrawRandomFromSubsetRespectingWeights(qualityCards, fromQualityCount, excludedIds, strictExcludeIds) local restCount = count - #fromQuality local rest = restCount > 0 and self:DrawRandomCardIdsRespectingPoolWeights(restCount, excludedIds, strictExcludeIds) or ({}) local ____array_46 = __TS__SparseArrayNew(unpack(fromQuality)) __TS__SparseArrayPush( ____array_46, unpack(rest) ) local guaranteedCards = {__TS__SparseArraySpread(____array_46)} do local i = #guaranteedCards - 1 while i > 0 do local j = math.floor(math.random() * (i + 1)) local ____temp_47 = {guaranteedCards[j + 1], guaranteedCards[i + 1]} guaranteedCards[i + 1] = ____temp_47[1] guaranteedCards[j + 1] = ____temp_47[2] i = i - 1 end end print("[CardSystem.GetRerollCardsWithGuaranteedQuality] Итого возвращаем карт: " .. tostring(#guaranteedCards)) print(("[CardSystem.GetRerollCardsWithGuaranteedQuality] ID карт: [" .. table.concat(guaranteedCards, ", ")) .. "]") print("[CardSystem.GetRerollCardsWithGuaranteedQuality] ================================") return guaranteedCards end function CardSystem.prototype.OnChooseCard(self, playerId, index, selectionToken) local player = PlayerResource:GetPlayer(playerId) local hero = player and player:GetAssignedHero() if not hero or not IsValidEntity(hero) or not hero:IsAlive() then return end local normalizedToken = math.floor(__TS__Number(selectionToken)) if __TS__NumberIsFinite(normalizedToken) and normalizedToken > 0 and normalizedToken ~= self.currentSelectionToken then print((((("[CardSystem.OnChooseCard] Игнорируем устаревший выбор: player=" .. tostring(playerId)) .. ", token=") .. tostring(normalizedToken)) .. ", current=") .. tostring(self.currentSelectionToken)) return end local selected = self.playerItems[tostring(index)] if not selected then return end local cardId = selected.index local selectedEntryId = selected.entry_id local card68ExcludeOptionIds if cardId == 68 then card68ExcludeOptionIds = {} for key in pairs(self.playerItems) do local option = self.playerItems[key] if option and option.index ~= nil and option.index ~= 404 and not isSlagCardId(nil, option.index) then card68ExcludeOptionIds[#card68ExcludeOptionIds + 1] = option.index end end end if isSlagCardId(nil, cardId) then if selectedEntryId then self:ConsumePoolEntryWeight(selectedEntryId, 1) else self:ConsumePoolEntryWeight( self:PickPoolEntryIdForCardId(cardId), 1 ) end self:RebuildPoolFromEntries() self:SyncPool() self:registerTakenCardChoice() self.playerItems = {} self:SyncPlayerItems() self.isShowingCardSelection = false self:ProcessNextCardSelection() return end if cardId == 404 then self:registerTakenCardChoice() self.playerItems = {} self:SyncPlayerItems() self:SyncPool() self.isShowingCardSelection = false self:ProcessNextCardSelection() return end if selectedEntryId then self:ConsumePoolEntryWeight(selectedEntryId, 1) else local fallbackEntryId = self:PickPoolEntryIdForCardId(cardId) self:ConsumePoolEntryWeight(fallbackEntryId, 1) end self:RebuildPoolFromEntries() self:SyncPool() self:registerTakenCardChoice() local optionChain = __TS__ArrayIsArray(selected.source_chain_items) and selected.source_chain_items or self.currentSelectionSourceChain local pickChain = self:BuildSourceChain( "selected_card_" .. tostring(cardId), optionChain ) self:AddCard( tostring(cardId), pickChain ) if self.repeatNextSelectedCardOnce then self.repeatNextSelectedCardOnce = false if not isCard79RepeatBlockedCardId(nil, cardId) then local repeatChain = self:BuildSourceChain( "repeat_selected_card_" .. tostring(cardId), pickChain ) self:AddCard( tostring(cardId), repeatChain ) end end self.playerItems = {} self:SyncPlayerItems() self:SyncPool() self.isShowingCardSelection = false self:ProcessNextCardSelection() if card68ExcludeOptionIds ~= nil then self:ScheduleCard68CursedPick(card68ExcludeOptionIds) end end function CardSystem.prototype.registerTakenCardChoice(self) self.cardsTaken = self.cardsTaken + 1 self:SyncRemainingCards() local taken = self.cardsTaken if taken % 10 == 0 or taken % 15 == 0 or taken % 20 == 0 then self:CreatePoolEntry(404, 1, "threshold_empty_card_bonus") self:RebuildPoolFromEntries() self:SyncPool() self:SyncRemainingCards() end end function CardSystem.prototype.RerollCards(self, playerId) if not self.playerItems or #__TS__ObjectKeys(self.playerItems) == 0 then return end if self.rerollCost > 0 then local useCard49Free = self:HasActiveCard(49) and self.card49FreeRerollCharges > 0 local useCard6Free = not useCard49Free and self:GetCard6FreeRerollChargesForUse() > 0 local useCard88Free = not useCard49Free and not useCard6Free and self:GetCard88FreeRerollChargesForUse() > 0 if useCard49Free then self.card49FreeRerollCharges = self.card49FreeRerollCharges - 1 elseif useCard6Free then self.card6FreeRerollCharges = self.card6FreeRerollCharges - 1 elseif useCard88Free then self.card88FreeRerollCharges = self.card88FreeRerollCharges - 1 else local crystalSystem = CrystalCurrency:getInstance() local currentCrystals = crystalSystem:getCrystals(self.playerId) if currentCrystals < self.rerollCost then return end local success = crystalSystem:removeCrystals(self.playerId, self.rerollCost) if not success then return end end end if self.rerollCost < 50 then self.rerollCost = self.rerollCost + 5 end self:SyncRerollCost() self:SyncRemainingCards() local count = #__TS__ObjectKeys(self.playerItems) self:ProcessCardSelection(count, "reroll") end function CardSystem.prototype.AddCard(self, cardId, sourceChain, options) local skipCurseStack = (options and options.skipCurseStack) == true Timers:CreateTimer( 0.1, function() local player = self:GetPlayer() if not player then return nil end local hero = player:GetAssignedHero() if not hero or not hero:IsAlive() then return nil end local className = "card_" .. cardId local CardClass = ____exports.CardSystem.cardTypes[className] if not CardClass then return nil end local parsedId = __TS__ParseInt(cardId) local previousEffectContext = self.effectSourceChainContext self.effectSourceChainContext = sourceChain and #sourceChain > 0 and ({unpack(sourceChain)}) or previousEffectContext if not skipCurseStack and hero:HasModifier("modifier_card_69") then addCursedStack(nil, hero) end local ____self_itemsId_52 = self.itemsId ____self_itemsId_52[#____self_itemsId_52 + 1] = parsedId invalidateStatsMultiplierSumCache(nil, hero) do pcall(function() local card80 = require("cards.examples.card_80") local ____opt_53 = card80.notifyCard80PlayerTookCard if ____opt_53 ~= nil then ____opt_53(card80, hero, parsedId) end end) end local card = __TS__New(CardClass, hero, cardId) if isGreedPoolCardId(nil, parsedId) and card:GetModifierName() == nil then registerHiddenGreedCard(nil, hero, parsedId) end if not card:IsHidden() then local ____self_items_55 = self.items ____self_items_55[#____self_items_55 + 1] = card self:SyncActiveCards() else table.remove(self.itemsId) invalidateStatsMultiplierSumCache(nil, hero) end if isGreedPoolCardId(nil, parsedId) then updateGreedForHero(nil, hero, self) end self.effectSourceChainContext = previousEffectContext return nil end ) end function CardSystem.prototype.CraftOrUpgradeCard(self, cardId, _allowDebris) local normalizedCardId = math.floor(__TS__Number(cardId)) if not __TS__NumberIsFinite(normalizedCardId) or normalizedCardId <= 0 then self:sendCardUpgradeResult(false, "Некорректный id карты") return end local cardData = ____exports.CardSystem.cardData[normalizedCardId] if not cardData or cardData.disabled == true then self:sendCardUpgradeResult(false, "Карта недоступна для улучшения") return end if cardData.canupgrade == false then self:sendCardUpgradeResult(false, "Эту карту нельзя улучшать") return end local key = tostring(normalizedCardId) local currentState = self.cardPieces[key] or ({num = 0, level = 1}) local currentLevel = math.max( 1, math.floor(__TS__Number(currentState.level or 1)) ) if currentLevel >= CARD_UPGRADE_MAX_LEVEL then self:sendCardUpgradeResult( false, "Максимальный уровень: " .. tostring(CARD_UPGRADE_MAX_LEVEL) ) return end local nextLevel = currentLevel + 1 local upgradeCost = self:getCardUpgradeCost(cardData.quality, currentLevel) if upgradeCost <= 0 then self:sendCardUpgradeResult(false, "Ошибка стоимости улучшения") return end local storeModule = require("store_manager") local ____opt_58 = storeModule.StoreManager local ____opt_56 = ____opt_58 and ____opt_58.getInstance local store = ____opt_56 and ____opt_56(____opt_58) if not store then self:sendCardUpgradeResult(false, "Магазин недоступен") return end local isDefaultCard = cardData.default == true local hasPurchasedCardById = store.hasPurchasedCardById local ____hasPurchasedCardById_60 if hasPurchasedCardById then ____hasPurchasedCardById_60 = store:hasPurchasedCardById(self.playerId, normalizedCardId) == true else ____hasPurchasedCardById_60 = false end local hasPurchasedCard = ____hasPurchasedCardById_60 local unlockCardId = math.floor(__TS__Number(cardData.deck_builder_unlock_card_id or 0)) if cardData.deck_builder_non_deckable == true and unlockCardId > 0 then local ____hasPurchasedCardById_61 if hasPurchasedCardById then ____hasPurchasedCardById_61 = store:hasPurchasedCardById(self.playerId, unlockCardId) == true else ____hasPurchasedCardById_61 = false end hasPurchasedCard = ____hasPurchasedCardById_61 end if not isDefaultCard and not hasPurchasedCard then self:sendCardUpgradeResult( false, unlockCardId > 0 and cardData.deck_builder_non_deckable == true and getDeckBuilderUnlockRequiredMessage(nil, unlockCardId) or "Нельзя улучшить некупленную карту" ) return end local currentDustCurrency = store:getDustCurrency(self.playerId) if currentDustCurrency < upgradeCost then self:sendCardUpgradeResult( false, "Недостаточно пыли: нужно " .. tostring(upgradeCost) ) return end local removed = store:removeDustCurrency(self.playerId, upgradeCost) if not removed then self:sendCardUpgradeResult(false, "Не удалось списать пыль") return end local ____this_63 ____this_63 = store local ____opt_62 = ____this_63.saveCurrencyToServer if ____opt_62 ~= nil then ____opt_62(____this_63, self.playerId) end self.cardPieces[key] = __TS__ObjectAssign({}, currentState, {level = nextLevel}) self:SyncCardPieces() self:RefreshActiveCardModifiersById(normalizedCardId) if normalizedCardId == ____exports.CardSystem.CARD_6_ID then self:TryGrantCard6Level2FreeRerolls() end self:saveCardLevelsToServer() self:sendCardUpgradeResult( true, ("Карта улучшена до " .. tostring(nextLevel)) .. " уровня", normalizedCardId, nextLevel, upgradeCost ) end function CardSystem.prototype.getCardUpgradeCost(self, quality, currentLevel) return ____exports.CardSystem:getCardUpgradeDustCost(quality, currentLevel) end function CardSystem.getCardUpgradeDustCost(self, quality, currentLevel) local baseCost = ____exports.CardSystem.CARD_UPGRADE_BASE_COST_BY_QUALITY[quality] or 2000 return math.max( 1, math.floor(baseCost * math.max(1, currentLevel)) ) end function CardSystem.getDuplicateCardDustCompensation(self, quality, cardLevel) local maxLevel = CARD_UPGRADE_MAX_LEVEL local levelForCost = math.max( 1, math.floor(cardLevel) ) if levelForCost >= maxLevel then levelForCost = maxLevel - 1 end return ____exports.CardSystem:getCardUpgradeDustCost(quality, levelForCost) end function CardSystem.prototype.sendCardUpgradeResult(self, success, message, cardId, newLevel, spentFreeCurrency) local player = self:GetPlayer() if not player then return end CustomGameEventManager:Send_ServerToPlayer(player, "card_upgrade_result", { success = success and 1 or 0, message = message, card_id = cardId, new_level = newLevel, spent_free_currency = spentFreeCurrency, spent_dust_currency = spentFreeCurrency }) end function CardSystem.prototype.RefreshActiveCardModifiersById(self, cardId) for ____, card in ipairs(self.items) do do local cardUnsafe = card local activeCardId = __TS__Number(cardUnsafe.cardKey or "0") if not __TS__NumberIsFinite(activeCardId) or activeCardId ~= cardId then goto __continue451 end if type(cardUnsafe.ModifierRefresh) == "function" then cardUnsafe:ModifierRefresh() end end ::__continue451:: end end function CardSystem.prototype.buildCardLevelsSyncPayload(self) local payload = {} for ____, ____value in ipairs(__TS__ObjectEntries(____exports.CardSystem.cardData)) do local idRaw = ____value[1] local cardData = ____value[2] do local id = __TS__Number(idRaw) if not __TS__NumberIsFinite(id) or id <= 0 or id == 404 or cardData.disabled == true then goto __continue456 end local key = tostring(id) local ____math_max_67 = math.max local ____math_floor_66 = math.floor local ____opt_64 = self.cardPieces[key] local level = ____math_max_67( 1, ____math_floor_66(__TS__Number(____opt_64 and ____opt_64.level or 1)) ) local canUpgradeByRule = cardData.canupgrade ~= false local canUpgrade = canUpgradeByRule and level < CARD_UPGRADE_MAX_LEVEL payload[key] = { level = level, max_level = CARD_UPGRADE_MAX_LEVEL, next_upgrade_cost = canUpgrade and self:getCardUpgradeCost(cardData.quality, level) or 0, can_upgrade = canUpgradeByRule and 1 or 0 } end ::__continue456:: end return payload end function CardSystem.prototype.SyncCardPieces(self) CustomNetTables:SetTableValue( "cards", "card_pieces_" .. tostring(self.playerId), self.cardPieces ) CustomNetTables:SetTableValue( "cards", "card_levels_" .. tostring(self.playerId), self:buildCardLevelsSyncPayload() ) end function CardSystem.prototype.buildPersistedCardLevelsPayload(self) local payload = {} for ____, ____value in ipairs(__TS__ObjectEntries(____exports.CardSystem.cardData)) do local idRaw = ____value[1] local cardData = ____value[2] do local id = __TS__Number(idRaw) if not __TS__NumberIsFinite(id) or id <= 0 or id == 404 or cardData.disabled == true then goto __continue461 end local key = tostring(id) local ____math_max_71 = math.max local ____math_floor_70 = math.floor local ____opt_68 = self.cardPieces[key] local level = ____math_max_71( 1, ____math_floor_70(__TS__Number(____opt_68 and ____opt_68.level or 1)) ) payload[key] = level end ::__continue461:: end return payload end function CardSystem.prototype.saveCardLevelsToServer(self) local steamId = PlayerResource:GetSteamAccountID(self.playerId) if not steamId then return end local request = CreateHTTPRequestScriptVM( "PUT", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/card-levels" ) setApiHeaders(nil, request) request:SetHTTPRequestRawPostBody( "application/json", json.encode({card_levels = self:buildPersistedCardLevelsPayload()}) ) request:Send(function(_result) end) end function CardSystem.prototype.loadCardLevelsFromServer(self) local steamId = PlayerResource:GetSteamAccountID(self.playerId) if not steamId then self.cardLevelsLoaded = true return end local request = CreateHTTPRequestScriptVM( "GET", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/card-levels" ) setApiHeaders(nil, request) request:Send(function(result) if result.StatusCode < 200 or result.StatusCode >= 300 then self.cardLevelsLoaded = true return end do local function ____catch(_error) self:SyncCardPieces() self.cardLevelsLoaded = true end local ____try, ____hasReturned, ____returnValue = pcall(function() local decoded = {json.decode(result.Body)} local ____temp_72 if __TS__ArrayIsArray(decoded) and #decoded > 0 then ____temp_72 = decoded[1] else ____temp_72 = decoded end local responseData = ____temp_72 local ____opt_result_75 if responseData ~= nil then ____opt_result_75 = responseData.card_levels end local cardLevels = ____opt_result_75 if not cardLevels or type(cardLevels) ~= "table" then self:SyncCardPieces() self.cardLevelsLoaded = true return true end for ____, ____value in ipairs(__TS__ObjectEntries(cardLevels)) do local cardIdRaw = ____value[1] local levelRaw = ____value[2] do local cardIdNum = __TS__Number(cardIdRaw) local levelNum = __TS__Number(levelRaw) if not __TS__NumberIsFinite(cardIdNum) or cardIdNum <= 0 or not __TS__NumberIsFinite(levelNum) then goto __continue473 end local cardId = math.floor(cardIdNum) local level = math.max( 1, math.min( CARD_UPGRADE_MAX_LEVEL, math.floor(levelNum) ) ) local key = tostring(cardId) local current = self.cardPieces[key] or ({num = 0, level = 1}) self.cardPieces[key] = __TS__ObjectAssign({}, current, {level = level}) end ::__continue473:: end self:SyncCardPieces() self.cardLevelsLoaded = true end) if not ____try then ____hasReturned, ____returnValue = ____catch(____hasReturned) end if ____hasReturned then return ____returnValue end end end) end function CardSystem.prototype.CreateNewDeck(self, name) local player = self:GetPlayer() if not player then return end do local i = 1 while i <= 10 do if not self.decks[tostring(i)] then self.decks[tostring(i)] = {name = name, cards = {}} self:SaveDeckToServer(i, {}, name) self:SyncDecks() CustomGameEventManager:Send_ServerToPlayer(player, "deck_selected", {index = i}) return end i = i + 1 end end end function CardSystem.prototype.SaveDeck(self, index, cards, deckName) if not cards then return end local cardsArray if __TS__ArrayIsArray(cards) then cardsArray = cards elseif type(cards) == "table" and cards ~= nil then local keys = __TS__ArraySort( __TS__ObjectKeys(cards), function(____, a, b) return __TS__ParseInt(a) - __TS__ParseInt(b) end ) cardsArray = __TS__ArrayFilter( __TS__ArrayMap( keys, function(____, key) return cards[key] end ), function(____, card) return type(card) == "number" end ) else return end local normalizedCards = self:normalizeDeckCards(cardsArray) local finalDeckName = self:resolveDeckName(deckName, index) self.decks[tostring(index)] = {name = finalDeckName, cards = normalizedCards} self:SyncDecks() self:SaveDeckToServer(index, normalizedCards, finalDeckName) if index == self.activeDeckIndex then self:ReinitCardPool() end end function CardSystem.prototype.SaveDeckToServer(self, index, cards, deckName) local player = self:GetPlayer() if not player then return end local steamId = PlayerResource:GetSteamAccountID(self.playerId) if not steamId then return end local finalDeckName if deckName and __TS__StringTrim(deckName) ~= "" then finalDeckName = __TS__StringTrim(deckName) else local deckData = self.decks[tostring(index)] if deckData and type(deckData) == "table" and deckData.name ~= nil then finalDeckName = deckData.name or "Колода " .. tostring(index) else finalDeckName = "Колода " .. tostring(index) end end local request = CreateHTTPRequestScriptVM( "PUT", (((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/decks/") .. tostring(index) ) setApiHeaders(nil, request) local dataToSend = {name = finalDeckName, cards = cards, active = index == self.activeDeckIndex} request:SetHTTPRequestRawPostBody( "application/json", json.encode(dataToSend) ) request:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then else end end) end function CardSystem.prototype.DeleteDeck(self, index) __TS__Delete( self.decks, tostring(index) ) self:SyncDecks() self:DeleteDeckFromServer(index) end function CardSystem.prototype.DeleteDeckFromServer(self, index) local player = self:GetPlayer() if not player then return end local steamId = PlayerResource:GetSteamAccountID(self.playerId) if not steamId then return end local request = CreateHTTPRequestScriptVM( "DELETE", (((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/decks/") .. tostring(index) ) setApiHeaders(nil, request) request:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then else end end) end function CardSystem.prototype.ActivateDeck(self, index) self.activeDeckIndex = index self:SyncDecks() local player = self:GetPlayer() if player then CustomGameEventManager:Send_ServerToPlayer(player, "deck_selected", {index = index}) end self:UpdateActiveDeckOnServer() self:ReinitCardPool() end function CardSystem.prototype.UpdateActiveDeckOnServer(self) local player = self:GetPlayer() if not player then return end local steamId = PlayerResource:GetSteamAccountID(self.playerId) if not steamId then return end for ____, ____value in ipairs(__TS__ObjectEntries(self.decks)) do local deckIndex = ____value[1] local deckData = ____value[2] local index = __TS__ParseInt(deckIndex) local isActive = index == self.activeDeckIndex local deckName local deckCards if __TS__ArrayIsArray(deckData) then deckName = deckData[1] deckCards = __TS__ArraySlice(deckData, 1) elseif deckData and type(deckData) == "table" and deckData.name ~= nil then deckName = deckData.name or "Колода " .. tostring(index) deckCards = deckData.cards or ({}) else local keys = __TS__ArraySort( __TS__ObjectKeys(deckData), function(____, a, b) return __TS__ParseInt(a) - __TS__ParseInt(b) end ) deckName = deckData[keys[1]] or "Колода " .. tostring(index) deckCards = __TS__ArrayFilter( __TS__ArrayMap( __TS__ArraySlice(keys, 1), function(____, key) return deckData[key] end ), function(____, card) return type(card) == "number" end ) end local request = CreateHTTPRequestScriptVM( "PUT", (((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/decks/") .. tostring(index) ) setApiHeaders(nil, request) request:SetHTTPRequestHeaderValue("Content-Type", "application/json") request:SetHTTPRequestNetworkActivityTimeout(10) request:SetHTTPRequestAbsoluteTimeoutMS(10000) local dataToSend = {name = deckName, cards = deckCards, active = isActive} request:SetHTTPRequestRawPostBody( "application/json", json.encode(dataToSend) ) request:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then else end end) end end function CardSystem.prototype.LoadDecks(self) local player = self:GetPlayer() if not player then return end local steamId = PlayerResource:GetSteamAccountID(self.playerId) if not steamId then return end local request = CreateHTTPRequestScriptVM( "GET", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/decks" ) setApiHeaders(nil, request) request:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then do pcall(function() local data = {json.decode(result.Body)} if data then for key in pairs(data) do end else end local success = true local decks = data and data[1] local activeDeckIndex = 0 if data and success and decks then self.decks = decks.decks or decks self.activeDeckIndex = decks.activeDeckIndex or activeDeckIndex or 0 self:normalizeAllDecks() for key in pairs(self.decks) do end for ____, ____value in ipairs(__TS__ObjectEntries(self.decks)) do local deckIndex = ____value[1] local deckData = ____value[2] do if type(deckData) == "boolean" then goto __continue536 end if type(deckData) == "number" then goto __continue536 end local isLegacy = __TS__ArrayIsArray(deckData) local deckName = isLegacy and deckData[1] or deckData.name local cardCount = isLegacy and #deckData - 1 or (deckData.cards ~= nil and deckData.cards ~= nil and #deckData.cards or 0) end ::__continue536:: end self:SyncDecks() else self:SyncDecks() end end) end else end end) end function CardSystem.prototype.GetPlayer(self) return PlayerResource:GetPlayer(self.playerId) end function CardSystem.prototype.resolveDeckName(self, rawName, index) if type(rawName) == "string" and __TS__StringTrim(rawName) ~= "" then return __TS__StringTrim(rawName) end local existingDeck = self.decks[tostring(index)] if existingDeck ~= nil and existingDeck ~= nil then if __TS__ArrayIsArray(existingDeck) then local legacyName = __TS__StringTrim(tostring(existingDeck[1] or "")) if legacyName ~= "" then return legacyName end elseif type(existingDeck) == "table" and existingDeck.name ~= nil then local existingName = __TS__StringTrim(tostring(existingDeck.name or "")) if existingName ~= "" then return existingName end end end return "Колода " .. tostring(index) end function CardSystem.prototype.getDeckCardsList(self, deckData) if not deckData then return {} end if __TS__ArrayIsArray(deckData) then return __TS__ArrayMap( __TS__ArrayFilter( __TS__ArrayMap( __TS__ArraySlice(deckData, 1), function(____, card) return __TS__Number(card) end ), function(____, card) return __TS__NumberIsFinite(card) and card > 0 end ), function(____, card) return math.floor(card) end ) end local cards = deckData.cards if __TS__ArrayIsArray(cards) then return __TS__ArrayMap( __TS__ArrayFilter( __TS__ArrayMap( cards, function(____, card) return __TS__Number(card) end ), function(____, card) return __TS__NumberIsFinite(card) and card > 0 end ), function(____, card) return math.floor(card) end ) end if cards and type(cards) == "table" then return __TS__ArrayMap( __TS__ArrayFilter( __TS__ArrayMap( __TS__ObjectValues(cards), function(____, card) return __TS__Number(card) end ), function(____, card) return __TS__NumberIsFinite(card) and card > 0 end ), function(____, card) return math.floor(card) end ) end return {} end function CardSystem.prototype.getDefaultDeckSeed(self) local seed = {} local seen = __TS__New(Set) local function pushIfValid(____, rawId) local id = math.floor(__TS__Number(rawId)) if not __TS__NumberIsFinite(id) or id <= 0 or seen:has(id) then return end local cardData = ____exports.CardSystem.cardData[id] if not cardData or cardData.disabled == true then return end seen:add(id) seed[#seed + 1] = id end for ____, cardId in ipairs(DEFAULT_DECK_CARD_IDS) do pushIfValid(nil, cardId) end if #seed > 0 then return seed end for ____, ____value in ipairs(__TS__ObjectEntries(____exports.CardSystem.cardData)) do local cardIdRaw = ____value[1] local cardData = ____value[2] do if not cardData or cardData.disabled == true then goto __continue571 end pushIfValid( nil, __TS__Number(cardIdRaw) ) end ::__continue571:: end return seed end function CardSystem.prototype.normalizeDeckCards(self, rawDeck) local requiredSize = DECK_BUILDER_SLOT_CAPACITY local normalized = {} local copiesByCardId = {} local sourceCards = __TS__ArrayIsArray(rawDeck) and rawDeck or self:getDeckCardsList(rawDeck) local function getCardMaxCopiesForDeck(____, cardData) local configured = __TS__Number(cardData.max_copies) if __TS__NumberIsFinite(configured) and configured > 0 then return math.floor(configured) end if cardData.quality >= ____exports.CardQuality.LEGENDARY then return 1 end return 2 end local function getCardMaxCopiesForDeckById(____, cardId) local cardData = ____exports.CardSystem.cardData[cardId] if not cardData then return 2 end return getCardMaxCopiesForDeck(nil, cardData) end for ____, rawId in ipairs(sourceCards) do do local id = math.floor(__TS__Number(rawId)) if not __TS__NumberIsFinite(id) or id <= 0 then goto __continue580 end local cardData = ____exports.CardSystem.cardData[id] if not cardData then if not ____exports.CardSystem:canAddCardToDeckSlots(normalized, id, requiredSize) then goto __continue580 end normalized[#normalized + 1] = id copiesByCardId[id] = (copiesByCardId[id] or 0) + 1 if ____exports.CardSystem:getDeckUsedSlots(normalized) >= requiredSize then break end goto __continue580 end if cardData.disabled == true then goto __continue580 end if cardData.deck_builder_non_deckable == true then goto __continue580 end local maxCopies = getCardMaxCopiesForDeck(nil, cardData) local currentCopies = copiesByCardId[id] or 0 if currentCopies >= maxCopies then goto __continue580 end if not ____exports.CardSystem:canAddCardToDeckSlots(normalized, id, requiredSize) then goto __continue580 end normalized[#normalized + 1] = id copiesByCardId[id] = currentCopies + 1 if ____exports.CardSystem:getDeckUsedSlots(normalized) >= requiredSize then break end end ::__continue580:: end if ____exports.CardSystem:getDeckUsedSlots(normalized) >= requiredSize then return normalized end local seed = self:getDefaultDeckSeed() if #seed <= 0 then local emergencySeed = __TS__ArrayFilter( __TS__ArrayFrom(__TS__New(Set, normalized)), function(____, id) return __TS__NumberIsFinite(id) and id > 0 end ) if #emergencySeed > 0 then seed = emergencySeed else local sourceSeed = __TS__ArrayFilter( __TS__ArrayMap( __TS__ArrayFrom(__TS__New(Set, sourceCards)), function(____, id) return math.floor(__TS__Number(id)) end ), function(____, id) return __TS__NumberIsFinite(id) and id > 0 end ) if #sourceSeed > 0 then seed = sourceSeed else return normalized end end end while ____exports.CardSystem:getDeckUsedSlots(normalized) < requiredSize do local addedInPass = 0 for ____, nextId in ipairs(seed) do do local cardData = ____exports.CardSystem.cardData[nextId] if cardData and cardData.disabled == true then goto __continue601 end if cardData and cardData.deck_builder_non_deckable == true then goto __continue601 end if (copiesByCardId[nextId] or 0) > 0 then goto __continue601 end local maxCopies = getCardMaxCopiesForDeckById(nil, nextId) local currentCopies = copiesByCardId[nextId] or 0 if currentCopies >= maxCopies then goto __continue601 end if not ____exports.CardSystem:canAddCardToDeckSlots(normalized, nextId, requiredSize) then goto __continue601 end normalized[#normalized + 1] = nextId copiesByCardId[nextId] = currentCopies + 1 addedInPass = addedInPass + 1 if ____exports.CardSystem:getDeckUsedSlots(normalized) >= requiredSize then break end end ::__continue601:: end if addedInPass <= 0 then break end end if ____exports.CardSystem:getDeckUsedSlots(normalized) < requiredSize and #seed > 0 then while ____exports.CardSystem:getDeckUsedSlots(normalized) < requiredSize do local addedInPass = 0 for ____, nextId in ipairs(seed) do do local cardData = ____exports.CardSystem.cardData[nextId] if cardData and cardData.disabled == true then goto __continue612 end if cardData and cardData.deck_builder_non_deckable == true then goto __continue612 end local maxCopies = getCardMaxCopiesForDeckById(nil, nextId) local currentCopies = copiesByCardId[nextId] or 0 if currentCopies >= maxCopies then goto __continue612 end if not ____exports.CardSystem:canAddCardToDeckSlots(normalized, nextId, requiredSize) then goto __continue612 end normalized[#normalized + 1] = nextId copiesByCardId[nextId] = currentCopies + 1 addedInPass = addedInPass + 1 if ____exports.CardSystem:getDeckUsedSlots(normalized) >= requiredSize then break end end ::__continue612:: end if addedInPass <= 0 then break end end end return normalized end function CardSystem.prototype.normalizeAllDecks(self) local normalizedDecks = {} for ____, ____value in ipairs(__TS__ObjectEntries(self.decks)) do local deckIndexRaw = ____value[1] local deckData = ____value[2] do local index = __TS__Number(deckIndexRaw) if not __TS__NumberIsFinite(index) or index <= 0 then goto __continue621 end normalizedDecks[deckIndexRaw] = { name = self:resolveDeckName( deckData, math.floor(index) ), cards = self:normalizeDeckCards(deckData) } end ::__continue621:: end self.decks = normalizedDecks end function CardSystem.prototype.SyncDecks(self) for ____, ____value in ipairs(__TS__ObjectEntries(self.decks)) do local deckIndex = ____value[1] local deckData = ____value[2] do if not deckData then goto __continue625 end local isLegacy = __TS__ArrayIsArray(deckData) local deckName local cardCount if isLegacy then deckName = deckData[1] cardCount = #deckData - 1 else deckName = deckData.name or "Колода " .. deckIndex local cards = deckData.cards cardCount = cards ~= nil and cards ~= nil and __TS__ArrayIsArray(cards) and #cards or 0 end end ::__continue625:: end CustomNetTables:SetTableValue( "cards", "decks_" .. tostring(self.playerId), self.decks ) CustomNetTables:SetTableValue( "cards", "active_deck_" .. tostring(self.playerId), {index = self.activeDeckIndex} ) self:SyncCardPieces() self:CaptureRuntimeState() end function CardSystem.prototype.SyncPlayerItems(self) CustomNetTables:SetTableValue( "cards", "selection_" .. tostring(self.playerId), self.playerItems ) end function CardSystem.prototype.SyncSelectionQueue(self) local data = {size = #self.cardSelectionQueue, showing = self.isShowingCardSelection} CustomNetTables:SetTableValue( "cards", "selection_queue_" .. tostring(self.playerId), data ) end function CardSystem.prototype.SyncSelectionSource(self) CustomNetTables:SetTableValue( "cards", "selection_source_" .. tostring(self.playerId), { source = self.currentSelectionSource, chain = self:SerializeSourceChain(self.currentSelectionSourceChain), chain_items = self.currentSelectionSourceChain, selection_token = self.currentSelectionToken } ) end function CardSystem.prototype.SyncPoolSourceChains(self) local data = {} for entryId in pairs(self.poolEntriesById) do do local entry = self.poolEntriesById[entryId] local chain = entry and entry.sourceChain or ({}) if #chain <= 0 then goto __continue634 end data[entryId] = { index = entry.cardId, source = chain[#chain] or "unknown", chain = self:SerializeSourceChain(chain), chain_items = chain } end ::__continue634:: end CustomNetTables:SetTableValue( "cards", "pool_source_" .. tostring(self.playerId), data ) end function CardSystem.prototype.SyncActiveCards(self) CustomNetTables:SetTableValue( "cards", "active_cards_" .. tostring(self.playerId), self.itemsId ) self:CaptureRuntimeState() end function CardSystem.prototype.SyncPool(self) local poolData = {} for entryId in pairs(self.poolEntriesById) do do local entry = self.poolEntriesById[entryId] if not entry or entry.weight <= 0 then goto __continue639 end poolData[#poolData + 1] = {entry_id = entry.entryId, index = entry.cardId, weight = entry.weight} end ::__continue639:: end CustomNetTables:SetTableValue( "cards", "pool_" .. tostring(self.playerId), poolData ) self:SyncPoolSourceChains() self:SyncRemainingCards() self:CaptureRuntimeState() end function CardSystem.prototype.SyncRemainingCards(self) local remainingCount = self.pool ~= nil and self.pool.totalProportion or 0 CustomNetTables:SetTableValue( "cards", "remaining_cards_" .. tostring(self.playerId), {count = remainingCount} ) end function CardSystem.prototype.SyncCardData(self) if ____exports.CardSystem.clientCardCatalogSynced then return end ____exports.CardSystem.clientCardCatalogSynced = true local function serializeValuesForNetTable(____, values) if not values then return nil end local out = {} for ____, ____value in ipairs(__TS__ObjectEntries(values)) do local key = ____value[1] local raw = ____value[2] do if raw == nil or raw == nil then goto __continue647 end if type(raw) == "table" then local leveled = {} local hasLevel = false local tableRaw = raw do local i = 1 while i <= 32 do local ____tableRaw_i_78 = tableRaw[i] if ____tableRaw_i_78 == nil then ____tableRaw_i_78 = tableRaw[tostring(i)] end local v = __TS__Number(____tableRaw_i_78) if __TS__NumberIsFinite(v) then leveled[tostring(i)] = v hasLevel = true end i = i + 1 end end if hasLevel then out[key] = leveled goto __continue647 end end local numeric = __TS__Number(raw) if __TS__NumberIsFinite(numeric) then out[key] = numeric end end ::__continue647:: end return out end local cardDataForClient = {} for cardId in pairs(____exports.CardSystem.cardData) do local card = ____exports.CardSystem.cardData[cardId] cardDataForClient[cardId] = __TS__ObjectAssign( {}, card, { values = serializeValuesForNetTable(nil, card.values), disabled = card.disabled == true and "true" or "false", inherent = card.inherent == true and "true" or "false", default = card.default == true and "true" or "false", purchasable = card.purchasable == false and "false" or "true", obtainable = card.obtainable == false and "false" or "true", deck_builder_non_deckable = card.deck_builder_non_deckable == true and "true" or "false", deck_builder_unlock_card_id = card.deck_builder_unlock_card_id } ) end CustomNetTables:SetTableValue("cards", "card_data", cardDataForClient) for cardId in pairs(____exports.CardSystem.cardData) do local card = ____exports.CardSystem.cardData[cardId] if card.disabled == true then end end end function CardSystem.prototype.SyncRerollCost(self) local key = "reroll_cost_" .. tostring(self.playerId) local card49Charges = self:HasActiveCard(49) and self.card49FreeRerollCharges or 0 local card6Charges = self:GetCard6FreeRerollChargesForUse() local card88Charges = self:GetCard88FreeRerollChargesForUse() local totalFreeCharges = card49Charges + card6Charges + card88Charges local hasFreeReroll = self.rerollCost > 0 and totalFreeCharges > 0 local data = { cost = self.rerollCost, card49_free_reroll = hasFreeReroll and "1" or "0", card49_free_charges = tostring(card49Charges), card6_free_charges = tostring(card6Charges), card88_free_charges = tostring(card88Charges), free_reroll_charges_total = tostring(totalFreeCharges) } CustomNetTables:SetTableValue("cards", key, data) self:CaptureRuntimeState() end function CardSystem.prototype.PushRerollCostToClient(self) self:SyncRerollCost() end function CardSystem.prototype.ResetCard51MorningGold(self) self.card51MorningGoldEarned = 0 self:CaptureRuntimeState() end function CardSystem.prototype.GetCard51MorningGoldEarned(self) return math.max( 0, math.floor(self.card51MorningGoldEarned) ) end function CardSystem.prototype.TryGrantCard51GoldFromDamage(self, hero, goldGain) if not hero or not IsValidEntity(hero) or not hero:IsRealHero() then return 0 end local baseCap = math.max( 0, math.floor(self:getCardValueByLevel(51, "morning_gold_cap", 400)) ) local copies = math.max( 1, self:GetActiveCardCopies(51) ) local cap = baseCap * copies if cap <= 0 or goldGain <= 0 then return 0 end local earned = self:GetCard51MorningGoldEarned() local remaining = math.max(0, cap - earned) local actualGain = math.min(goldGain, remaining) if actualGain <= 0 then return 0 end self.card51MorningGoldEarned = earned + actualGain PlayerResource:ModifyGold(self.playerId, actualGain, true, DOTA_ModifyGold_Unspecified) SendOverheadEventMessage( nil, OVERHEAD_ALERT_GOLD, hero, actualGain, hero:GetPlayerOwner() ) self:CaptureRuntimeState() return actualGain end function CardSystem.prototype.AddSlagToPool(self, weight, count, source, sourceChain) if weight == nil then weight = 1 end if count == nil then count = 1 end if source == nil then source = "card_85_fishing_slag" end local safeWeight = math.max( 1, math.floor(weight) ) local safeCount = math.max( 1, math.floor(count) ) if safeCount <= 1 then self:CreatePoolEntry(SLAG_CARD_ID, safeWeight, source, sourceChain) else self:CreatePoolEntry(SLAG_CARD_ID, safeWeight * safeCount, source, sourceChain) end self:TryGrantCard88FreeRerollsFromSlag(safeCount) self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.removePoolEntriesForCardWithSourceToken(self, cardId, sourceToken) local normalizedCardId = math.floor(__TS__Number(cardId)) local normalizedSource = self:NormalizeSourceToken(sourceToken) if not __TS__NumberIsFinite(normalizedCardId) or normalizedCardId <= 0 or #normalizedSource <= 0 then return end for entryId in pairs(self.poolEntriesById) do do local entry = self.poolEntriesById[entryId] if not entry or entry.cardId ~= normalizedCardId then goto __continue673 end local chain = entry.sourceChain or ({}) local matches = false for ____, token in ipairs(chain) do if self:NormalizeSourceToken(token) == normalizedSource then matches = true break end end if matches then __TS__Delete(self.poolEntriesById, entryId) end end ::__continue673:: end end function CardSystem.prototype.SetPoolCardFromSource(self, cardId, weight, source, sourceChain) if weight == nil then weight = 1 end if source == nil then source = "manual_pool_add" end if isSlagCardId(nil, cardId) then self:AddSlagToPool(weight, 1, source, sourceChain) return end local cardData = ____exports.CardSystem.cardData[cardId] if cardData and (cardData.disabled == true or cardData.obtainable == false) then return end self:removePoolEntriesForCardWithSourceToken(cardId, source) self:CreatePoolEntry( cardId, math.max( 1, math.floor(weight) ), source, sourceChain ) self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.AddCardToPool(self, cardId, weight, source, sourceChain) if weight == nil then weight = 5 end if source == nil then source = "manual_pool_add" end if isSlagCardId(nil, cardId) then self:AddSlagToPool(weight, 1, source, sourceChain) return end local cardData = ____exports.CardSystem.cardData[cardId] if cardData and (cardData.disabled == true or cardData.obtainable == false) then return end self:CreatePoolEntry(cardId, weight, source, sourceChain) self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.AddCardToPoolCount(self, cardId, weight, count, source, sourceChain) if weight == nil then weight = 5 end if count == nil then count = 1 end if source == nil then source = "manual_pool_add" end if isSlagCardId(nil, cardId) then self:AddSlagToPool(weight, count, source, sourceChain) return end local cardData = ____exports.CardSystem.cardData[cardId] if cardData and (cardData.disabled == true or cardData.obtainable == false) then return end local safeCount = math.max( 0, math.floor(count) ) if safeCount <= 1 then self:CreatePoolEntry(cardId, weight, source, sourceChain) else self:CreatePoolEntry(cardId, weight * safeCount, source, sourceChain) end self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.RemoveCardFromPool(self, cardId) for entryId in pairs(self.poolEntriesById) do local entry = self.poolEntriesById[entryId] if entry and entry.cardId == cardId then __TS__Delete(self.poolEntriesById, entryId) end end self:RebuildPoolFromEntries() self:SyncPool() end function CardSystem.prototype.HasCardInPool(self, cardId) for entryId in pairs(self.poolEntriesById) do local entry = self.poolEntriesById[entryId] if entry and entry.cardId == cardId and entry.weight > 0 then return true end end return false end function CardSystem.prototype.GetPoolSize(self) return self.pool.len end function CardSystem.prototype.GetPoolCards(self) local set = __TS__New(Set) for entryId in pairs(self.poolEntriesById) do local entry = self.poolEntriesById[entryId] if entry and entry.weight > 0 then set:add(entry.cardId) end end return __TS__ArrayFrom(set:values()) end function CardSystem.prototype.GetPoolCardWeightSum(self, cardId) local normalizedCardId = math.floor(__TS__Number(cardId)) if not __TS__NumberIsFinite(normalizedCardId) or normalizedCardId <= 0 then return 0 end local total = 0 for entryId in pairs(self.poolEntriesById) do local entry = self.poolEntriesById[entryId] if entry and entry.cardId == normalizedCardId and entry.weight > 0 then total = total + entry.weight end end return total end function CardSystem.prototype.ClearPool(self) self.poolEntriesById = {} self.pool:clear() self:SyncPool() end function CardSystem.prototype.RemoveRandomCardsFromPool(self, count) local removedCards = {} local allCards = self:GetPoolCards() if #allCards == 0 then return removedCards end local cardsToRemove = math.min(count, #allCards) local shuffledCards = {unpack(allCards)} do local i = #shuffledCards - 1 while i > 0 do local j = math.floor(math.random() * (i + 1)) local ____temp_79 = {shuffledCards[j + 1], shuffledCards[i + 1]} shuffledCards[i + 1] = ____temp_79[1] shuffledCards[j + 1] = ____temp_79[2] i = i - 1 end end do local i = 0 while i < cardsToRemove do local cardId = shuffledCards[i + 1] for entryId in pairs(self.poolEntriesById) do local entry = self.poolEntriesById[entryId] if entry and entry.cardId == cardId then __TS__Delete(self.poolEntriesById, entryId) end end removedCards[#removedCards + 1] = cardId i = i + 1 end end self:RebuildPoolFromEntries() self:SyncPool() return removedCards end CardSystem.CARD_49_FREE_STACK_CAP = 99 CardSystem.CARD_6_ID = 6 CardSystem.CARD_6_FREE_STACK_CAP = 99 CardSystem.CARD_88_ID = 88 CardSystem.CARD_88_FREE_STACK_CAP = 99 CardSystem.CARD_UPGRADE_BASE_COST_BY_QUALITY = { [____exports.CardQuality.COMMON] = 500, [____exports.CardQuality.RARE] = 1000, [____exports.CardQuality.EPIC] = 2000, [____exports.CardQuality.LEGENDARY] = 7000, [____exports.CardQuality.MYTHIC] = 15000 } CardSystem.cardTypes = {} CardSystem.cardData = {} CardSystem.clientCardCatalogSynced = false CardSystem.runtimeStateByPlayerId = {} CardSystem.QUALITY_FALLBACK_ORDER = { ____exports.CardQuality.MYTHIC, ____exports.CardQuality.LEGENDARY, ____exports.CardQuality.EPIC, ____exports.CardQuality.RARE, ____exports.CardQuality.COMMON } --- Инициализация Card System для всех игроков function ____exports.InitCardSystem(self) local function initPlayerCardSystem(____, playerId) local player = PlayerResource:GetPlayer(playerId) if player and not player.cardSystem then player.cardSystem = __TS__New(____exports.CardSystem, playerId) end end local function initAllConnectedLobbyPlayers() do local i = 0 while i < DOTA_MAX_PLAYERS do do local playerId = i if not isRealLobbyPlayer(nil, playerId) then goto __continue721 end initPlayerCardSystem(nil, playerId) end ::__continue721:: i = i + 1 end end end CustomGameEventManager:RegisterListener( "init_card_system", function(source, _event) local playerId = source if playerId == nil or playerId < 0 then return end initPlayerCardSystem(nil, playerId) local player = PlayerResource:GetPlayer(playerId) local ____opt_80 = player and player.cardSystem if ____opt_80 ~= nil then ____opt_80:ForceDeckBuilderSync() end end ) CustomGameEventManager:RegisterListener( "request_available_cards", function(source, _event) local playerId = source if playerId == nil or playerId < 0 then return end initPlayerCardSystem(nil, playerId) local player = PlayerResource:GetPlayer(playerId) local ____opt_84 = player and player.cardSystem if ____opt_84 ~= nil then ____opt_84:ForceDeckBuilderSync() end end ) ListenToGameEvent( "npc_spawned", function(event) local unit = EntIndexToHScript(event.entindex) if unit and unit:IsRealHero() then local playerId = unit:GetPlayerOwnerID() if playerId >= 0 then Timers:CreateTimer( 0.1, function() initPlayerCardSystem(nil, playerId) local player = PlayerResource:GetPlayer(playerId) local hero = player and player:GetAssignedHero() if hero and IsValidEntity(hero) and hero:IsRealHero() then updateGreedForHero(nil, hero, player and player.cardSystem) end return nil end ) end end end, nil ) ListenToGameEvent( "game_rules_state_change", function() local state = GameRules:State_Get() if state == DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then initAllConnectedLobbyPlayers(nil) end end, nil ) end --- Утилитарная функция: получить количество взятых карт игрока function ____exports.GetPlayerCardsTaken(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetCardsTaken() end return 0 end --- Сколько карт активно у игрока (в т.ч. стартовая колода) — для масштабирования эффектов вроде карты 5. function ____exports.GetPlayerActiveCardCount(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetActiveCardCount() end return 0 end --- Активные карты игрока без врождённых из колоды. function ____exports.GetPlayerActiveCardCountExcludingInherent(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetActiveCardCountExcludingInherent() end return 0 end --- Добавить карту в пул игрока function ____exports.AddCardToPlayerPool(self, playerId, cardId, weight, count, source) if weight == nil then weight = 5 end if count == nil then count = 1 end if source == nil then source = "manual_pool_add" end local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:AddCardToPoolCount(cardId, weight, count, source) else end end --- Замешать шлак (86) в пул игрока. function ____exports.AddSlagToPlayerPool(self, playerId, weight, count, source) if weight == nil then weight = 1 end if count == nil then count = 1 end if source == nil then source = "card_85_fishing_slag" end local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:AddSlagToPool(weight, count, source) end end --- Удалить карту из пула игрока function ____exports.RemoveCardFromPlayerPool(self, playerId, cardId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:RemoveCardFromPool(cardId) else end end --- Проверить, есть ли карта в пуле игрока function ____exports.HasCardInPlayerPool(self, playerId, cardId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:HasCardInPool(cardId) end return false end --- Получить размер пула игрока function ____exports.GetPlayerPoolSize(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetPoolSize() end return 0 end --- Получить все карты в пуле игрока function ____exports.GetPlayerPoolCards(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetPoolCards() end return {} end --- Очистить пул игрока function ____exports.ClearPlayerPool(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:ClearPool() else end end --- Удалить случайные карты из пула игрока function ____exports.RemoveRandomCardsFromPlayerPool(self, playerId, count) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:RemoveRandomCardsFromPool(count) else return {} end end --- Добавить гарантированные карты определенного качества в реролы для игрока function ____exports.AddPlayerGuaranteedQualityRerolls(self, playerId, quality, rerollsCount, cardsPerReroll, fallbackBehavior) if fallbackBehavior == nil then fallbackBehavior = "skip" end print("[AddPlayerGuaranteedQualityRerolls] ===== ДОБАВЛЕНИЕ ГАРАНТИРОВАННЫХ КАРТ =====") print("[AddPlayerGuaranteedQualityRerolls] Игрок: " .. tostring(playerId)) print("[AddPlayerGuaranteedQualityRerolls] Качество: " .. tostring(quality)) print("[AddPlayerGuaranteedQualityRerolls] Количество реролов: " .. tostring(rerollsCount)) print("[AddPlayerGuaranteedQualityRerolls] Карт за рерол: " .. tostring(cardsPerReroll)) print("[AddPlayerGuaranteedQualityRerolls] Поведение при отсутствии: " .. fallbackBehavior) local player = PlayerResource:GetPlayer(playerId) if not player then print(("[AddPlayerGuaranteedQualityRerolls] ❌ Игрок " .. tostring(playerId)) .. " не найден") return end local hero = player:GetAssignedHero() if not hero then print(("[AddPlayerGuaranteedQualityRerolls] ❌ Герой игрока " .. tostring(playerId)) .. " не найден") return end if not player.cardSystem then print(("[AddPlayerGuaranteedQualityRerolls] ⚠️ CardSystem не найден для игрока " .. tostring(playerId)) .. ", создаю...") print("[AddPlayerGuaranteedQualityRerolls] Тип player: " .. __TS__TypeOf(player)) print("[AddPlayerGuaranteedQualityRerolls] player.cardSystem до создания: " .. tostring(player.cardSystem)) player.cardSystem = __TS__New(____exports.CardSystem, playerId) print(("[AddPlayerGuaranteedQualityRerolls] ✅ CardSystem создан для игрока " .. tostring(playerId)) .. "!") print("[AddPlayerGuaranteedQualityRerolls] player.cardSystem после создания: " .. tostring(player.cardSystem)) else print("[AddPlayerGuaranteedQualityRerolls] ✅ CardSystem уже существует для игрока " .. tostring(playerId)) end print("[AddPlayerGuaranteedQualityRerolls] ✅ Все проверки пройдены, добавляем гарантированные карты") player.cardSystem:AddGuaranteedQualityRerolls(quality, rerollsCount, cardsPerReroll, fallbackBehavior) print("[AddPlayerGuaranteedQualityRerolls] ================================================") end --- Получить количество оставшихся реролов с гарантированными картами определенного качества для игрока function ____exports.GetPlayerGuaranteedQualityRerolls(self, playerId, quality) local player = PlayerResource:GetPlayer(playerId) if not player then return 0 end local hero = player:GetAssignedHero() if not hero then return 0 end if not player.cardSystem then return 0 end return player.cardSystem:GetGuaranteedQualityRerolls(quality) end --- Получить количество карт в каждом рероле с гарантированными картами определенного качества для игрока function ____exports.GetPlayerGuaranteedCardsPerReroll(self, playerId, quality) local player = PlayerResource:GetPlayer(playerId) if not player then return 0 end local hero = player:GetAssignedHero() if not hero then return 0 end if not player.cardSystem then return 0 end return player.cardSystem:GetGuaranteedCardsPerReroll(quality) end --- Получить все гарантированные карты качества для игрока function ____exports.GetPlayerAllGuaranteedQualityRerolls(self, playerId) local player = PlayerResource:GetPlayer(playerId) if not player then return {} end local hero = player:GetAssignedHero() if not hero then return {} end if not player.cardSystem then return {} end return player.cardSystem:GetAllGuaranteedQualityRerolls() end --- Очистить все гарантированные карты качества для игрока function ____exports.ClearPlayerGuaranteedQualityRerolls(self, playerId) local player = PlayerResource:GetPlayer(playerId) if not player then return end local hero = player:GetAssignedHero() if not hero then return end if not player.cardSystem then return end player.cardSystem:ClearGuaranteedQualityRerolls() end --- Игрок с «Буйным ростом» (58) не получает стандартный выбор карт на рассвете. function ____exports.shouldPlayerSkipDawnCardSelection(self, playerId) local player = PlayerResource:GetPlayer(playerId) if not (player and player.cardSystem) then return false end return player.cardSystem:SkipsDawnCardSelection() end --- Показать выбор карт игроку с источником function ____exports.ShowCardSelectionToPlayer(self, playerId, count, source, qualityFilter) if source == nil then source = "unknown" end local player = PlayerResource:GetPlayer(playerId) if not player then return end local hero = player:GetAssignedHero() if not hero then return end if not player.cardSystem then player.cardSystem = __TS__New(____exports.CardSystem, playerId) end player.cardSystem:ShowCardSelection(count, source, qualityFilter) end --- Выбор карт, среди которых гарантированно есть `forcedCardId` (остальные слоты — из пула / 404). function ____exports.ShowCardSelectionWithForcedCardToPlayer(self, playerId, forcedCardId, count, source) if count == nil then count = 3 end if source == nil then source = "forced_card" end local player = PlayerResource:GetPlayer(playerId) if not player then return end local hero = player:GetAssignedHero() if not hero then return end if not player.cardSystem then player.cardSystem = __TS__New(____exports.CardSystem, playerId) end player.cardSystem:ShowCardSelectionWithForcedCard(forcedCardId, count, source) end --- Показ выбора карт из фиксированного списка id. function ____exports.ShowCardSelectionExactOptionsToPlayer(self, playerId, optionIds, source) if source == nil then source = "exact_options" end local player = PlayerResource:GetPlayer(playerId) if not player or not player.cardSystem then print(("[ShowCardSelectionExactOptionsToPlayer] Игрок " .. tostring(playerId)) .. " не найден или не имеет cardSystem") return end player.cardSystem:ShowCardSelectionExactOptions(optionIds, source) end --- Получить размер очереди выборов карт игрока function ____exports.GetPlayerCardSelectionQueueSize(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetCardSelectionQueueSize() end return 0 end --- Очистить очередь выборов карт игрока function ____exports.ClearPlayerCardSelectionQueue(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:ClearCardSelectionQueue() else end end --- Проверить, показывается ли выбор карт игроку function ____exports.IsPlayerShowingCardSelection(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:IsShowingCardSelection() end return false end --- Принудительно выполнить рерол карт для игрока (для тестирования) function ____exports.ForceRerollCards(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:RerollCards(playerId) else end end --- Установить фильтр качества для рерола игрока function ____exports.SetPlayerRerollQualityFilter(self, playerId, qualityFilter) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then player.cardSystem:SetRerollQualityFilter(qualityFilter) else end end --- Получить фильтр качества для рерола игрока function ____exports.GetPlayerRerollQualityFilter(self, playerId) local player = PlayerResource:GetPlayer(playerId) if player and player.cardSystem then return player.cardSystem:GetRerollQualityFilter() end return nil end return ____exports