require( "map_encounter" ) require( "aghanim_utility_functions" ) require( "spawner" ) _G.BOSS_SPEECH_COOLDOWN = 7.0 _G.BOSS_LAUGH_COOLDOWN = 20.0 -------------------------------------------------------------------------------- if CMapEncounter_BossBase == nil then CMapEncounter_BossBase = class( {}, {}, CMapEncounter ) end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:constructor( hRoom, szEncounterName ) CMapEncounter.constructor( self, hRoom, szEncounterName ) self.bBossIntroduced = false self.bBossFightStarted = false self.bBossKilled = false self.flBossSpeechCooldown = -1 self.flBossNextLaughTime = -1 self.flBossIntroEndTime = 9999999999999999999999 self.Bosses = {} end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:OnThink() CMapEncounter.OnThink( self ) if self.bBossIntroduced == false then for nPlayerID = 0,AGHANIM_PLAYERS-1 do local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID ) if hPlayerHero then for _,hBoss in pairs( self.Bosses ) do if hPlayerHero:CanEntityBeSeenByMyTeam( hBoss ) then self:IntroduceBoss( hBoss ) return end end end end return end local flNow = GameRules:GetGameTime() if self.bBossIntroduced and self.flBossIntroEndTime > flNow then return end if not self.bBossFightStarted and flNow > self.flBossIntroEndTime then self:StartBossFight() return end if self.bBossFightStarted and flNow > self.flBossNextLaughTime then local szLaughLine = self:GetLaughLine() if szLaughLine ~= nil then self:BossSpeak( szLaughLine ) self.flBossNextLaughTime = flNow + BOSS_LAUGH_COOLDOWN end end for nPlayerID = 0,AGHANIM_PLAYERS-1 do local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID ) if hPlayerHero then local hVisionBuff = hPlayerHero:FindModifierByName( "modifier_provide_vision" ) if hVisionBuff == nil then for _,hBoss in pairs( self.Bosses ) do if hBoss and hBoss:IsNull() == false and hBoss:IsAlive() then hPlayerHero:AddNewModifier( hBoss, nil, "modifier_provide_vision", { duration = -1 } ) break end end end end end self:UpdateBossHP() end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:UpdateBossHP() local flHPResolution = 500.0; local netTable = {} local nEntIndex = -1; local szUnitName = self:GetBossUnitName(); local flMaxHP = 0 local flCurHP = 0 local flBossHP = 0 local bAnyActive = false for _,hBoss in pairs ( self.Bosses ) do if hBoss and not hBoss:IsNull() then if hBoss:IsAlive() then bAnyActive = true flCurHP = flCurHP + hBoss:GetHealth() end flMaxHP = flMaxHP + hBoss:GetMaxHealth() nEntIndex = hBoss:entindex() end end flBossHP = math.floor( flHPResolution * ( flCurHP / flMaxHP ) ) / flHPResolution; CustomNetTables:SetTableValue( "boss_net_table", "current_boss", { active=bAnyActive, hp = flBossHP, ent_index=nEntIndex, unit_name=szUnitName } ) end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:InitializeObjectives() CMapEncounter.InitializeObjectives( self ) end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossUnitName() return self:GetPreviewUnit() end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossUnits() return self.Bosses end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:OnBossSpawned( hBoss ) hBoss.bIsBoss = true hBoss:SetAbsAngles( 0, 270, 0 ) hBoss:AddNewModifier( hPlayerHero, nil, "modifier_boss_intro", {} ) hBoss:RemoveAbility( "ability_ascension" ) hBoss:RemoveModifierByName( "modifier_ascension" ) table.insert( self.Bosses, hBoss ) end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroGesture() return ACT_DOTA_VICTORY end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroCameraPitch() return 30 end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroCameraDistance() return 800 end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroCameraHeight() return 50 end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroCameraYawRotateSpeed() return 0.1 end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroCameraInitialYaw() return 120 end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroDuration() return 4.0 end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetBossIntroVoiceLine() return nil end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:IntroduceBoss( hEncounteredBoss ) self.bBossIntroduced = true for _,hBoss in pairs ( self.Bosses ) do hBoss:SetAbsAngles( 0, 270, 0 ) hBoss:AddNewModifier( hBoss, nil, "modifier_provide_vision", { duration = -1 } ) hBoss:StartGesture( self:GetBossIntroGesture() ) end for nPlayerID = 0,AGHANIM_PLAYERS-1 do local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID ) if hPlayerHero then hPlayerHero:AddNewModifier( hPlayerHero, nil, "modifier_boss_intro", { } ) end end self:AddEncounterObjective( tostring( "defeat_boss_" .. self:GetBossUnitName() ), 0, 0 ) local netTable = {} netTable[ "boss_ent_index" ] = hEncounteredBoss:entindex() netTable[ "camera_pitch" ] = self:GetBossIntroCameraPitch() netTable[ "camera_distance" ] = self:GetBossIntroCameraDistance() netTable[ "camera_height" ] = self:GetBossIntroCameraHeight() netTable[ "camera_yaw_rotate_speed" ] = self:GetBossIntroCameraYawRotateSpeed() netTable[ "camera_inital_yaw" ] = self:GetBossIntroCameraInitialYaw() self.flBossIntroEndTime = GameRules:GetGameTime() + self:GetBossIntroDuration() CustomGameEventManager:Send_ServerToAllClients( "boss_intro_begin", netTable ) if self:GetBossIntroVoiceLine() ~= nil then self:BossSpeak( self:GetBossIntroVoiceLine() ) end end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:StartBossFight() self.nAbilityListener = ListenToGameEvent( "dota_non_player_begin_cast", Dynamic_Wrap( getclass( self ), "OnNonPlayerBeginCast" ), self ) self.bBossFightStarted = true self:UpdateBossHP() for _,hBoss in pairs ( self.Bosses ) do --hBoss:RemoveModifierByName( "modifier_boss_intro" local hBuff = hBoss:FindModifierByName( "modifier_boss_intro" ) if hBuff then hBuff:SetDuration( 1.0, false ) end hBoss.Encounter = self hBoss:RemoveGesture( self:GetBossIntroGesture() ) end for nPlayerID = 0,AGHANIM_PLAYERS-1 do local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID ) if hPlayerHero then hPlayerHero:RemoveModifierByName( "modifier_boss_intro" ) end local hPlayer = PlayerResource:GetPlayer( nPlayerID ) if hPlayer then hPlayer:SetMusicStatus( 2, 1.0 ) -- turn on battle music end end CustomGameEventManager:Send_ServerToAllClients( "boss_intro_end", netTable ) end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetMaxSpawnedUnitCount() return #self.Bosses end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:Start() CMapEncounter.Start( self ) self.nGoldReward = 0 self.Bosses = {} self:GetSpawner( "spawner_boss" ):SpawnUnits() end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:OnSpawnerFinished( hSpawner, hSpawnedUnits ) CMapEncounter.OnSpawnerFinished( self, hSpawner, hSpawnedUnits ) if hSpawner:GetSpawnerName() == "spawner_boss" then for _,hUnit in pairs ( hSpawnedUnits ) do if hUnit then self:OnBossSpawned( hUnit ) end end end end --------------------------------------------------------------------------- function CMapEncounter_BossBase:OnEnemyCreatureSpawned( hEnemyCreature ) CMapEncounter.OnEnemyCreatureSpawned( self, hEnemyCreature ) end --------------------------------------------------------------------------- function CMapEncounter_BossBase:OnEntityKilled( event ) CMapEncounter.OnEntityKilled( self, event ) local hVictim = nil local hAttacker = nil local hInflictor = nil if event.entindex_killed ~= nil then hVictim = EntIndexToHScript( event.entindex_killed ) end if event.entindex_attacker ~= nil then hAttacker = EntIndexToHScript( event.entindex_attacker ) end if event.entindex_inflictor ~= nil then hInflictor = EntIndexToHScript( event.entindex_inflictor ) end if hVictim ~= nil then if hVictim.bIsBoss == true then self:OnBossKilled( hVictim, hAttacker ) end if hVictim:IsRealHero() and hAttacker.bIsBoss == true then self:BossSpeak( self:GetKillTauntLine() ) end end end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:OnBossKilled( hBoss, hAttacker ) for k,Boss in pairs ( self.Bosses ) do if hBoss == Boss then table.remove( self.Bosses, k ) end end if #self.Bosses == 0 then self.bBossKilled = true CustomGameEventManager:Send_ServerToAllClients( "boss_fight_finished", netTable ) for i=1,NUM_LIVES_FROM_BOSSES do self:DropLifeRuneFromUnit( hBoss, hAttacker, false ) end self:DropNeutralItemFromUnit( hBoss, hAttacker, true ) for nPlayerID = 0,AGHANIM_PLAYERS-1 do local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID ) if hPlayerHero then hPlayerHero:RemoveModifierByName( "modifier_provide_vision" ) end local hPlayer = PlayerResource:GetPlayer( nPlayerID ) if hPlayer then hPlayer:SetMusicStatus( 0, 1.0 ) -- go back to laning music end end end end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:OnComplete() CMapEncounter.OnComplete( self ) if self.nAbilityListener ~= nil then StopListeningToGameEvent( self.nAbilityListener ) end if self.bBossFightStarted == false then -- Fix for test encounter for nPlayerID = 0,AGHANIM_PLAYERS-1 do local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID ) if hPlayerHero then hPlayerHero:RemoveModifierByName( "modifier_boss_intro" ) end end self:UpdateBossHP() CustomGameEventManager:Send_ServerToAllClients( "boss_intro_end", netTable ) end end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:CheckForCompletion() if self.bBossKilled then return true end return false end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:DestroyRemainingSpawnedUnits() -- Necessary to make bosses drop loot in the case of win_encounter for i = #self.Bosses,1,-1 do self.Bosses[i]:ForceKill( false ) end CMapEncounter.DestroyRemainingSpawnedUnits( self ) end -------------------------------------------------------------------------------- function CMapEncounter_BossBase:GetLaughLine() return nil end --------------------------------------------------------------------------- function CMapEncounter_BossBase:GetKillTauntLine() return nil end --------------------------------------------------------------------------- function CMapEncounter_BossBase:GetAbilityUseLine( szAbilityName ) return nil end --------------------------------------------------------------------------- function CMapEncounter_BossBase:BossSpeak( szSoundEvent, bLaugh ) if szSoundEvent == nil then print( "CMapEncounter_BossBase:BossSpeak - szSoundEvent is nil! This might be ok if the boss doesn't have a response for this." ) return end local flNow = GameRules:GetGameTime() if flNow > self.flBossSpeechCooldown then self.flBossSpeechCooldown = GameRules:GetGameTime() + BOSS_SPEECH_COOLDOWN EmitGlobalSound( szSoundEvent ) end end --------------------------------------------------------- -- dota_non_player_begin_cast -- * abilityname -- * caster_entindex --------------------------------------------------------- function CMapEncounter_BossBase:OnNonPlayerBeginCast( event ) local hCaster = nil if event.caster_entindex ~= nil and event.abilityname ~= nil then hCaster = EntIndexToHScript( event.caster_entindex ) if hCaster ~= nil and hCaster.bIsBoss then self:BossSpeak( self:GetAbilityUseLine( event.abilityname ) ) end end end -------------------------------------------------------------------------------- return CMapEncounter_BossBase