72b73c4dd6
Allows the game client to make HTTP API calls from a listen server (local lobby) instead of requiring a Steam dedicated server. CreateHTTPRequestScriptVM has the exact same API signature but works in both dedicated server and listen server contexts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1834 lines
78 KiB
Lua
1834 lines
78 KiB
Lua
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("<font color='#FF69B4'>Добро пожаловать в игру хозяин Nasqreal!</font>", 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("<font color='#c10020'>ЧЁ ХОТЕЛ СПИЗИДТЬ ХУЕСОС?!?!?!? ПОСОСАЛ?!?!?!?</font>", 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
|