local ____lualib = require("lualib_bundle") local __TS__Class = ____lualib.__TS__Class local __TS__New = ____lualib.__TS__New local Map = ____lualib.Map local Set = ____lualib.Set local __TS__ObjectEntries = ____lualib.__TS__ObjectEntries local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach local __TS__ObjectKeys = ____lualib.__TS__ObjectKeys local __TS__ArrayIncludes = ____lualib.__TS__ArrayIncludes local __TS__StringTrim = ____lualib.__TS__StringTrim local __TS__StringStartsWith = ____lualib.__TS__StringStartsWith local __TS__ArrayJoin = ____lualib.__TS__ArrayJoin local __TS__NumberToFixed = ____lualib.__TS__NumberToFixed local __TS__ArrayIsArray = ____lualib.__TS__ArrayIsArray local __TS__Decorate = ____lualib.__TS__Decorate local ____exports = {} local ____tstl_2Dutils = require("lib.tstl-utils") local reloadable = ____tstl_2Dutils.reloadable local ____WaveManager = require("WaveManager") local WaveManager = ____WaveManager.WaveManager local ____DayNightCycleManager = require("DayNightCycleManager") local DayNightCycleManager = ____DayNightCycleManager.DayNightCycleManager local ____skip_night_vote_manager = require("skip_night_vote_manager") local SkipNightVoteManager = ____skip_night_vote_manager.SkipNightVoteManager local ____item_detector = require("item_detector") local ItemDetector = ____item_detector.ItemDetector local ____item_drops = require("item_drops") local ItemDropSystem = ____item_drops.ItemDropSystem local ____random_events = require("events.random_events") local RandomEventsManager = ____random_events.RandomEventsManager local ____SpawnManager = require("SpawnManager") local SpawnManager = ____SpawnManager.SpawnManager local ____blackshop = require("blackshop") local BlackShop = ____blackshop.BlackShop local registerBlackShopCustomGameEvents = ____blackshop.registerBlackShopCustomGameEvents local ____order_filter = require("filters.order_filter") local InitOrderFilter = ____order_filter.InitOrderFilter local ____QuestInitializer = require("quests.QuestInitializer") local InitializeQuests = ____QuestInitializer.InitializeQuests local ____kunkka_shovel_anchor_markers = require("quests.kunkka_shovel_anchor_markers") local initializeKunkkaShovelAnchorMarkers = ____kunkka_shovel_anchor_markers.initializeKunkkaShovelAnchorMarkers local ____difficulty_manager = require("difficulty_manager") local Difficulty = ____difficulty_manager.Difficulty local ____contracts_registry = require("death_sentence.contracts_registry") local applyDeathSentenceContractOnEnemySpawn = ____contracts_registry.applyDeathSentenceContractOnEnemySpawn local ____crystal_currency = require("crystal_currency") local CrystalCurrency = ____crystal_currency.CrystalCurrency local ____mini_profile_server = require("mini_profile_server") local MiniProfileServer = ____mini_profile_server.MiniProfileServer local ____leaderboard_server = require("leaderboard_server") local LeaderboardServer = ____leaderboard_server.LeaderboardServer local ____player_profile_manager = require("player_profile_manager") local PlayerProfileManager = ____player_profile_manager.PlayerProfileManager local ____store_manager = require("store_manager") local StoreManager = ____store_manager.StoreManager local ____ArsenalManager = require("arsenal.ArsenalManager") local ArsenalManager = ____ArsenalManager.ArsenalManager local ____MarketplaceManager = require("arsenal.MarketplaceManager") local MarketplaceManager = ____MarketplaceManager.MarketplaceManager local ____hero_list_table = require("tables.hero_list_table") local HERO_LIST_TABLE = ____hero_list_table.HERO_LIST_TABLE local ____HeroCosmeticManager = require("HeroCosmeticManager") local HeroCosmeticManager = ____HeroCosmeticManager.HeroCosmeticManager local ____ability_alt_cast_manager = require("ability_alt_cast_manager") local AbilityAltCastManager = ____ability_alt_cast_manager.AbilityAltCastManager local ____admin_menu = require("admin_menu") local AdminMenuManager = ____admin_menu.AdminMenuManager local ____CookingSystem = require("cooking.CookingSystem") local CookingSystem = ____CookingSystem.CookingSystem local ____battle_pass_server = require("battle_pass_server") local BattlePassServer = ____battle_pass_server.BattlePassServer local ____game_stats_tracker = require("game_stats_tracker") local GameStatsTracker = ____game_stats_tracker.GameStatsTracker local ____match_end_rewards = require("match_end_rewards") local MatchEndRewards = ____match_end_rewards.MatchEndRewards local ____match_end_combat_stats = require("match_end_combat_stats") local MatchEndCombatStats = ____match_end_combat_stats.MatchEndCombatStats local ____player_connection_state = require("utils.player_connection_state") local isConnectionStateEffectivelyInGame = ____player_connection_state.isConnectionStateEffectivelyInGame local ____real_lobby_player = require("utils.real_lobby_player") local countRealLobbyPlayers = ____real_lobby_player.countRealLobbyPlayers local isRealLobbyPlayer = ____real_lobby_player.isRealLobbyPlayer local ____game_setup_lobby_check = require("game_setup_lobby_check") local resetGameSetupLobbyCheckPhase = ____game_setup_lobby_check.resetGameSetupLobbyCheckPhase local sendGameSetupLobbyCheck = ____game_setup_lobby_check.sendGameSetupLobbyCheck local tryGameSetupLobbyCheckFromTicker = ____game_setup_lobby_check.tryGameSetupLobbyCheckFromTicker local ____player_info = require("player_info") local PlayerInfo = ____player_info.PlayerInfo local ____store_arcade_packs = require("store_arcade_packs") local loadArcadePityForPlayer = ____store_arcade_packs.loadArcadePityForPlayer local ____store_item_access = require("store_item_access") local isStorePurchaseBlockedById = ____store_item_access.isStorePurchaseBlockedById local ____chat_wheel_grant = require("chat_wheel_grant") local getChatWheelVideoPathForSound = ____chat_wheel_grant.getChatWheelVideoPathForSound 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 ____SoundSystem = require("SoundSystem") local SoundSystemManager = ____SoundSystem.SoundSystemManager local SoundCooldownSystem = ____SoundSystem.SoundCooldownSystem local SoundEventSystem = ____SoundSystem.SoundEventSystem local ____precache_models_from_kv = require("utils.precache_models_from_kv") local precacheModelsFromNpcUnitsCustomKv = ____precache_models_from_kv.precacheModelsFromNpcUnitsCustomKv local ____precache_all_npc_units = require("utils.precache_all_npc_units") local precacheAllCustomUnitsByNameAsync = ____precache_all_npc_units.precacheAllCustomUnitsByNameAsync local ____precache_misc_particles = require("utils.precache_misc_particles") local precacheMiscDistributedAndOrphanParticles = ____precache_misc_particles.precacheMiscDistributedAndOrphanParticles local ____CutsceneManager = require("cutscenes.CutsceneManager") local CutsceneManager = ____CutsceneManager.CutsceneManager local ____hero_gravestone_respawn = require("gameplay.hero_gravestone_respawn") local HeroGravestoneRespawn = ____hero_gravestone_respawn.HeroGravestoneRespawn local precacheHeroGravestoneParticles = ____hero_gravestone_respawn.precacheHeroGravestoneParticles local ____light_fix = require("utils.light_fix") local spawnLightFixForPlayer = ____light_fix.spawnLightFixForPlayer local ____cards_init = require("cards.cards_init") local InitCards = ____cards_init.InitCards local ____CardSystem = require("cards.CardSystem") local ShowCardSelectionWithForcedCardToPlayer = ____CardSystem.ShowCardSelectionWithForcedCardToPlayer local ____card_52_effects = require("cards.card_52_effects") local getEnemyStatsMultiplierFromCard52 = ____card_52_effects.getEnemyStatsMultiplierFromCard52 local ____utils = require("utils.utils") local GetAllHeroes = ____utils.GetAllHeroes local ____hero_rage = require("abilities.system.hero_rage") local attachHeroRageSystemForConfiguredHero = ____hero_rage.attachHeroRageSystemForConfiguredHero local function rawPrintFn(____, ...) _G:print(...) end local ENABLE_VERBOSE_GAME_MODE_LOGS = false local ____print = ENABLE_VERBOSE_GAME_MODE_LOGS and rawPrintFn or (function(____, ...) return nil end) local heroSelectionTime = 120 local CHAT_WHEEL_COOLDOWN = 12 --- Raid-боссы из spawn_manager — победа при убийстве local function isRaidBossUnitName(self, unitName) return unitName == "npc_boss_nevermore" end ____exports.GameMode = __TS__Class() local GameMode = ____exports.GameMode GameMode.name = "GameMode" GameMode.____file_path = "scripts/vscripts/GameMode.lua" function GameMode.prototype.____constructor(self) self.isSetupStarting = false self.playerReadyState = {} self.chatWheelPreviewSounds = __TS__New(Map) self.playerWorldLightFixSpawned = __TS__New(Set) self.homerUnderAttackNotifyNextAt = 0 self.pendingDayNightSyncBurstByPlayer = __TS__New(Set) GameRules.CutsceneManager = __TS__New(CutsceneManager) HeroGravestoneRespawn:initialize() self:configure() InitOrderFilter(nil) self:startSetupStatusTicker() self:registerSetupHandlers() GameRules:SetCustomGameSetupAutoLaunchDelay(-1) GameRules:SetStrategyTime(15) Difficulty:Init() self:initializeHeroCosmeticManager() ListenToGameEvent( "game_rules_state_change", function() return self:OnStateChange() end, nil ) ListenToGameEvent( "npc_spawned", function(event) return self:OnNpcSpawned(event) end, nil ) ListenToGameEvent( "entity_killed", function(event) return self:OnEntityKilled(event) end, nil ) ListenToGameEvent( "entity_hurt", function(event) return self:OnEntityHurtHomer(event) end, nil ) ListenToGameEvent( "dota_item_picked_up", function(event) return self:OnItemPickedUp(event) end, nil ) ListenToGameEvent( "player_chat", function(event) return self:OnPlayerChat(event) end, nil ) ListenToGameEvent( "tree_cut", function(event) return self:OnTreeCut(event) end, nil ) ListenToGameEvent( "player_connect_full", function(event) local playerId = event.PlayerID if playerId ~= nil and playerId >= 0 then Timers:CreateTimer( 1.5, function() self:loadChatWheelSoundsFromServer(playerId) loadArcadePityForPlayer(nil, playerId) return nil end ) Timers:CreateTimer( 1.5, function() ArsenalManager:loadFromServer(playerId) return nil end ) self:scheduleDayNightAndAltCastResyncBurst(playerId) end end, nil ) CustomGameEventManager:RegisterListener( "on_tiped", function(_eventSourceIndex, data) return self:OnTiped(data) end ) CustomGameEventManager:RegisterListener( "hero_selection_preview", function(_eventSourceIndex, data) return self:OnHeroSelectionPreview(data) end ) CustomGameEventManager:RegisterListener( "hero_selection_confirmed", function(_eventSourceIndex, data) return self:OnHeroSelectionConfirmed(data) end ) CustomGameEventManager:RegisterListener( "hero_selection_random_pick", function(_eventSourceIndex, data) return self:OnHeroSelectionRandomPick(data) end ) CustomGameEventManager:RegisterListener( "day_night_sync_request", function(_eventSourceIndex, data) local playerId = data.PlayerID or data.player_id if playerId == nil or playerId < 0 then return end if GameRules:State_Get() ~= DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then return end DayNightCycleManager:getInstance():SyncStateToPlayer(playerId) end ) CustomGameEventManager:RegisterListener( "request_alt_cast_sync", function(_eventSourceIndex, data) local playerId = data.PlayerID or data.player_id if playerId == nil or playerId < 0 then return end if GameRules:State_Get() ~= DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then return end AbilityAltCastManager:getInstance():syncPlayerStatesToClient(playerId) end ) SkipNightVoteManager:getInstance() CustomGameEventManager:RegisterListener( "chat_wheel_select", function(_eventSourceIndex, data) return self:OnChatWheelSelect(data) end ) CustomGameEventManager:RegisterListener( "chat_wheel_cd_start", function(_eventSourceIndex, data) return self:OnChatWheelStartCooldown(data) end ) CustomGameEventManager:RegisterListener( "chat_wheel_set_sound", function(_eventSourceIndex, data) return self:OnChatWheelSetSound(data) end ) CustomGameEventManager:RegisterListener( "chat_wheel_buy_sound", function(_eventSourceIndex, data) return self:OnChatWheelBuySound(data) end ) CustomGameEventManager:RegisterListener( "chat_wheel_preview_sound", function(_eventSourceIndex, data) return self:OnChatWheelPreviewSound(data) end ) CustomGameEventManager:RegisterListener( "store_subscription_mascot_sound", function(_eventSourceIndex, data) return self:OnStoreSubscriptionMascotSound(data) end ) AbilityAltCastManager:getInstance() SoundSystemManager:getInstance():initialize() MatchEndCombatStats:registerDamageFilter() end function GameMode.Precache(context) precacheModelsFromNpcUnitsCustomKv(nil, context) precacheAllCustomUnitsByNameAsync(nil, context) PrecacheResource("particle_folder", "particles/units/heroes/hero_slark/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_death_prophet/", context) PrecacheResource("particle_folder", "models/items/death_prophet/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_phantom_assassin/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_sven/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_magnataur/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_primal_beast/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_venomancer/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_viper/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_witchdoctor/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_largo/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_crystalmaiden/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_lina/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_axe/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_beastmaster/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_techies/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_nevermore", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_bristleback", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_legion_commander/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_queenofpain/", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_terrorblade", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_bloodseeker", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_phantom_assassin_persona", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_jakiro", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_dragon_knight", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_dragon_knight_persona", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_undying", context) PrecacheResource("particle_folder", "particles/units/heroes/hero_centaur/", context) precacheMiscDistributedAndOrphanParticles(nil, context) precacheHeroGravestoneParticles(nil, context) PrecacheResource("particle", "particles/econ/items/wraith_king/wraith_king_ti6_bracer/wraith_king_ti6_hellfireblast_debuff_fire.vpcf", context) PrecacheResource("particle", "particles/units/heroes/hero_invoker/invoker_emp_explode.vpcf", context) PrecacheResource("particle", "particles/units/heroes/hero_arc_warden/arc_warden_magnetic_tempest_ring.vpcf", context) PrecacheResource("soundfile", "soundevents/invasion_sounds.vsndevts", context) PrecacheResource("soundfile", "soundevents/wave_music.vsndevts", context) PrecacheResource("sound", "sounds/ui/treasure_01.vsnd", context) PrecacheResource("sound", "sounds/ui/treasure_02.vsnd", context) PrecacheResource("sound", "sounds/ui/treasure_03.vsnd", context) PrecacheResource("sound", "sounds/ui/cards/flip.vsnd", context) PrecacheResource("particle", "particles/econ/items/centaur/centaur_ti9/centaur_double_edge_ti9_hit_tgt.vpcf", context) PrecacheResource("sound", "sounds/ui/stingers/enc/muerta_debut_stinger.vsnd", context) PrecacheResource("sound", "sounds/weapons/hero/skeleton_king/hellfire_blast.vsnd", context) PrecacheResource("sound", "sounds/weapons/hero/chaos_knight/chaos_strike.vsnd", context) PrecacheResource("particle", "particles/econ/items/pugna/pugna_ward_ti5/pugna_ward_attack_heavy_ti_5.vpcf", context) PrecacheResource("particle", "particles/econ/items/dazzle/dazzle_ti9/dazzle_shadow_wave_ti9_crimson_impact_damage.vpcf", context) PrecacheResource("particle", "particles/econ/items/medusa/medusa_daughters/medusa_daughters_mana_shield_shell_impact_d.vpcf", context) PrecacheResource("particle", "particles/units/heroes/hero_medusa/medusa_mana_shield.vpcf", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_warlock.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_arc_warden.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_skeletonking.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_abaddon.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_pudge.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_bristleback.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_centaur.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_bristleback.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_chaos_knight.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_queenofpain.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_queenofpain.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_hoodwink.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_drowranger.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_bloodseeker.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_phantom_assassin.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_phantom_assassin.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_crystalmaiden.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_crystalmaiden.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_jakiro.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_jakiro.vsndevts", context) PrecacheResource("soundfile", "soundevents/game_sounds_heroes/game_sounds_spectre.vsndevts", context) PrecacheResource("soundfile", "soundevents/voscripts/game_sounds_vo_spectre.vsndevts", context) PrecacheResource("sound", "sounds/weapons/hero/arc_warden/magnetic_field.vsnd", context) PrecacheResource("sound", "sounds/items/skull_basher.vsnd", context) PrecacheResource("sound", "sounds/ui/compendium_levelup.vsnd", context) PrecacheResource("sound", "sounds/ui/compendium_points.vsnd", context) PrecacheResource("sound", "sounds/weapons/hero/techies/remote_mine01.vsnd", context) PrecacheResource("sound", "sounds/ui/xp_count.vsnd", context) PrecacheResource("sound", "sounds/weapons/hero/templar_assassin/trap_explode.vsnd", context) PrecacheResource("sound", "sounds/music/dsadowski_02.music.battle_03_end.vsnd", context) PrecacheResource("sound", "sounds/music/i_am_sad.vsnd", context) end function GameMode.Activate() GameRules.Addon = __TS__New(____exports.GameMode) registerBlackShopCustomGameEvents(nil) InitCards(nil) InitializeQuests(nil) MiniProfileServer:getInstance() LeaderboardServer:getInstance() GameStatsTracker:getInstance() PlayerProfileManager:getInstance() AdminMenuManager:getInstance():initialize() BattlePassServer:getInstance() ArsenalManager:initialize() MarketplaceManager:initialize() end function GameMode.prototype.configure(self) local customMaxLevel = 99 local expPerLevel = { 100, 150, 200, 250, 300, 350, 400, 450, 500, 550, 625, 700, 775, 850, 925, 1000, 1075, 1150, 1225, 1300, 1375, 1450, 1525, 1600, 1675, 1775, 1875, 1975, 2075, 2175, 2275, 2375, 2475, 2575, 2675, 2775, 2875, 2975, 3075, 3175, 3275, 3375, 3475, 3575, 3675, 3775, 3875, 3975, 4075, 4175, 4325, 4475, 4625, 4775, 4925, 5075, 5225, 5375, 5525, 5675, 5825, 5975, 6125, 6275, 6425, 6575, 6725, 6875, 7025, 7175, 7325, 7475, 7625, 7775, 7925, 8050, 8175, 8300, 8425, 8550, 8675, 8800, 8925, 9050, 9175, 9300, 9425, 9550, 9675, 9800, 9925, 10050, 10175, 10300, 10425, 10550, 10675, 10800 } GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_AGILITY_ARMOR, 0.05) GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_AGILITY_ATTACK_SPEED, 0.5) GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_INTELLIGENCE_MANA, 4) GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_AGILITY_DAMAGE, 2) GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_STRENGTH_DAMAGE, 4) GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_INTELLIGENCE_DAMAGE, 3) GameRules:GetGameModeEntity():SetCustomAttributeDerivedStatValue(DOTA_ATTRIBUTE_ALL_DAMAGE, 1.25) local heroExpTable = {} heroExpTable[2] = 0 do local i = 2 while i <= customMaxLevel do heroExpTable[i + 1] = (heroExpTable[i] or 0) + (expPerLevel[i - 2 + 1] or expPerLevel[#expPerLevel]) i = i + 1 end end GameRules:GetGameModeEntity():SetCustomXPRequiredToReachNextLevel(heroExpTable) GameRules:GetGameModeEntity():SetUseCustomHeroLevels(true) GameRules:GetGameModeEntity():SetCustomHeroMaxLevel(customMaxLevel) WaveManager:getInstance():Initialize() ItemDropSystem:Initialize() ItemDetector:Initialize() SpawnManager:getInstance():Initialize() CookingSystem:initialize() GameRules:SetCustomGameTeamMaxPlayers(DOTA_TEAM_GOODGUYS, 4) GameRules:SetCustomGameTeamMaxPlayers(DOTA_TEAM_BADGUYS, 0) if GameRules:IsCheatMode() then GameRules:SetCustomGameTeamMaxPlayers(DOTA_TEAM_BADGUYS, 6) end GameRules:GetGameModeEntity():SetPlayerHeroAvailabilityFiltered(true) GameRules:SetPreGameTime(3) GameRules:GetGameModeEntity():SetDaynightCycleDisabled(true) GameRules:SetShowcaseTime(0) GameRules:SetHeroSelectionTime(heroSelectionTime) GameRules:GetGameModeEntity():SetFreeCourierModeEnabled(true) GameRules:GetGameModeEntity():SetUseTurboCouriers(true) GameRules:GetGameModeEntity():SetCustomBuybackCostEnabled(true) GameRules:GetGameModeEntity():SetLoseGoldOnDeath(false) GameRules:SetGoldPerTick(0) GameRules:SetGoldTickTime(0) GameRules:SetUseUniversalShopMode(true) PlayerResource:SetCustomBuybackCost(0, 1000) end function GameMode.prototype.startSetupStatusTicker(self) Timers:CreateTimer( 0.1, function() local state = GameRules:State_Get() if state ~= DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then return 0.1 end if self.isSetupStarting then return 0.5 end local totalSlots = GameRules:GetCustomGameTeamMaxPlayers(DOTA_TEAM_GOODGUYS) + GameRules:GetCustomGameTeamMaxPlayers(DOTA_TEAM_BADGUYS) local connected = 0 do local playerId = 0 while playerId < DOTA_MAX_PLAYERS do do local pid = playerId if not isRealLobbyPlayer(nil, pid) then goto __continue42 end local cs = PlayerResource:GetConnectionState(pid) if isConnectionStateEffectivelyInGame(nil, cs) then connected = connected + 1 end end ::__continue42:: playerId = playerId + 1 end end local realInLobby = countRealLobbyPlayers(nil) local allConnected = connected >= totalSlots or realInLobby > 0 and connected == realInLobby local setupTimeout = 10 local timeLeft = math.max( 0, setupTimeout - GameRules:GetDOTATime(false, false) % (setupTimeout + 1) ) CustomGameEventManager:Send_ServerToAllClients("setup_status_update", {loading = not allConnected, timeLeft = allConnected and -1 or -1, connected = connected, totalSlots = totalSlots}) local players = {} do local playerId = 0 while playerId < DOTA_MAX_PLAYERS do do local pid = playerId if not isRealLobbyPlayer(nil, pid) then goto __continue45 end local steamId = tostring(PlayerResource:GetSteamAccountID(pid)) players[#players + 1] = { id = playerId, name = PlayerResource:GetPlayerName(pid), steamId = steamId } end ::__continue45:: playerId = playerId + 1 end end CustomGameEventManager:Send_ServerToAllClients("setup_players_update", {players = players}) CustomGameEventManager:Send_ServerToAllClients("setup_ready_update", {ready = self.playerReadyState}) tryGameSetupLobbyCheckFromTicker(nil) return 0.5 end ) end function GameMode.prototype.registerSetupHandlers(self) CustomGameEventManager:RegisterListener( "player_toggle_ready", function(eventSourceIndex, _data) local playerController = EntIndexToHScript(eventSourceIndex) if not playerController then return end local playerID = playerController:GetPlayerID() if not PlayerResource:IsValidPlayerID(playerID) or not isRealLobbyPlayer(nil, playerID) then return end local isHost = playerID == 0 if isHost then return end local current = self.playerReadyState[playerID] == true self.playerReadyState[playerID] = not current CustomGameEventManager:Send_ServerToAllClients("setup_ready_update", {ready = self.playerReadyState}) end ) CustomGameEventManager:RegisterListener( "host_pressed_ready", function(eventSourceIndex, _data) local playerController = EntIndexToHScript(eventSourceIndex) if not playerController then return end local playerID = playerController:GetPlayerID() local isHost = playerID == 0 if not PlayerResource:IsValidPlayerID(playerID) or not isRealLobbyPlayer(nil, playerID) or not isHost then return end self.isSetupStarting = true GameRules:SetCustomGameSetupRemainingTime(3) CustomGameEventManager:Send_ServerToAllClients("start_custom_setup_time", {}) end ) CustomGameEventManager:RegisterListener( "host_cancel_ready", function(eventSourceIndex, _data) local playerController = EntIndexToHScript(eventSourceIndex) if not playerController then return end local playerID = playerController:GetPlayerID() local isHost = playerID == 0 if not PlayerResource:IsValidPlayerID(playerID) or not isRealLobbyPlayer(nil, playerID) or not isHost then return end self.isSetupStarting = false GameRules:SetCustomGameSetupRemainingTime(300) CustomGameEventManager:Send_ServerToAllClients("cancel_custom_setup_time", {}) end ) Timers:CreateTimer( 0.1, function() if GameRules:State_Get() ~= DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then return 0.3 end do local playerId = 0 while playerId < DOTA_MAX_PLAYERS do do local pid = playerId if not isRealLobbyPlayer(nil, pid) then goto __continue60 end local isHost = playerId == 0 local player = PlayerResource:GetPlayer(pid) if player then CustomGameEventManager:Send_ServerToPlayer(player, "setup_host_info", {isHost = isHost}) CustomGameEventManager:Send_ServerToPlayer(player, "setup_ready_update", {ready = self.playerReadyState}) end end ::__continue60:: playerId = playerId + 1 end end return 0.5 end ) end function GameMode.prototype.OnStateChange(self) local state = GameRules:State_Get() if state == DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then do local playerId = 0 while playerId < DOTA_MAX_PLAYERS do local pid = playerId if isRealLobbyPlayer(nil, pid) then PlayerResource:SetCustomTeamAssignment(pid, DOTA_TEAM_GOODGUYS) end playerId = playerId + 1 end end resetGameSetupLobbyCheckPhase(nil) Timers:CreateTimer( 0.5, function() if GameRules:State_Get() == DOTA_GAMERULES_STATE_CUSTOM_GAME_SETUP then sendGameSetupLobbyCheck(nil) end return nil end ) end if state == DOTA_GAMERULES_STATE_PRE_GAME then resetGameSetupLobbyCheckPhase(nil) self.playerReadyState = {} self:StartGame() end if state == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then DayNightCycleManager:getInstance():Initialize() RandomEventsManager:getInstance() BlackShop:getInstance() initializeKunkkaShovelAnchorMarkers(nil) CrystalCurrency:getInstance() WaveManager:getInstance():SetGameStarted() self:GameInProgress() end if state == DOTA_GAMERULES_STATE_HERO_SELECTION then self:CheckPlayerAvailability() end end function GameMode.prototype.StartGame(self) end function GameMode.prototype.CheckPlayerAvailability(self) local storeManager = StoreManager:getInstance() do local playerId = 0 while playerId < DOTA_MAX_TEAM_PLAYERS do do local pid = playerId if not isRealLobbyPlayer(nil, pid) then goto __continue74 end __TS__ArrayForEach( __TS__ObjectEntries(HERO_LIST_TABLE), function(____, ____bindingPattern0) local heroData local heroName heroName = ____bindingPattern0[1] heroData = ____bindingPattern0[2] local isDonateHero = heroData.isDonate == true local heroUnlocked = not isDonateHero or storeManager:hasUnlockedHero(pid, heroName, heroData.storeItemId) if heroUnlocked then GameRules:AddHeroToPlayerAvailability(pid, heroData.heroId) end end ) end ::__continue74:: playerId = playerId + 1 end end end function GameMode.prototype.getAvailableHeroNamesForPlayer(self, playerId) local storeManager = StoreManager:getInstance() local enabledHeroes = self:getEnabledHeroesFromHeroList() local availableHeroes = {} __TS__ArrayForEach( __TS__ObjectEntries(HERO_LIST_TABLE), function(____, ____bindingPattern0) local heroData local heroName heroName = ____bindingPattern0[1] heroData = ____bindingPattern0[2] if not enabledHeroes[heroName] then return end local isDonateHero = heroData.isDonate == true local heroUnlocked = not isDonateHero or storeManager:hasUnlockedHero(playerId, heroName, heroData.storeItemId) if heroUnlocked then availableHeroes[#availableHeroes + 1] = heroName end end ) return availableHeroes end function GameMode.prototype.getEnabledHeroesFromHeroList(self) local enabledHeroes = {} local rawKv = LoadKeyValues("scripts/npc/herolist.txt") local ____temp_2 = rawKv and rawKv.CustomHeroList if ____temp_2 == nil then ____temp_2 = rawKv end local heroList = ____temp_2 if heroList then for heroName in pairs(heroList) do local count = tonumber(tostring(heroList[heroName])) or 0 if count > 0 or count == -1 then enabledHeroes[tostring(heroName)] = true end end end if #__TS__ObjectKeys(enabledHeroes) == 0 then for heroName in pairs(HERO_LIST_TABLE) do enabledHeroes[tostring(heroName)] = true end end return enabledHeroes end function GameMode.prototype.initializeHeroCosmeticManager(self) local cosmeticManager = HeroCosmeticManager:getInstance() cosmeticManager:registerHeroCosmetics("npc_dota_hero_nagash", {model = "models/heroes/phantom_assassin_persona/phantom_assassin_persona.vmdl", slots = {weapon = "models/heroes/phantom_assassin_persona/phantom_assassin_persona_weapon.vmdl", head = "models/heroes/phantom_assassin_persona/phantom_assassin_persona_head.vmdl", armor = "models/heroes/phantom_assassin_persona/phantom_assassin_persona_armor.vmdl", legs = "models/heroes/phantom_assassin_persona/phantom_assassin_persona_legs.vmdl"}, scale = 1}) cosmeticManager:registerHeroCosmetics("npc_dota_hero_bloodhunter", {model = "models/heroes/blood_seeker/blood_seeker.vmdl", slots = {weapon = "models/items/blood_seeker/thirst_of_eztzhok_weapon/thirst_of_eztzhok.vmdl", right_hook4_0 = "models/items/blood_seeker/thirst_of_eztzhok_weapon_offhand/thirst_of_eztzhok_offhand.vmdl", select_high = "models/items/blood_seeker/bloodseeker_crownfall_immortal/bloodseeker_crownfall_immortal.vmdl", head = "models/items/blood_seeker/bloodseeker_immortal_head/bloodseeker_immortal_head.vmdl"}}) cosmeticManager:registerHeroCosmetics("npc_dota_hero_yuki_onna", {model = "models/heroes/crystal_maiden/crystal_maiden_arcana.vmdl", slots = {weapon = "models/items/crystal_maiden/cm_screeauk/cm_screeauk_weapon.vmdl", select_high = "models/items/crystal_maiden/cm_screeauk/cm_screeauk_shoulder.vmdl", Spine_0 = "models/items/crystal_maiden/cm_screeauk/cm_screeauk_back.vmdl", head = "models/items/crystal_maiden/cm_screeauk/cm_screeauk_head.vmdl"}}) end function GameMode.prototype.GameInProgress(self) Difficulty:OnHeroSelectionState() do local playerID = 0 while playerID < DOTA_MAX_PLAYERS do if PlayerResource:IsValidPlayerID(playerID) then local steamID = PlayerResource:GetSteamAccountID(playerID) local testerIDs = { "877002179", "453736017", "133273043", "223272113", "355480974", "985645109", "1592242655", "899783721", "959290792" } if __TS__ArrayIncludes( testerIDs, tostring(steamID) ) then if steamID == 877002179 then GameRules:SendCustomMessage("Добро пожаловать в игру хозяин Nasqreal!", 0, 0) end return end end return end end end function GameMode.prototype.Reload(self) end function GameMode.prototype.addTestBots(self) if GameRules:IsCheatMode() then local botHeroes = {"npc_dota_hero_drow_ranger"} do local i = 0 while i < 1 do local heroName = botHeroes[i % #botHeroes + 1] Tutorial:AddBot( heroName, "TestBot" .. tostring(i + 1), "", true ) i = i + 1 end end end end function GameMode.prototype.getDifficulter(self) return type(Difficulter) ~= "nil" and Difficulter or 1 end function GameMode.prototype.resetWorldLightFixSpawnForPlayer(self, playerId) self.playerWorldLightFixSpawned:delete(playerId) end function GameMode.prototype.OnPlayerChat(self, event) if not IsServer() then return end local trimmed = __TS__StringTrim(event.text) local lower = string.lower(trimmed) if lower == "-lightfix" then local playerId = event.playerid if not PlayerResource:IsValidPlayerID(playerId) or PlayerResource:IsFakeClient(playerId) then return end spawnLightFixForPlayer(nil, playerId) return end if GameRules:IsCheatMode() and GameRules:State_Get() == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then if string.len(lower) >= 6 and string.sub(lower, 1, 5) == "-card" then local rest = (string.gsub( string.sub(lower, 6), "^%s+", "" )) if rest ~= "" then local cardId = tonumber(rest) if cardId ~= nil and cardId >= 1 then local playerId = event.playerid if not PlayerResource:IsValidPlayerID(playerId) or PlayerResource:IsFakeClient(playerId) then return end ShowCardSelectionWithForcedCardToPlayer( nil, playerId, cardId, 1, "cheat_chat_card" ) return end end end end end function GameMode.prototype.OnNpcSpawned(self, event) local unit = EntIndexToHScript(event.entindex) if not unit then return end if unit:IsRealHero() and not unit:IsIllusion() then local ownerPlayerId = unit:GetPlayerOwnerID() if ownerPlayerId >= 0 and PlayerResource:IsValidPlayerID(ownerPlayerId) then self:scheduleDayNightAndAltCastResyncBurst(ownerPlayerId) end local lightFixPid = unit:GetPlayerOwnerID() if lightFixPid >= 0 and PlayerResource:IsValidPlayerID(lightFixPid) and not PlayerResource:IsFakeClient(lightFixPid) and not self.playerWorldLightFixSpawned:has(lightFixPid) then self.playerWorldLightFixSpawned:add(lightFixPid) Timers:CreateTimer( 6.5, function() spawnLightFixForPlayer(nil, lightFixPid) return nil end ) end attachHeroRageSystemForConfiguredHero(nil, unit) end if unit:IsRealHero() and not unit:HasAbility("ability_stacking_crit") then local playerId = unit:GetPlayerOwnerID() local steamID = PlayerResource:GetSteamAccountID(playerId) Timers:CreateTimer( 1, function() self:loadChatWheelSoundsFromServer(playerId) return nil end ) if steamID == 946902506 then unit:AddItemByName("item_rofl_for_kaban_pumba") end unit:AddAbility("ability_stacking_crit"):SetLevel(1) unit:AddAbility("ability_stacking_spell_crit"):SetLevel(1) unit:AddAbility("ability_stats_multiplier"):SetLevel(1) unit:AddAbility("ability_effects"):SetLevel(1) if GameRules:IsCheatMode() and not unit:FindItemInInventory("item_blink") then unit:AddItemByName("item_blink") unit:AddItemByName("item_test") end HeroCosmeticManager:getInstance():onHeroSpawn(unit) local arsenalPlayerId = unit:GetPlayerOwnerID() if arsenalPlayerId >= 0 then local arsenalPlayer = PlayerResource:GetPlayer(arsenalPlayerId) if arsenalPlayer then ArsenalManager:applyLoadout(arsenalPlayer, unit) end end local heroPlayerId = unit:GetPlayerOwnerID() if heroPlayerId >= 0 then local heroName = unit:GetUnitName() CustomGameEventManager:Send_ServerToAllClients("hero_spawned", {playerId = heroPlayerId, heroName = heroName}) end end if unit:GetTeam() == DOTA_TEAM_GOODGUYS then return end local requiredPlayers = 4 local realHeroesCount = math.max( 1, countRealLobbyPlayers(nil) ) local difficulter = self:getDifficulter() if realHeroesCount < requiredPlayers and self:getDifficulter() >= 2 then local missing = requiredPlayers - realHeroesCount difficulter = difficulter * (1 - 0.1 * missing) end local gameTime = GameRules:GetDOTATime(false, false) local minutesPassed = math.max( 0, math.floor(gameTime / 60) ) local timeScale = 1 + minutesPassed * 0.02 local card52EnemyScale = getEnemyStatsMultiplierFromCard52(nil) local totalScale = difficulter * timeScale * card52EnemyScale local unitName = unit.GetUnitName and unit:GetUnitName() if type(unitName) == "string" and __TS__StringStartsWith(unitName, "npc_wave_boss") and WaveManager:getInstance():GetCurrentNight() > 6 then totalScale = totalScale * 2 end unit:SetBaseMaxHealth(unit:GetMaxHealth() * totalScale) unit:SetMaxHealth(unit:GetMaxHealth() * totalScale) unit:SetHealth(unit:GetMaxHealth()) unit:SetBaseHealthRegen(unit:GetBaseHealthRegen() * totalScale) unit:SetBaseDamageMin(unit:GetBaseDamageMin() * totalScale) unit:SetBaseDamageMax(unit:GetBaseDamageMax() * totalScale) local diminishingScale = math.sqrt(totalScale) local currentArmor = unit:GetPhysicalArmorBaseValue() if currentArmor < 0 then unit:SetPhysicalArmorBaseValue(currentArmor - diminishingScale) else unit:SetPhysicalArmorBaseValue(currentArmor + diminishingScale) end local currentResist = unit:GetBaseMagicalResistanceValue() if currentResist < 0 then unit:SetBaseMagicalResistanceValue(currentResist - diminishingScale) else unit:SetBaseMagicalResistanceValue(currentResist + diminishingScale) end applyDeathSentenceContractOnEnemySpawn( nil, unit, Difficulty:getWinningContractSnapshot() ) end function GameMode.prototype.scheduleDayNightAndAltCastResyncBurst(self, playerId) if self.pendingDayNightSyncBurstByPlayer:has(playerId) then return end self.pendingDayNightSyncBurstByPlayer:add(playerId) local delays = {0.2, 1, 2, 4} local pending = #delays for ____, delay in ipairs(delays) do Timers:CreateTimer( delay, function() do pcall(function() if GameRules:State_Get() == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS and PlayerResource:IsValidPlayerID(playerId) then DayNightCycleManager:getInstance():SyncStateToPlayer(playerId) AbilityAltCastManager:getInstance():syncPlayerStatesToClient(playerId) end end) do pending = pending - 1 if pending <= 0 then self.pendingDayNightSyncBurstByPlayer:delete(playerId) end end end return nil end ) end end function GameMode.prototype.OnEntityHurtHomer(self, event) if not IsServer() then return end local ev = event local victimIndex = ev.entindex_victim or ev.entindex_killed if victimIndex == nil or victimIndex == nil then return end local victim = EntIndexToHScript(victimIndex) if not victim or victim:IsNull() or not victim:IsAlive() or not IsValidEntity(victim) then return end if victim:GetUnitName() ~= "npc_homer" then return end local damage = ev.damage or 0 if damage <= 0 then return end local attackerIndex = ev.entindex_attacker if attackerIndex == nil or attackerIndex == nil then return end local attacker = EntIndexToHScript(attackerIndex) if not attacker or not IsValidEntity(attacker) then return end if attacker == victim then return end if attacker:GetTeamNumber() == victim:GetTeamNumber() then return end local now = GameRules:GetGameTime() if now < self.homerUnderAttackNotifyNextAt then return end self.homerUnderAttackNotifyNextAt = now + 4.5 CustomGameEventManager:Send_ServerToAllClients("homer_under_attack", {}) end function GameMode.prototype.applyTieredHeroRespawnOnDeath(self, hero) if not IsServer() then return end if not hero or not IsValidEntity(hero) then return end if hero:WillReincarnate() or hero:IsReincarnating() then return end local cutscene = GameRules.CutsceneManager if cutscene ~= nil and cutscene:isCutsceneActive() then return end local level = hero:GetLevel() local seconds = level < 10 and 30 or (level <= 30 and 60 or 90) hero:SetTimeUntilRespawn(seconds) end function GameMode.prototype.OnEntityKilled(self, event) local killedUnit = EntIndexToHScript(event.entindex_killed) if not killedUnit then return end if killedUnit:IsHero() and killedUnit:IsRealHero() and not killedUnit:IsIllusion() then local heroDead = killedUnit if heroDead:GetTeamNumber() == DOTA_TEAM_GOODGUYS then self:applyTieredHeroRespawnOnDeath(heroDead) end end local unitName = killedUnit:GetUnitName() if unitName == "npc_homer" then GameStatsTracker:getInstance():onDefeat(function() MatchEndRewards:dispatch( "loss", function() GameRules:SetGameWinner(DOTA_TEAM_BADGUYS) end ) end) return end if isRaidBossUnitName(nil, unitName) then GameStatsTracker:getInstance():onVictory(function() __TS__ArrayForEach( GetAllHeroes(nil), function(____, hero) hero:AddNewModifier( hero, getModifierSourceAbility(nil, hero), "modifier_cutscene_lock", {} ) end ) MatchEndRewards:dispatch("win") end) end end function GameMode.prototype.OnTreeCut(self, event) local treePosition = Vector(event.tree_x, event.tree_y, 0) local dropChance = 0.2 if math.random() < dropChance then local bananaItem = CreateItem("item_banana", nil, nil) if bananaItem then local drop = CreateItemOnPositionForLaunch(treePosition, bananaItem) local dropRadius = RandomFloat(50, 100) bananaItem:LaunchLootInitialHeight( false, 0, 150, 0.5, treePosition + RandomVector(dropRadius) ) end end end function GameMode.prototype.OnItemPickedUp(self, event) local unitIndex = event.ItemEntityIndex local unit = EntIndexToHScript(unitIndex) local hero = unit and unit:GetOwner() local item = EntIndexToHScript(event.ItemEntityIndex) local owner = EntIndexToHScript(event.ItemEntityIndex) local itemName = item:GetAbilityName() local shopItem = BlackShop:getInstance():getItemInfo(itemName) if shopItem then if item._wasPurchased then return end local playerId = hero and hero:GetPlayerOwnerID() or -1 local blackShopRestockPosition local physicalParent = item:GetContainer() if physicalParent and not physicalParent:IsNull() then blackShopRestockPosition = physicalParent:GetAbsOrigin() end local crystalCurrency = CrystalCurrency:getInstance() local crystalAmount = crystalCurrency:getCrystals(playerId) if crystalAmount >= shopItem.cost then crystalCurrency:removeCrystals(playerId, shopItem.cost) local success = BlackShop:getInstance():OnItemPickup(item, playerId) if not success then crystalCurrency:addCrystals(playerId, shopItem.cost) UTIL_Remove(item) else do pcall(function() local qualityMap = { [0] = "common", [1] = "rare", [2] = "epic", [3] = "legendary", [4] = "cursed", [5] = "heavenly" } local qualityStr = qualityMap[shopItem.quality] or "common" BattlePassServer:getInstance():onBlackShopPurchase(playerId, qualityStr) end) end local ____opt_7 = PlayerResource:GetPlayer(playerId) local heroForRestock = ____opt_7 and ____opt_7:GetAssignedHero() if blackShopRestockPosition and heroForRestock and heroForRestock:HasModifier("modifier_item_restock") then Timers:CreateTimer( 0.03, function() BlackShop:getInstance():SpawnItemAtPosition(blackShopRestockPosition) end ) end end else BlackShop:getInstance():SpawnItemAtPosition(owner:GetAbsOrigin()) GameRules:SendCustomMessage("ЧЁ ХОТЕЛ СПИЗИДТЬ ХУЕСОС?!?!?!? ПОСОСАЛ?!?!?!?", 0, 0) hero:ForceKill(true) EmitSoundOn("DOTA_Item.Dagon.Activate", hero) EmitSoundOn("DOTA_Item.Dagon5.Target", owner) UTIL_Remove(item) end end end function GameMode.prototype.OnTiped(self, data) local ____data_9 = data local target = ____data_9.target local playerId = ____data_9.playerId local hero = PlayerResource:GetSelectedHeroEntity(target) if hero and IsValidEntity(hero) then EmitSoundOn("General.Ping", hero) ParticleManager:CreateParticle("particles/ping_player_clown.vpcf", PATTACH_ABSORIGIN_FOLLOW, hero) end do pcall(function() BattlePassServer:getInstance():onTipped(playerId) end) end CustomGameEventManager:Send_ServerToAllClients("player_tip", data) end function GameMode.prototype.OnHeroSelectionPreview(self, data) CustomGameEventManager:Send_ServerToAllClients("hero_selection_preview", data) end function GameMode.prototype.OnHeroSelectionConfirmed(self, data) CustomGameEventManager:Send_ServerToAllClients("hero_selection_confirmed", data) end function GameMode.prototype.OnHeroSelectionRandomPick(self, data) local playerId = data.playerId or data.PlayerID if playerId == nil or playerId < 0 then return end local selectedHero = PlayerResource:GetSelectedHeroName(playerId) if selectedHero and selectedHero ~= "" then return end local player = PlayerResource:GetPlayer(playerId) if not player then return end local availableHeroes = self:getAvailableHeroNamesForPlayer(playerId) if #availableHeroes == 0 then player:MakeRandomHeroSelection() return end local randomIndex = RandomInt(0, #availableHeroes - 1) local randomHeroName = availableHeroes[randomIndex + 1] player:SetSelectedHero(randomHeroName) end function GameMode.prototype.OnChatWheelSelect(self, data) local ChatActions = ChatActions or ({}) ChatActions.PLAYER_NAME = 0 ChatActions[ChatActions.PLAYER_NAME] = "PLAYER_NAME" ChatActions.PLAYER_COLOR = 1 ChatActions[ChatActions.PLAYER_COLOR] = "PLAYER_COLOR" ChatActions.HERO_NAME = 2 ChatActions[ChatActions.HERO_NAME] = "HERO_NAME" local playerInfo = PlayerInfo:GetPlayerInfo(data.PlayerID) local ____opt_10 = playerInfo and playerInfo.chat_wheel if ____opt_10 ~= nil then ____opt_10 = ____opt_10[tostring(data.select)] end local chatWheel = ____opt_10 if not chatWheel then return end local showVideo = false local videoPath = nil if chatWheel.sound then ____print( nil, "[CHAT_WHEEL] Ищем видео для звука: " .. tostring(chatWheel.sound) ) local soundsWheel = playerInfo and playerInfo.sounds_wheel or ({}) ____print( nil, "[CHAT_WHEEL] sounds_wheel keys: " .. __TS__ArrayJoin( __TS__ObjectKeys(soundsWheel), ", " ) ) local soundData = nil for ____, ____value in ipairs(__TS__ObjectEntries(soundsWheel)) do local soundId = ____value[1] local data = ____value[2] local dataAny = data ____print( nil, (("[CHAT_WHEEL] Проверяем звук: id=" .. tostring(soundId)) .. ", sound=") .. tostring(dataAny.sound) ) if dataAny.sound == chatWheel.sound or soundId == chatWheel.sound then soundData = dataAny ____print( nil, (("[CHAT_WHEEL] Найден звук! video=" .. tostring(dataAny.video)) .. ", video_path=") .. tostring(dataAny.video_path) ) break end end if soundData then videoPath = soundData.video or soundData.video_path if not videoPath and chatWheel.sound then videoPath = getChatWheelVideoPathForSound(nil, chatWheel.sound) end showVideo = not not videoPath ____print( nil, (("[CHAT_WHEEL] Видео найдено: showVideo=" .. tostring(showVideo)) .. ", videoPath=") .. tostring(videoPath) ) ____print( nil, (("[CHAT_WHEEL] soundData.video=" .. tostring(soundData.video)) .. ", soundData.video_path=") .. tostring(soundData.video_path) ) else ____print(nil, "[CHAT_WHEEL] Звук не найден в sounds_wheel!") if chatWheel.sound then videoPath = getChatWheelVideoPathForSound(nil, chatWheel.sound) showVideo = not not videoPath end end local soundIdForConfig = chatWheel.sound if soundData then local soundsWheel = playerInfo and playerInfo.sounds_wheel or ({}) for ____, ____value in ipairs(__TS__ObjectEntries(soundsWheel)) do local id = ____value[1] local data = ____value[2] local dataAny = data if dataAny.sound == chatWheel.sound or id == chatWheel.sound then soundIdForConfig = tostring(id) break end end end local cooldownSystem = SoundCooldownSystem:getInstance() local soundConfigKey = cooldownSystem:resolveSoundConfigKey(soundIdForConfig, chatWheel.sound) local steamId = PlayerResource:GetSteamAccountID(data.PlayerID) local isNoCooldownPlayer = steamId == SoundCooldownSystem.NO_COOLDOWN_STEAM_ID ____print( nil, (((("[CHAT_WHEEL] Воспроизведение звука: chatWheel.sound=" .. tostring(chatWheel.sound)) .. ", soundIdForConfig=") .. soundIdForConfig) .. ", soundConfigKey=") .. soundConfigKey ) if cooldownSystem:isAnySoundBlocking() and not isNoCooldownPlayer then local currentSoundConfig = cooldownSystem:getSoundConfig(soundConfigKey, chatWheel.sound) local isCurrentSoundBlocking = (currentSoundConfig and currentSoundConfig.blocksOtherSounds) == true if not isCurrentSoundBlocking then local player = PlayerResource:GetPlayer(data.PlayerID) if player ~= nil and player ~= nil then CustomGameEventManager:Send_ServerToPlayer(player, "create_error_message", {message = "Сейчас играет другой звук, подождите"}) end ____print( nil, ("[CHAT_WHEEL] Звук " .. tostring(chatWheel.sound)) .. " заблокирован - играет другой звук" ) return end end if not cooldownSystem:canPlaySound(soundIdForConfig, data.PlayerID) then local remaining = cooldownSystem:getRemainingCooldown(soundIdForConfig, data.PlayerID) local player = PlayerResource:GetPlayer(data.PlayerID) if player ~= nil and player ~= nil then local timeText if remaining >= 60 then local minutes = math.floor(remaining / 60) local seconds = math.ceil(remaining % 60) local secondsStr = seconds < 10 and "0" .. tostring(seconds) or tostring(seconds) timeText = (tostring(minutes) .. ":") .. secondsStr else timeText = tostring(math.ceil(remaining)) .. " сек" end CustomGameEventManager:Send_ServerToPlayer(player, "create_error_message", {message = "Звук на кулдауне. Осталось: " .. timeText}) SoundSystemManager:getInstance():updateSoundCooldownInNetTables(data.PlayerID, soundIdForConfig, remaining) end ____print( nil, ((((("[CHAT_WHEEL] Звук " .. tostring(chatWheel.sound)) .. " на кулдауне для игрока ") .. tostring(data.PlayerID)) .. ", осталось ") .. __TS__NumberToFixed(remaining, 1)) .. " сек" ) return end local soundEventName = chatWheel.sound == "i_am_sad" and "I_am_sad" or chatWheel.sound ____print( nil, "[CHAT_WHEEL] Используется soundEventName=" .. tostring(soundEventName) ) local hero = PlayerResource:GetSelectedHeroEntity(data.PlayerID) if hero then EmitGlobalSound(soundEventName) else do local playerID = 0 while playerID < PlayerResource:GetPlayerCount() do if PlayerResource:IsValidPlayerID(playerID) then local player = PlayerResource:GetPlayer(playerID) if player then EmitSoundOnClient(soundEventName, player) end end playerID = playerID + 1 end end end local soundConfig = cooldownSystem:getSoundConfig(soundConfigKey, chatWheel.sound) if (soundConfig and soundConfig.blocksOtherSounds) == true then cooldownSystem:setBlocking(soundConfigKey, true) local blockDuration = soundConfig.blockDuration or 5.7 Timers:CreateTimer( blockDuration, function() cooldownSystem:setBlocking(soundConfigKey, false) ____print( nil, "[CHAT_WHEEL] Блокировка других звуков снята после окончания " .. tostring(chatWheel.sound) ) return nil end ) end local foundSoundId = nil if soundData then local soundsWheel = playerInfo and playerInfo.sounds_wheel or ({}) for ____, ____value in ipairs(__TS__ObjectEntries(soundsWheel)) do local id = ____value[1] local data = ____value[2] local dataAny = data if dataAny.sound == chatWheel.sound or id == chatWheel.sound then foundSoundId = tostring(id) break end end end cooldownSystem:setPlayerCooldown(soundIdForConfig, data.PlayerID) SoundSystemManager:getInstance():startSoundCooldownTimer(data.PlayerID, soundIdForConfig) SoundEventSystem:getInstance():triggerSoundEvent(data.PlayerID, chatWheel.sound, chatWheel.sound, soundData) else ____print(nil, "[CHAT_WHEEL] chatWheel.sound пустой!") end if videoPath and not showVideo then ____print(nil, "[CHAT_WHEEL] Исправляем: videoPath есть, но showVideo=false. Устанавливаем showVideo=true") showVideo = true end ____print( nil, (("[CHAT_WHEEL] Отправляем событие: show_video=" .. tostring(showVideo)) .. ", video_path=") .. tostring(videoPath) ) CustomGameEventManager:Send_ServerToAllClients("chat_wheel_send_sound", { sender_id = data.PlayerID, main_token = chatWheel.name, is_team = false, is_all = true, sound = chatWheel.sound or "", show_video = showVideo, video_path = videoPath, video = videoPath, tokens = {players = {[0] = {player_name = ChatActions.PLAYER_NAME, player_color = ChatActions.PLAYER_COLOR, hero_name = ChatActions.HERO_NAME}}}, add_tag = true }) end function GameMode.prototype.OnChatWheelStartCooldown(self, data) local playerID = data.PlayerID local playerInfo = CustomNetTables:GetTableValue( "cooldown_info", tostring(playerID) ) local steamID = tostring(PlayerResource:GetSteamAccountID(playerID)) if not playerInfo then playerInfo = {cooldown_chat = 1} CustomNetTables:SetTableValue( "cooldown_info", tostring(playerID), playerInfo ) Timers:CreateTimer( CHAT_WHEEL_COOLDOWN, function() playerInfo = {cooldown_chat = 0} CustomNetTables:SetTableValue( "cooldown_info", tostring(playerID), playerInfo ) end ) else playerInfo.cooldown_chat = 1 CustomNetTables:SetTableValue( "cooldown_info", tostring(playerID), playerInfo ) Timers:CreateTimer( CHAT_WHEEL_COOLDOWN, function() playerInfo.cooldown_chat = 0 CustomNetTables:SetTableValue( "cooldown_info", tostring(playerID), playerInfo ) end ) end end function GameMode.prototype.OnChatWheelPreviewSound(self, data) local playerID = data.PlayerID local soundId = data.sound_id if not soundId then return end local soundEventName = soundId == "i_am_sad" and "I_am_sad" or soundId local player = PlayerResource:GetPlayer(playerID) if not player then return end local previousSound = self.chatWheelPreviewSounds:get(playerID) if previousSound then StopSoundOn(previousSound, player) self.chatWheelPreviewSounds:delete(playerID) end EmitSoundOnClient(soundEventName, player) self.chatWheelPreviewSounds:set(playerID, soundEventName) end function GameMode.prototype.OnStoreSubscriptionMascotSound(self, data) local playerID = data.PlayerID local soundId = __TS__StringTrim(tostring(data.sound_id or "")) if not soundId then return end local player = PlayerResource:GetPlayer(playerID) if not player then return end EmitSoundOnClient(soundId, player) end function GameMode.prototype.OnChatWheelSetSound(self, data) local playerID = data.PlayerID local slot = data.slot local soundId = data.sound_id ____print( nil, (((("[CHAT_WHEEL] Установка звука: soundId=" .. soundId) .. ", slot=") .. tostring(slot)) .. ", playerID=") .. tostring(playerID) ) local playerInfoData = PlayerInfo:GetPlayerInfo(playerID) if not playerInfoData then playerInfoData = {} end local soundsWheel = playerInfoData.sounds_wheel or ({}) ____print( nil, "[CHAT_WHEEL] Доступные звуки в sounds_wheel: " .. __TS__ArrayJoin( __TS__ObjectKeys(soundsWheel), ", " ) ) local soundData = soundsWheel[soundId] if not soundData then ____print( nil, (("[CHAT_WHEEL] Звук " .. soundId) .. " не найден у игрока ") .. tostring(playerID) ) return end ____print( nil, (((("[CHAT_WHEEL] Найден звук: soundId=" .. soundId) .. ", soundData.sound=") .. tostring(soundData.sound)) .. ", soundData.name=") .. tostring(soundData.name) ) if not playerInfoData.chat_wheel then playerInfoData.chat_wheel = {} end local ____temp_24 if soundData.sound and soundData.sound ~= "" then ____temp_24 = soundData.sound else ____temp_24 = soundId end local soundToUse = ____temp_24 playerInfoData.chat_wheel[tostring(slot)] = {name = soundData.name or soundId, type = "sound", sellPrice = 0, sound = soundToUse} PlayerInfo:UpdatePlayerInfo(playerID, playerInfoData) self:saveChatWheelToServer(playerID, playerInfoData.chat_wheel) ____print( nil, (((((((("[CHAT_WHEEL] Игрок " .. tostring(playerID)) .. " установил звук ") .. soundId) .. " в слот ") .. tostring(slot)) .. ", sound=") .. tostring(soundToUse)) .. ", soundData.sound=") .. tostring(soundData.sound) ) end function GameMode.prototype.OnChatWheelBuySound(self, data) local playerID = data.PlayerID local soundId = data.sound_id local soundData = data.sound_data local storeManager = StoreManager:getInstance() local fullSoundItemId = "chat_wheel_sound_" .. soundId if isStorePurchaseBlockedById(nil, fullSoundItemId) then rawPrintFn(nil, ("[CHAT_WHEEL] Покупка отклонена: " .. fullSoundItemId) .. " не продаётся в магазине") return end if not storeManager:hasPurchasedItem(playerID, fullSoundItemId) then rawPrintFn( nil, (("[CHAT_WHEEL] Покупка отклонена: " .. fullSoundItemId) .. " не найден в кэше покупок игрока ") .. tostring(playerID) ) storeManager:sendPurchaseResult(playerID, false, "Сначала купи звук в магазине") return end local playerInfoData = PlayerInfo:GetPlayerInfo(playerID) if not playerInfoData then playerInfoData = {} end local soundsWheel = playerInfoData.sounds_wheel or ({}) if soundsWheel[soundId] then ____print( nil, (("[CHAT_WHEEL] Звук " .. soundId) .. " уже куплен игроком ") .. tostring(playerID) ) return end if not playerInfoData.sounds_wheel then playerInfoData.sounds_wheel = {} end local soundDataAny = soundData local soundToSave = soundData.sound and soundData.sound ~= "" and soundData.sound or soundId ____print( nil, (((((((("[CHAT_WHEEL] Сохраняем звук: id=" .. soundId) .. ", sound=") .. soundToSave) .. ", soundData.sound=") .. soundData.sound) .. ", video=") .. tostring(soundDataAny.video)) .. ", video_path=") .. tostring(soundDataAny.video_path) ) playerInfoData.sounds_wheel[soundId] = { name = soundData.name, type = "sound", sellPrice = 0, sound = soundToSave, icon = soundData.icon, video = soundDataAny.video or soundDataAny.video_path, video_path = soundDataAny.video_path or soundDataAny.video } local savedSound = playerInfoData.sounds_wheel[soundId] ____print( nil, (((((("[CHAT_WHEEL] Звук сохранен в sounds_wheel: name=" .. tostring(savedSound.name)) .. ", sound=") .. tostring(savedSound.sound)) .. ", video=") .. tostring(savedSound.video)) .. ", video_path=") .. tostring(savedSound.video_path) ) PlayerInfo:UpdatePlayerInfo(playerID, playerInfoData) self:saveSoundsWheelToServer(playerID, playerInfoData.sounds_wheel) storeManager:updateCurrencyDisplay(playerID) storeManager:saveCurrencyToServer(playerID) rawPrintFn( nil, (("[CHAT_WHEEL] Покупка звука: player=" .. tostring(playerID)) .. ", sound=") .. soundId ) end function GameMode.prototype.saveChatWheelToServer(self, playerId, chatWheel) local steamId = PlayerResource:GetSteamAccountID(playerId) if not steamId then return end local request = CreateHTTPRequestScriptVM( "PUT", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/chat_wheel" ) setApiHeaders(nil, request) local dataToSend = {chat_wheel = chatWheel} request:SetHTTPRequestRawPostBody( "application/json", json.encode(dataToSend) ) request:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then ____print( nil, "[CHAT_WHEEL] Chat wheel сохранен на сервере для игрока " .. tostring(playerId) ) else ____print( nil, "[CHAT_WHEEL] Ошибка сохранения chat_wheel: StatusCode=" .. tostring(result.StatusCode) ) end end) end function GameMode.prototype.saveSoundsWheelToServer(self, playerId, soundsWheel) local steamId = PlayerResource:GetSteamAccountID(playerId) if not steamId then return end local request = CreateHTTPRequestScriptVM( "PUT", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/sounds_wheel" ) setApiHeaders(nil, request) local dataToSend = {sounds_wheel = soundsWheel} request:SetHTTPRequestRawPostBody( "application/json", json.encode(dataToSend) ) request:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then ____print( nil, "[CHAT_WHEEL] Sounds wheel сохранен на сервере для игрока " .. tostring(playerId) ) else ____print( nil, "[CHAT_WHEEL] Ошибка сохранения sounds_wheel: StatusCode=" .. tostring(result.StatusCode) ) end end) end function GameMode.prototype.loadChatWheelSoundsFromServer(self, playerId) local steamId = PlayerResource:GetSteamAccountID(playerId) if not steamId then return end local chatWheelRequest = CreateHTTPRequestScriptVM( "GET", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/chat_wheel" ) setApiHeaders(nil, chatWheelRequest) chatWheelRequest:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then do local function ____catch(e) ____print( nil, "[CHAT_WHEEL] Ошибка парсинга chat_wheel: " .. tostring(e) ) end local ____try, ____hasReturned = pcall(function() local decoded = {json.decode(result.Body)} local responseData = nil if __TS__ArrayIsArray(decoded) and #decoded > 0 then responseData = decoded[1] elseif decoded and type(decoded) == "table" then responseData = decoded end if responseData and type(responseData) == "table" and responseData.chat_wheel ~= nil then local loadedChatWheel = responseData.chat_wheel or ({}) local playerInfoData = PlayerInfo:GetPlayerInfo(playerId) if not playerInfoData then playerInfoData = {} end playerInfoData.chat_wheel = loadedChatWheel PlayerInfo:UpdatePlayerInfo(playerId, playerInfoData) ____print( nil, "[CHAT_WHEEL] Загружен chat_wheel для игрока " .. tostring(playerId) ) end end) if not ____try then ____catch(____hasReturned) end end else ____print( nil, "[CHAT_WHEEL] Ошибка загрузки chat_wheel: StatusCode=" .. tostring(result.StatusCode) ) end end) local soundsWheelRequest = CreateHTTPRequestScriptVM( "GET", ((SERVER_CONFIG.API_URL .. "/player/") .. tostring(steamId)) .. "/sounds_wheel" ) setApiHeaders(nil, soundsWheelRequest) soundsWheelRequest:Send(function(result) if result.StatusCode >= 200 and result.StatusCode < 300 then do local function ____catch(e) ____print( nil, "[CHAT_WHEEL] Ошибка парсинга sounds_wheel: " .. tostring(e) ) end local ____try, ____hasReturned = pcall(function() local decoded = {json.decode(result.Body)} local responseData = nil if __TS__ArrayIsArray(decoded) and #decoded > 0 then responseData = decoded[1] elseif decoded and type(decoded) == "table" then responseData = decoded end if responseData and type(responseData) == "table" and responseData.sounds_wheel ~= nil then local loadedSoundsWheel = responseData.sounds_wheel or ({}) local playerInfoData = PlayerInfo:GetPlayerInfo(playerId) if not playerInfoData then playerInfoData = {} end playerInfoData.sounds_wheel = loadedSoundsWheel PlayerInfo:UpdatePlayerInfo(playerId, playerInfoData) ____print( nil, ((("[CHAT_WHEEL] Загружен sounds_wheel для игрока " .. tostring(playerId)) .. ": ") .. tostring(#__TS__ObjectKeys(loadedSoundsWheel))) .. " звуков" ) else local playerInfoData = PlayerInfo:GetPlayerInfo(playerId) if not playerInfoData then playerInfoData = {} end if not playerInfoData.sounds_wheel then playerInfoData.sounds_wheel = {} PlayerInfo:UpdatePlayerInfo(playerId, playerInfoData) ____print( nil, "[CHAT_WHEEL] sounds_wheel не найден на сервере, создан пустой объект для игрока " .. tostring(playerId) ) end end end) if not ____try then ____catch(____hasReturned) end end else ____print( nil, "[CHAT_WHEEL] Ошибка загрузки sounds_wheel: StatusCode=" .. tostring(result.StatusCode) ) end end) end GameMode = __TS__Decorate(GameMode, GameMode, {reloadable}, {kind = "class", name = "GameMode"}) ____exports.GameMode = GameMode return ____exports