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 = "Another sound is playing, please wait"})
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 = "Sound on cooldown. Remaining: " .. 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