Files
2021-10-24 15:36:18 -04:00

2299 lines
77 KiB
Lua
Executable File

if CAghanim == nil then
CAghanim = class({})
_G.CAghanim = CAghanim
end
------------------------------------------------------------------------------------------------------------------------------------------------------
-- Required .lua files, which help organize functions contained in our addon.
-- Make sure to call these beneath the mode's class creation.
------------------------------------------------------------------------------------------------------------------------------------------------------
require( "constants" ) -- require constants first
require( "aghanim_ability_upgrade_constants" ) -- lists of ability upgrades per hero
require( "aghanim_ability_upgrade_interface" ) -- upgrading abilities can go through the interface
require( "utility_functions" ) -- require utility_functions early (other required files may use its functions)
require( "aghanim_utility_functions" )
require( "precache" )
require( "blessings" )
require( "events" )
require( "filters" )
require( "room_tables" )
require( "ascension_levels" )
require( "triggers" )
require( "map_room" )
require( "rewards" )
require( "containers/breakable_containers_data" )
require( "containers/breakable_container_surprises" )
require( "containers/treasure_chest_data" )
require( "containers/treasure_chest_surprises" )
require( "containers/explosive_barrel_data" )
--require( "map_generation" )
--------------------------------------------------------------------------------
function Precache( context )
print( "Precaching Aghanim assets..." )
for _,Item in pairs( g_ItemPrecache ) do
PrecacheItemByNameSync( Item, context )
end
for Item,Price in pairs( PRICED_ITEM_REWARD_LIST ) do
PrecacheItemByNameSync( Item, context )
end
for i=1,#TREASURE_REWARDS do
for j=1,#TREASURE_REWARDS[i] do
PrecacheItemByNameSync( TREASURE_REWARDS[i][j], context )
end
end
for _, breakableData in ipairs( BreakablesData ) do
for i=1,#breakableData.CommonItems do
PrecacheItemByNameSync( breakableData.CommonItems[i], context )
end
for i=1,#breakableData.MonsterUnits do
PrecacheUnitByNameSync( breakableData.MonsterUnits[i], context, -1 )
end
for i=1,#breakableData.RareItems do
PrecacheItemByNameSync( breakableData.RareItems[i], context )
end
end
for _,Unit in pairs( g_UnitPrecache ) do
PrecacheUnitByNameSync( Unit, context, -1 )
end
for AbilityName,Ability in pairs( ASCENSION_ABILITIES ) do
-- Yes, it's not an item, but this works anyways since abilities are similar to items
PrecacheItemByNameSync( AbilityName, context )
end
for _,Model in pairs( g_ModelPrecache ) do
PrecacheResource( "model", Model, context )
end
for _,Particle in pairs( g_ParticlePrecache ) do
PrecacheResource( "particle", Particle, context )
end
for _,Sound in pairs( g_SoundPrecache ) do
PrecacheResource( "soundfile", Sound, context )
end
end
--------------------------------------------------------------------------------
-- Create the game mode when we activate
function Activate()
GameRules.Aghanim = CAghanim()
GameRules.Aghanim:InitGameMode()
LinkModifiers()
end
--------------------------------------------------------------------------------
function SpawnGroupPrecache( hSpawnGroup, context )
local room = GameRules.Aghanim:FindRoomBySpawnGroupHandle( hSpawnGroup )
if room ~= nil then
--print( "Precaching room " .. room:GetName() .. "..." )
room:GetEncounter():Precache( context )
end
end
--------------------------------------------------------------------------------
function LinkModifiers()
-- This is the modifier that pukeeps morty in the level
LinkLuaModifier( "modifier_bonus_room_start", "modifiers/modifier_bonus_room_start", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_morty_leash", "modifiers/modifier_morty_leash", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_provides_fow_position", "modifiers/modifier_provides_fow_position", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_sand_king_boss_caustic_finale", "modifiers/creatures/modifier_sand_king_boss_caustic_finale", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_breakable_container", "modifiers/modifier_breakable_container", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_boss_intro", "modifiers/modifier_boss_intro", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_attack_speed_unslowable", "modifiers/modifier_attack_speed_unslowable", LUA_MODIFIER_MOTION_NONE )
LinkLuaModifier( "modifier_move_speed_unslowable", "modifiers/modifier_move_speed_unslowable", LUA_MODIFIER_MOTION_NONE )
end
--------------------------------------------------------------------------------
function GetExitOptionData( nOptionNumber )
return GameRules.Aghanim:GetExitOptionData( nOptionNumber )
end
--------------------------------------------------------------------------------
function CAghanim:InitGameMode()
print( "Aghanim addon is loaded." )
self.CurrentRoom = nil
self.bStreamedStartingRoomExits = false
self.bIsInTournamentMode = false
self.nSeed = 0
self.bFastTestEncounter = false
if GameRules:GetGameModeEntity():GetEventWindowStartTime() > 0 then
self.nSeed = GameRules:GetGameModeEntity():GetEventGameSeed()
if self.nSeed > 0 then
self.bIsInTournamentMode = true
end
end
if self.nSeed == 0 then
self.nSeed = math.floor( GetSystemTimeMS() )
else
print( "Using fixed seed from the GC: " .. self.nSeed )
end
self.nAscensionLevel = 0
self.bHasSetAscensionLevel = false
self.bWonGame = false
self.bHasAnyNewPlayers = false
self.bHasSetNewPlayers = false
self.bHasInitializedSpectatorCameras = false
self.AghanimSummons = {}
self.hMapRandomStream = CreateUniformRandomStream( self.nSeed )
self.hPlayerRandomStreams = {}
math.randomseed( self.nSeed )
GameRules:GetGameModeEntity():SetAnnouncerDisabled( true )
GameRules:SetCustomGameSetupTimeout( 0 )
GameRules:SetCustomGameSetupAutoLaunchDelay( 0 )
GameRules:SetCustomGameTeamMaxPlayers( DOTA_TEAM_GOODGUYS, 1 )
GameRules:SetCustomGameTeamMaxPlayers( DOTA_TEAM_BADGUYS, 0 )
GameRules:SetTimeOfDay( 0.25 )
GameRules:SetStrategyTime( 0.0 )
GameRules:SetShowcaseTime( 0.0 )
GameRules:SetPreGameTime( 5.0 )
GameRules:SetPostGameTime( 45.0 )
GameRules:SetHeroSelectionTime( 90 )
GameRules:SetTreeRegrowTime( 60.0 )
GameRules:SetStartingGold( AGHANIM_STARTING_GOLD )
GameRules:SetGoldTickTime( 999999.0 )
GameRules:SetGoldPerTick( 0 )
GameRules:SetUseUniversalShopMode( true )
GameRules:GetGameModeEntity():SetRemoveIllusionsOnDeath( true )
GameRules:GetGameModeEntity():SetDaynightCycleDisabled( true )
GameRules:GetGameModeEntity():SetStashPurchasingDisabled( true )
GameRules:GetGameModeEntity():SetRandomHeroBonusItemGrantDisabled( true )
GameRules:GetGameModeEntity():SetDefaultStickyItem( "item_boots" )
GameRules:GetGameModeEntity():SetForceRightClickAttackDisabled( true )
GameRules:GetGameModeEntity():DisableClumpingBehaviorByDefault( true )
GameRules:GetGameModeEntity():SetMinimumAttackSpeed( 0.4 )
GameRules:GetGameModeEntity():SetNeutralStashTeamViewOnlyEnabled( true )
GameRules:GetGameModeEntity():SetNeutralItemHideUndiscoveredEnabled( true )
--Temp for tesitng new lives rules
if AGHANIM_TIMED_RESPAWN_MODE == true then
GameRules:GetGameModeEntity():SetBuybackEnabled( false )
GameRules:GetGameModeEntity():SetFixedRespawnTime( AGHANIM_TIMED_RESPAWN_TIME )
else
GameRules:GetGameModeEntity():SetCustomBuybackCooldownEnabled( true )
GameRules:GetGameModeEntity():SetCustomBuybackCostEnabled( true )
GameRules:SetHeroRespawnEnabled( false )
end
GameRules:GetGameModeEntity():SetLoseGoldOnDeath( false )
GameRules:GetGameModeEntity():SetFriendlyBuildingMoveToEnabled( true )
GameRules:GetGameModeEntity():SetHudCombatEventsDisabled( true )
GameRules:GetGameModeEntity():SetWeatherEffectsDisabled( true )
GameRules:GetGameModeEntity():SetCameraSmoothCountOverride( 2 )
GameRules:GetGameModeEntity():SetSelectionGoldPenaltyEnabled( false )
GameRules:GetGameModeEntity():SetUnseenFogOfWarEnabled( true )
GameRules:GetGameModeEntity():SetTPScrollSlotItemOverride( "item_bottle" )
GameRules:GetGameModeEntity():SetSendToStashEnabled( false )
self.hFowBlockerRegion = GameRules:GetGameModeEntity():AllocateFowBlockerRegion( -16384, -16384, 16384, 16384, 128 )
GameRules:SetCustomGameAllowHeroPickMusic( false )
GameRules:SetCustomGameAllowBattleMusic( false )
GameRules:SetCustomGameAllowMusicAtGameStart( true )
-- Make the camera not z clip
GameRules:GetGameModeEntity():SetCameraZRange( 11, 3800 )
-- Event Registration: Functions are found in dungeon_events.lua
ListenToGameEvent( "game_rules_state_change", Dynamic_Wrap( CAghanim, 'OnGameRulesStateChange' ), self )
ListenToGameEvent( "player_connect_full", Dynamic_Wrap( CAghanim, 'OnPlayerConnected' ), self )
ListenToGameEvent( "dota_player_reconnected", Dynamic_Wrap( CAghanim, 'OnPlayerReconnected' ), self )
ListenToGameEvent( "hero_selected", Dynamic_Wrap( CAghanim, 'OnHeroSelected' ), self )
ListenToGameEvent( "npc_spawned", Dynamic_Wrap( CAghanim, "OnNPCSpawned" ), self )
ListenToGameEvent( "entity_killed", Dynamic_Wrap( CAghanim, 'OnEntityKilled' ), self )
ListenToGameEvent( "dota_player_gained_level", Dynamic_Wrap( CAghanim, "OnPlayerGainedLevel" ), self )
ListenToGameEvent( "dota_item_picked_up", Dynamic_Wrap( CAghanim, "OnItemPickedUp" ), self )
ListenToGameEvent( "dota_holdout_revive_complete", Dynamic_Wrap( CAghanim, "OnPlayerRevived" ), self )
ListenToGameEvent( "dota_buyback", Dynamic_Wrap( CAghanim, "OnPlayerBuyback" ), self )
ListenToGameEvent( "dota_item_spawned", Dynamic_Wrap( CAghanim, "OnItemSpawned" ), self )
ListenToGameEvent( "dota_item_purchased", Dynamic_Wrap( CAghanim, "OnItemPurchased" ), self )
--ListenToGameEvent( "dota_non_player_used_ability", Dynamic_Wrap( CAghanim, "OnNonPlayerUsedAbility" ), self )
ListenToGameEvent( "trigger_start_touch", Dynamic_Wrap( CAghanim, "OnTriggerStartTouch" ), self )
ListenToGameEvent( "trigger_end_touch", Dynamic_Wrap( CAghanim, "OnTriggerEndTouch" ), self )
ListenToGameEvent( "aghsfort_path_selected", Dynamic_Wrap( CAghanim, "OnNextRoomSelected" ), self )
ListenToGameEvent( "dota_hero_entered_shop", Dynamic_Wrap( CAghanim, "OnHeroEnteredShop" ), self )
ListenToGameEvent( "dota_player_team_changed", Dynamic_Wrap( CAghanim, "OnPlayerTeamChanged" ), self )
ListenToGameEvent( "player_chat", Dynamic_Wrap(CAghanim, "PlayerChat") , self)
-- Filter Registration: Functions are found in filters.lua
--GameRules:GetGameModeEntity():SetHealingFilter( Dynamic_Wrap( CAghanim, "HealingFilter" ), self )
--GameRules:GetGameModeEntity():SetDamageFilter( Dynamic_Wrap( CAghanim, "DamageFilter" ), self )
--GameRules:GetGameModeEntity():SetItemAddedToInventoryFilter( Dynamic_Wrap( CAghanim, "ItemAddedToInventoryFilter" ), self )
GameRules:GetGameModeEntity():SetModifierGainedFilter( Dynamic_Wrap( CAghanim, "ModifierGainedFilter" ), self )
self.nCrystalsLeft = 5
self.PlayerCrystals = {}
self.PlayerCurrentRooms = {}
for nPlayerID = 0, AGHANIM_PLAYERS-1 do
PlayerResource:SetCustomTeamAssignment( nPlayerID, DOTA_TEAM_GOODGUYS )
table.insert( self.PlayerCurrentRooms, nPlayerID , {} )
table.insert( self.PlayerCrystals, nPlayerID , {} )
end
for szHeroName,HeroUpgrades in pairs ( MINOR_ABILITY_UPGRADES ) do
for k,v in pairs ( HeroUpgrades ) do
v[ "id" ] = k
end
--PrintTable( HeroUpgrades, szHeroName .. ": " )
end
GameRules:GetGameModeEntity():SetThink( "OnThink", self, "GlobalThink", 0.5 )
-- Used to display the blessings
CustomNetTables:SetTableValue( "game_global", "blessings", {} )
-- parse dev mode starting flags
self._bDevMode = (GameRules:GetGameSessionConfigValue("DevMode", "false") == "true")
self._szDevHero = GameRules:GetGameSessionConfigValue("DevHero", nil)
self._szDevEncounter = GameRules:GetGameSessionConfigValue("DevEncounter", nil)
if self._bDevMode then
GameRules:SetHeroSelectionTime( 20.0 )
GameRules:SetHeroSelectPenaltyTime( 0.0 )
GameRules:SetPostGameTime( 10.0 )
end
self:RegisterConCommands()
self.nNumViableRoomsForItems = NUM_VIABLE_ROOMS_FOR_DROPPED_ITEMS
self.nNumNeutralItems = NUM_NEUTRAL_ITEMS_DROPPED
self.DroppedNeutralItems = {}
self:InitScoreboardInfo()
self:InitPlayerInfo()
self:AllocateRoomLayout()
self:AssignEncountersToRooms()
self:SetupSpawnLocations()
-- Mark the first room as loaded, and start streaming the exit rooms immediately
local room = self:GetStartingRoom()
if room~= nil then
room:OnSpawnRoomComplete( room:GetSpawnGroupHandle() )
end
-- Listener for the ability upgrade
CustomGameEventManager:RegisterListener( "ability_upgrade_button_clicked", function(...) return self:OnAbilityUpgradeButtonClicked( ... ) end )
self.bTestingAbilityUpgrades = false
-- Listener for reward choice
CustomGameEventManager:RegisterListener( "reward_choice", function(...) return OnRewardChoice( ... ) end )
if self.bIsInTournamentMode == true then
self:SetAscensionLevel( 1 )
print( "Tournament game difficulty is " .. self:GetAscensionLevel() )
else
local nCustomGameDifficulty = GameRules:GetCustomGameDifficulty()
if nCustomGameDifficulty > 0 then
print( "Lobby game difficulty is " .. nCustomGameDifficulty )
self:SetAscensionLevel( nCustomGameDifficulty - 1 )
end
end
-- Create announcer Unit
local dummyTable =
{
MapUnitName = "npc_dota_announcer_aghanim",
teamnumber = DOTA_TEAM_GOODGUYS,
}
CreateUnitFromTable( dummyTable, Vector( 0, 0, 0 ) )
self:InitializeMetagame()
self.BristlebackItems = {}
end
---------------------------------------------------------
-- function CAghanim:showworldranklisk( keys )
-- local worldranklist = {rank01,rank02,rank03,rank04,rank05,rand06,rank07,rank08,rank09,rank10}
-- rank01 = {"playerid","3600","diffcult","heroid","4800"}
-- local gametime = GameRules:GetGameTime()
-- if gametime == 10 then
-- print("30")
-- GameRules:SendCustomMessage(string.format("01 : %s",rank01[00])+string.format("time : %d",rank01[01])+string.format("diffcult : %d",rank01[02])+string.format("hero : %d",rank01[03])+string.format("score : %d",rank01[04]),DOTA_TEAM_GOODGUYS, 0)
-- end
-- return 1
-- end
--聊天检测
---------------------------------------------------------
local expertmode = false
function CAghanim:PlayerChat( keys )
print("PlayerSay")
DeepPrintTable(keys)
local worldranklist = {rank01,rank02,rank03,rank04,rank05,rand06,rank07,rank08,rank09,rank10}
rank01 = {"playerid","3600","diffcult","heroid","4800"}
local chattext = keys.text
local gametime = GameRules:GetGameTime()
if gametime < 120 then
if chattext == "expert"
then
expertmode = true
_G.AGHANIM_MAX_LIVES = 1
_G.HEALTH_POTION_DROP_PCT = 15
_G.MANA_POTION_DROP_PCT = 15
_G.ELITE_VALUE_MODIFIER = 3
print("expertmode on")
GameRules:SendCustomMessage("expertmode on", DOTA_TEAM_GOODGUYS, 0)
-- CustomGameEventManager:Send_ServerToAllClients("bullet", {
-- player_id = keys.playerid
-- })
return true
end
if chattext == "worldrank" then
GameRules:SendCustomMessage(string.format("01---time---diffcult---hero---score"),DOTA_TEAM_GOODGUYS, 0)
GameRules:SendCustomMessage(string.format("%s---%s---%s---%s---%s",rank01[1],rank01[2], rank01[3], rank01[4],rank01[5]),DOTA_TEAM_GOODGUYS, 0)
return true
end
else return false
end
end
--------------------------------------------------------------------------------
function CAghanim:GetRandomSeed( )
return self.nSeed
end
--------------------------------------------------------------------------------
function CAghanim:GetHeroRandomStream( nPlayerID )
local hStream = self.hPlayerRandomStreams[ tostring( nPlayerID ) ]
if hStream ~= nil then
return hStream
end
local nHeroID = PlayerResource:GetSelectedHeroID( nPlayerID )
if nHeroID == 0 then
print( "GetHeroRandomStream: Warning! Encountered hero id " .. nHeroID )
end
local hStream = CreateUniformRandomStream( self.nSeed + nHeroID )
self.hPlayerRandomStreams[ tostring( nPlayerID ) ] = hStream
return hStream
end
--------------------------------------------------------------------------------
function CAghanim:SetAnnouncer( hAnnouncer )
self.hAnnouncer = hAnnouncer
end
--------------------------------------------------------------------------------
function CAghanim:GetAnnouncer( )
return self.hAnnouncer
end
--------------------------------------------------------------------------------
function CAghanim:InitPlayerInfo()
self.playerInfo = {}
local hEventGameDetails = GetLobbyEventGameDetails()
print("[Aghanim] EventGameDetails table:")
if hEventGameDetails == nil then
print("NOT FOUND!!")
hEventGameDetails = {}
end
DeepPrintTable(hEventGameDetails)
for nPlayerID = 0, AGHANIM_PLAYERS - 1 do
local szAccountID = tostring( PlayerResource:GetSteamAccountID( nPlayerID ) )
local hPlayerDetails = {}
local szPlayerRecord = string.format( "Player%d", nPlayerID )
if hEventGameDetails[szPlayerRecord] ~= nil then
local szRecordAccountID = hEventGameDetails[szPlayerRecord]['account_id']
if szRecordAccountID ~= nil and szRecordAccountID == szAccountID then
hPlayerDetails = hEventGameDetails[szPlayerRecord]
end
end
local info =
{
nBPCapTotal = hPlayerDetails["pointcap_total"] or 0,
nBPCapRemaining = hPlayerDetails["pointcap_remaining"] or 0,
nArcaneFragmentCapTotal = hPlayerDetails["premium_pointcap_total"] or 0,
nArcaneFragmentCapRemaining = hPlayerDetails["premium_pointcap_remaining"] or 0,
}
self.playerInfo[ tostring( nPlayerID ) ] = info
end
end
--------------------------------------------------------------------------------
function CAghanim:CanPlayersAcceptCurrency( bBattlePoints )
if bBattlePoints == false then
return true
end
local connectedPlayers = self:GetConnectedPlayers()
for i=1,#connectedPlayers do
local nPlayerID = connectedPlayers[i]
local player = self.playerInfo[ tostring( nPlayerID ) ]
if player ~= nil then
if player.nBPCapRemaining > 0 then
return true
end
end
end
return false
end
--------------------------------------------------------------------------------
function CAghanim:RegisterCurrencyGrant( nPlayerID, nPoints, bBattlePoints )
local player = self.playerInfo[ tostring( nPlayerID ) ]
if player == nil then
return 0
end
if bBattlePoints == true then
if nPoints > player.nBPCapRemaining then
nPoints = player.nBPCapRemaining
end
player.nBPCapRemaining = player.nBPCapRemaining - nPoints
else
local nBonusPoints = nPoints * 2
if nBonusPoints > player.nArcaneFragmentCapRemaining then
nBonusPoints = player.nArcaneFragmentCapRemaining
end
player.nArcaneFragmentCapRemaining = player.nArcaneFragmentCapRemaining - nBonusPoints
nPoints = nPoints + nBonusPoints
end
return nPoints
end
--------------------------------------------------------------------------------
function CAghanim:GetPointsCapRemaining( nPlayerID, bBattlePoints, bTotal )
local player = self.playerInfo[ tostring( nPlayerID ) ]
if player == nil then
return 0
end
if bBattlePoints == true then
if bTotal == false then
return player.nBPCapRemaining
else
return player.nBPCapTotal
end
end
if bTotal == false then
return player.nArcaneFragmentCapRemaining
else
return player.nArcaneFragmentCapTotal
end
end
--------------------------------------------------------------------------------
function CAghanim:InitScoreboardInfo()
for nPlayerID = 0, AGHANIM_PLAYERS - 1 do
local kv =
{
kills = 0,
death_count = 0,
gold_bags = 0
}
CustomNetTables:SetTableValue( "aghanim_scores", tostring( nPlayerID ), kv )
end
end
--------------------------------------------------------------------------------
function CAghanim:RegisterPlayerKillStat( nPlayerID, nDepth )
local scores = CustomNetTables:GetTableValue( "aghanim_scores", tostring(nPlayerID) )
scores.kills = scores.kills + 1
CustomNetTables:SetTableValue( "aghanim_scores", tostring(nPlayerID), scores )
local szRoomDepth = tostring( nDepth )
self:EnsurePlayerStatAtDepth( nPlayerID, szRoomDepth )
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].kills =
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].kills + 1
end
--------------------------------------------------------------------------------
function CAghanim:RegisterGoldBagCollectedStat( nPlayerID )
local scores = CustomNetTables:GetTableValue( "aghanim_scores", tostring(nPlayerID) )
scores.gold_bags = scores.gold_bags + 1
CustomNetTables:SetTableValue( "aghanim_scores", tostring(nPlayerID), scores )
if self:GetCurrentRoom() ~= nil then
local szRoomDepth = tostring( self:GetCurrentRoom():GetDepth() )
self:EnsurePlayerStatAtDepth( nPlayerID, szRoomDepth )
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].gold_bags =
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].gold_bags + 1
end
end
--------------------------------------------------------------------------------
function CAghanim:GetNewPlayerList( )
local vecPlayerIDs = {}
for nPlayerID = 0, AGHANIM_PLAYERS - 1 do
if PlayerResource:IsValidPlayerID( nPlayerID ) and PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
local nGamePlayedCount = PlayerResource:GetEventGameCustomActionClaimCountByName( nPlayerID, "ti10_event_game_num_games_played" )
if nGamePlayedCount < 3 then
table.insert( vecPlayerIDs, nPlayerID )
end
end
end
return vecPlayerIDs
end
--------------------------------------------------------------------------------
function CAghanim:ReassignTrapRoomToNormalEncounter( nAct )
local hRoom = nil
for _,room in pairs(self.rooms) do
if room:GetAct() == nAct and room:GetType() == ROOM_TYPE_TRAPS then
hRoom = room
break
end
end
if hRoom == nil then
return
end
-- Reassign this room back to a normal room
hRoom.nRoomType = ROOM_TYPE_ENEMY
-- Select a new encounter, and make sure no room has the same encounter on both exits
local vecSiblingRoomNames = self:GetSiblingRoomNames( hRoom:GetName() )
local vecEncounterOptions = {}
for encounterName,encounterDef in pairs(ENCOUNTER_DEFINITIONS) do
if not self:IsEncounterDefAppropriateForRoom( encounterDef, hRoom ) then
goto continue
end
for s=1,#vecSiblingRoomNames do
local szOtherEncounterName = self.rooms[ vecSiblingRoomNames[s] ]:GetEncounterName()
if szOtherEncounterName == encounterName then
goto continue
end
end
table.insert( vecEncounterOptions, encounterName )
::continue::
end
if #vecEncounterOptions > 0 then
local nPick = self.hMapRandomStream:RandomInt( 1, #vecEncounterOptions )
--print( "Replacing trap encounter " .. hRoom:GetEncounterName() .. " with encounter " .. vecEncounterOptions[nPick] .. " in room " .. hRoom:GetName() )
hRoom:AssignEncounter( vecEncounterOptions[nPick] )
hRoom:GetEncounter():SelectAscensionAbilities()
end
end
--------------------------------------------------------------------------------
function CAghanim:ComputeHasNewPlayers()
if self.bHasSetNewPlayers == true then
return
end
if PlayerResource:HasSetEventGameCustomActionClaimCount() == false then
return
end
self.bHasSetNewPlayers = true
-- Determine whether there are any new players
local vecPlayerIDs = self:GetNewPlayerList( )
self.bHasAnyNewPlayers = ( #vecPlayerIDs > 0 )
-- Show new player popup for new players
CustomNetTables:SetTableValue( "game_global", "new_players", vecPlayerIDs )
if self.bHasAnyNewPlayers == true and self:GetAscensionLevel() == 0 then
self:ReassignTrapRoomToNormalEncounter( 1 )
end
print( "New players " .. tostring( self.bHasAnyNewPlayers ) )
-- Can't do this until we know whether we have new players
self:GetAnnouncer():OnHeroSelectionStarted()
end
--------------------------------------------------------------------------------
function CAghanim:OnHeroSelectionStarted()
end
--------------------------------------------------------------------------------
function CAghanim:HasAnyNewPlayers()
return self.bHasAnyNewPlayers
end
--------------------------------------------------------------------------------
function CAghanim:IsInTournamentMode()
return self.bIsInTournamentMode
end
--------------------------------------------------------------------------------
function CAghanim:GetPlayerCurrentRoom( nPlayerID )
return self.PlayerCurrentRooms[ nPlayerID ]
end
--------------------------------------------------------------------------------
function CAghanim:ClampMinimapToRoom( nPlayerID, hRoom )
local flSize = hRoom:GetMaxs().x - hRoom:GetMins().x
local flSizeY = hRoom:GetMaxs().y - hRoom:GetMins().y
if flSizeY > flSize then
flSize = flSizeY
end
local netTable = {}
netTable[ "room_name" ] = hRoom:GetName()
netTable[ "map_name" ] = hRoom:GetMapName()
netTable[ "x" ] = hRoom:GetOrigin().x
netTable[ "y" ] = hRoom:GetOrigin().y
netTable[ "size" ] = flSize
netTable[ "scale" ] = 8
if hRoom:GetType() == ROOM_TYPE_BOSS then
netTable[ "scale" ] = 4
end
if hRoom:GetName() == "a2_transition" then
netTable[ "scale" ] = 2
end
if hRoom:GetType() == ROOM_TYPE_STARTING then
netTable[ "map_name" ] = "main"
end
CustomNetTables:SetTableValue( "game_global", "minimap_info" .. nPlayerID, netTable )
end
--------------------------------------------------------------------------------
function CAghanim:SetPlayerCurrentRoom( nPlayerID, hRoom )
self.PlayerCurrentRooms[ nPlayerID ] = hRoom
self:ClampMinimapToRoom( nPlayerID, hRoom )
end
--------------------------------------------------------------------------------
function CAghanim:GetStartingRoom()
for _,room in pairs(self.rooms) do
if room:GetAct() == 1 and room:GetType() == ROOM_TYPE_STARTING then
return room
end
end
return nil
end
--------------------------------------------------------------------------------
function CAghanim:RegisterConCommands()
print("Registering ConCommands...");
local eCommandFlags = FCVAR_CHEAT
Convars:RegisterCommand( "test_room_reward", function( ... ) return TestRoomRewardConsoleCommand(...) end, "Usage: test_room_reward <nRoomDepth> <bIsElite>", eCommandFlags );
-- use aghanim_ability_upgrades to open the ability upgrade dev interface
Convars:RegisterCommand( "aghanim_ability_upgrades", function(...) return self:TestAbilityUpgradesUICC( ... ) end, "", eCommandFlags )
Convars:RegisterCommand( "win_encounter", function(...) return self:Dev_WinEncounter( ... ) end, "Completes the current encounter.", eCommandFlags )
Convars:RegisterCommand( "win_game", function(...) return self:Dev_WinGame( ... ) end, "Completes the current game.", eCommandFlags )
Convars:RegisterCommand( "set_ascension_level", function(...) return self:Dev_SetAscensionLevel( ... ) end, "Sets the current ascension level", eCommandFlags )
Convars:RegisterCommand( "extra_lives", function(...) return self:Dev_ExtraLives( ... ) end, "Completes the current encounter.", eCommandFlags )
Convars:RegisterCommand( "aghanim_test_encounter", function(...) return self:Dev_TestEncounter( ... ) end, "Tests a specific encounter at a specific level", eCommandFlags )
Convars:RegisterCommand( "fast_test_encounter", function(...) self.bFastTestEncounter = true; return self:Dev_TestEncounter( ... ) end, "Tests a specific encounter at a specific level", eCommandFlags )
Convars:RegisterCommand( "set_new_players", function(...) return self:Dev_SetNewPlayers( ... ) end, "Sets whether there are new players or not", eCommandFlags )
end
--------------------------------------------------------------------------------
function CAghanim:SetAscensionLevel( nLevel )
if nLevel < 0 then
nLevel = 0
elseif nLevel > self:GetMaxAllowedAscensionLevel() then
nLevel = self:GetMaxAllowedAscensionLevel()
end
print( 'Setting Ascension Level to ' .. nLevel )
self.nAscensionLevel = nLevel
self.bHasSetAscensionLevel = true
CustomNetTables:SetTableValue( "game_global", "ascension_level", { nLevel } )
-- Assign elite rooms, since the #s are dependent on the ascension level
local vecEliteRooms = { {}, {}, {} }
-- Which rooms can be elite?
for k,roomDef in pairs(MAP_ATLAS) do
-- Clear out any previous state
local hRoom = self.rooms[ roomDef.name ]
hRoom:SetEliteDepthBonus( 0 )
-- No elites at depth 2 for ascension 0 or 1
local bSuppress = ( nLevel <= 1 ) and ( roomDef.nDepth == 2 )
if bSuppress == false and not roomDef.bCannotBeElite and hRoom:GetType() == ROOM_TYPE_ENEMY then
table.insert( vecEliteRooms[ hRoom.nAct ], roomDef.name )
end
end
for nAct=1,3 do
-- Assign elite rooms
for nEliteRoom=1, MAP_ATLAS_ELITE_ROOMS_PER_ACT[nLevel+1][nAct] do
if #vecEliteRooms[nAct] == 0 then
break
end
local nPick = self.hMapRandomStream:RandomInt( 1, #vecEliteRooms[nAct] )
local szEliteRoom = vecEliteRooms[nAct][nPick]
--print( "Selecting elite room " .. szEliteRoom )
self.rooms[ szEliteRoom ]:SetEliteDepthBonus( 1 )
self.rooms[ szEliteRoom ]:SendRoomToClient()
table.remove( vecEliteRooms[nAct], nPick )
-- Make sure no room has 2 elite exits at asc 0 and 1
if nLevel <= 1 then
local vecSiblingRoomNames = self:GetSiblingRoomNames( szEliteRoom )
for s=1,#vecSiblingRoomNames do
for h=1,#vecEliteRooms[nAct] do
if vecEliteRooms[nAct][h] == vecSiblingRoomNames[s] then
--print( "Removing elite room option " .. vecEliteRooms[nAct][h] )
table.remove( vecEliteRooms[nAct], h )
break
end
end
end
end
end
end
-- Now that we know our ascension level and eliteness, we can pick the abilities we want to use
for k,room in pairs(self.rooms) do
room:GetEncounter():SelectAscensionAbilities()
end
end
--------------------------------------------------------------------------------
function CAghanim:GetMaxAllowedAscensionLevel( )
return 3
end
--------------------------------------------------------------------------------
function CAghanim:GetAscensionLevel( )
return self.nAscensionLevel
end
--------------------------------------------------------------------------------
function CAghanim:HasSetAscensionLevel( )
return self.bHasSetAscensionLevel
end
--------------------------------------------------------------------------------
function CAghanim:DepthHasEliteEncounters( nDepth )
local bSkippedOptions = false
for encounterName,encounterDef in pairs(ENCOUNTER_DEFINITIONS) do
if ( nDepth >= encounterDef.nMinDepth and nDepth <= encounterDef.nMaxDepth ) then
-- if encounterDef.nMaxEliteRank ~= nil and encounterDef.nMaxEliteRank > 0 then
if encounterDef.nEncounterType == ROOM_TYPE_ENEMY then
return true
end
end
end
return false
end
--------------------------------------------------------------------------------
function CAghanim:GetSiblingRoomNames( szRoomName )
-- Find the room(s) that can be entered from the same room as hRoom can be
local hParentRooms = {}
for _,otherRoom in pairs(self.rooms) do
local vecExits = otherRoom:GetExits()
for _,exit in pairs(vecExits) do
if exit == szRoomName then
table.insert( hParentRooms, otherRoom )
end
end
end
local szOtherExits = {}
for i=1,#hParentRooms do
local vecExits = hParentRooms[i]:GetExits()
for _,szExit in pairs(vecExits) do
if szExit ~= szRoomName then
table.insert( szOtherExits, szExit )
end
end
end
return szOtherExits
end
--------------------------------------------------------------------------------
-- Assign room reward
function CAghanim:AssignRoomReward( szRoomName, RewardPossibilites )
local flRoll = self.hMapRandomStream:RandomFloat( 0, 100.0 )
local flThreshold = 0.0
for k,v in pairs( RewardPossibilites ) do
flThreshold = flThreshold + v
if flRoll <= flThreshold then
szRewardResult = k
break
end
end
local nExitRoomType = MAP_ATLAS[ szRoomName ].nRoomType
if nExitRoomType == ROOM_TYPE_BOSS then
if szRoomName ~= "a3_boss" then
szRewardResult = "REWARD_TYPE_GOLD"
else
szRewardResult = "REWARD_TYPE_NONE"
end
elseif nExitRoomType == ROOM_TYPE_BONUS then
szRewardResult = "REWARD_TYPE_GOLD"
elseif nExitRoomType == ROOM_TYPE_TRANSITIONAL or nExitRoomType == ROOM_TYPE_STARTING then
szRewardResult = "REWARD_TYPE_NONE"
end
--print( "Setting Room Reward for " .. szRoomName .. " to " .. szRewardResult )
return szRewardResult
end
--------------------------------------------------------------------------------
-- Allocates the room layout
function CAghanim:AllocateRoomLayout()
self.bMapFlipped = false --( self.hMapRandomStream:RandomInt( 0, 1 ) == 1 )
local vecPotentialTrapRooms = { {}, {}, {} }
local vecHiddenRooms = { {}, {}, {} }
self.nMaxDepth = 0
-- Assign room positions + exits, given flip horizontal logic
self.rooms = {}
self.RoomRewards = {}
for k,roomDef in pairs(MAP_ATLAS) do
local fHeightOffset = 512
local vCenter = Vector( roomDef.vCenter.x, roomDef.vCenter.y, roomDef.vCenter.z + fHeightOffset )
if self.bMapFlipped then
vCenter.x = -vCenter.x
end
if roomDef.nRoomType == ROOM_TYPE_ENEMY and roomDef.nDepth > self.nMaxDepth then
self.nMaxDepth = roomDef.nDepth
end
local vMins = vCenter - roomDef.vSize / 2
local vMaxs = vCenter + roomDef.vSize / 2
self.rooms[ roomDef.name ] = CMapRoom( roomDef.name, roomDef.nRoomType, roomDef.nDepth, vMins, vMaxs, vCenter )
local nGoldValue = ENCOUNTER_DEPTH_GOLD_REWARD[ roomDef.nDepth + 1 ]
local RewardPossibilites = deepcopy( ROOM_CHOICE_REWARDS )
ShuffleListInPlace( RewardPossibilites, self.hMapRandomStream )
-- If the side exit was previously assigned, remove it from the list of possible rewards
if self.RoomRewards[ roomDef.exit_side ] ~= nil then
RewardPossibilites[ self.RoomRewards[ roomDef.exit_side ] ] = nil
NormalizeFloatArrayInPlace( RewardPossibilites, 100.0 )
end
if ( roomDef.exit_up ~= nil ) then
self.rooms[ roomDef.name ]:AddExit( ROOM_EXIT_TOP, roomDef.exit_up )
if self.RoomRewards[ roomDef.exit_up ] == nil then
self.RoomRewards[ roomDef.exit_up ] = self:AssignRoomReward( roomDef.exit_up, RewardPossibilites )
end
-- Remove the reward assigned to exit_up as a possibility for exit_side
if self.RoomRewards[ roomDef.exit_up ] ~= nil then
RewardPossibilites[ self.RoomRewards[ roomDef.exit_up ] ] = nil
end
NormalizeFloatArrayInPlace( RewardPossibilites, 100.0 )
end
if ( roomDef.exit_side ~= nil ) then
-- Deal with map flip
local exitDirection = ROOM_EXIT_LEFT
if MAP_ATLAS[ roomDef.exit_side ].vCenter.x < roomDef.vCenter.x then
if self.bMapFlipped then
exitDirection = ROOM_EXIT_RIGHT
end
else
if not self.bMapFlipped then
exitDirection = ROOM_EXIT_RIGHT
end
end
self.rooms[ roomDef.name ]:AddExit( exitDirection, roomDef.exit_side )
if self.RoomRewards[ roomDef.exit_side ] == nil then
self.RoomRewards[ roomDef.exit_side ] = self:AssignRoomReward( roomDef.exit_side, RewardPossibilites )
end
end
if ( roomDef.nRoomType == ROOM_TYPE_ENEMY ) and ( roomDef.bCannotBeTrap == nil or roomDef.bCannotBeTrap == false ) then
table.insert( vecPotentialTrapRooms[ self.rooms[ roomDef.name ].nAct ], roomDef.name )
end
local bSuppress = ( roomDef.nDepth == 2 )
if ( not roomDef.bCannotBeElite ) and ( bSuppress == false ) then
table.insert( vecHiddenRooms[ self.rooms[ roomDef.name ].nAct ], roomDef.name )
end
end
for nAct=1,3 do
-- Assign trap rooms
for nTrapRoom=1, MAP_TRAP_ROOMS_PER_ACT[nAct] do
local nPick = self.hMapRandomStream:RandomInt( 1, #vecPotentialTrapRooms[nAct] )
--print( "Selecting trap room option " .. vecPotentialTrapRooms[nAct][nPick] )
self.rooms[ vecPotentialTrapRooms[nAct][nPick] ].nRoomType = ROOM_TYPE_TRAPS
table.remove( vecPotentialTrapRooms[nAct], nPick )
end
-- Assign hidden rooms
for nHiddenRoom=1, MAP_HIDDEN_ENCOUNTERS_PER_ACT[nAct] do
local nPick = self.hMapRandomStream:RandomInt( 1, #vecHiddenRooms[nAct] )
local szHiddenRoomName = vecHiddenRooms[nAct][nPick]
--print( "Selecting hidden room option " .. szHiddenRoomName )
self.rooms[ szHiddenRoomName ]:SetHidden( )
table.remove( vecHiddenRooms[nAct], nPick )
-- Make sure no room has 2 hidden exits
local vecSiblingRoomNames = self:GetSiblingRoomNames( szHiddenRoomName )
for s=1,#vecSiblingRoomNames do
for h=1,#vecHiddenRooms[nAct] do
if vecHiddenRooms[nAct][h] == vecSiblingRoomNames[s] then
--print( "Removing hidden room option " .. vecHiddenRooms[nAct][h] )
table.remove( vecHiddenRooms[nAct], h )
break
end
end
end
end
local nNumCrystalsPerAct = 2
if nAct == 3 then
nNumCrystalsPerAct = 1
end
for nCrystalRoom=1,nNumCrystalsPerAct do
local nPick = self.hMapRandomStream:RandomInt( 1, #vecPotentialTrapRooms[nAct] )
self.rooms[ vecPotentialTrapRooms[nAct][nPick] ].bHasCrystal = true
print( "assigning " .. vecPotentialTrapRooms[nAct][nPick] .. " a crystal " )
table.remove( vecPotentialTrapRooms[nAct], nPick )
end
end
for k,v in pairs ( self.rooms ) do
v:SetRoomChoiceReward( self.RoomRewards[ v:GetName() ] )
if self.RoomRewards[ v:GetName() ] ~= nil then
print( "Room " .. v:GetName() .. " reward: " .. self.RoomRewards[ v:GetName() ] )
end
end
end
--------------------------------------------------------------------------------
function CAghanim:IsEncounterDefAppropriateForRoom( encounterDef, room )
if room:GetType() ~= encounterDef.nEncounterType then
return false
end
local requiredDepth = room:GetDepth()
if ( requiredDepth < encounterDef.nMinDepth ) or ( requiredDepth > encounterDef.nMaxDepth ) then
return false
end
return true
end
--------------------------------------------------------------------------------
-- Allocates the room layout
function CAghanim:AssignEncountersToRooms()
local vecAlreadySelectedEncounters = {}
for k,room in pairs(self.rooms) do
local vecEncounterOptions = {}
while true do
local bSkippedOptions = false
for encounterName,encounterDef in pairs(ENCOUNTER_DEFINITIONS) do
if not self:IsEncounterDefAppropriateForRoom( encounterDef, room ) then
goto continue
end
if vecAlreadySelectedEncounters[ encounterName ] == true then
bSkippedOptions = true
goto continue
end
-- Make sure no room has the same encounter on both exits,
-- possible because we're shipping with only 3 options in act 3
local vecSiblingRoomNames = self:GetSiblingRoomNames( room:GetName() )
for s=1,#vecSiblingRoomNames do
local szOtherEncounterName = self.rooms[ vecSiblingRoomNames[s] ]:GetEncounterName()
if szOtherEncounterName == encounterName then
--print( "Suppressing duplicate encounter " .. szOtherEncounterName .. " in room " .. room:GetName() )
goto continue
end
end
table.insert( vecEncounterOptions, encounterName )
::continue::
end
if #vecEncounterOptions > 0 then
break
end
-- This logic causes us to re-use encounters if we've already used them all once
if bSkippedOptions == false then
vecEncounterOptions = { "encounter_test_immediate_victory" }
break
else
for encounterName,encounterDef in pairs(ENCOUNTER_DEFINITIONS) do
if self:IsEncounterDefAppropriateForRoom( encounterDef, room ) then
vecAlreadySelectedEncounters[ encounterName ] = nil
end
end
end
end
-- FOR DEBUGGING, USE A FIXED ENCOUNTER LAYOUT WITH SPECIFIC DUPES
if USE_ENCOUNTER_FIXED_LAYOUT == true then
vecEncounterOptions = { ENCOUNTER_FIXED_LAYOUT[room:GetName()] }
room.nRoomType = ENCOUNTER_DEFINITIONS[ vecEncounterOptions[1] ].nEncounterType
end
if self._szDevEncounter and room:GetName() == "a1_2a" then
vecEncounterOptions = { self._szDevEncounter }
room.nRoomType = ENCOUNTER_DEFINITIONS[ vecEncounterOptions[1] ].nEncounterType
end
if #vecEncounterOptions > 0 then
local nPick = self.hMapRandomStream:RandomInt( 1, #vecEncounterOptions )
room:AssignEncounter( vecEncounterOptions[nPick] )
vecAlreadySelectedEncounters[ vecEncounterOptions[nPick] ] = true
else
print( "Unable to find valid encounter for room " .. k )
end
end
end
--------------------------------------------------------------------------------
-- Sets up the spawn locations for the players
function CAghanim:SetupSpawnLocations()
if ( not self.bMapFlipped ) then
return
end
local hLeftEnt = Entities:FindByName( nil, "a1_1a_teamspawn_left" )
if hLeftEnt == nil then
print( "Unable to find a1_1a_teamspawn_left" )
end
local hRightEnt = Entities:FindByName( nil, "a1_1a_teamspawn_right" )
if hRightEnt == nil then
print( "Unable to find a1_1a_teamspawn_right" )
end
local vOffset = hRightEnt:GetAbsOrigin() - hLeftEnt:GetAbsOrigin()
local hPlayerStarts = Entities:FindAllByClassname( "info_player_start_goodguys" )
if #hPlayerStarts == 0 then
print( "Failed to find any info_player_start_goodguys entities" )
end
for i=1, #hPlayerStarts do
local vNewPosition = hPlayerStarts[i]:GetAbsOrigin() + vOffset
hPlayerStarts[i]:SetAbsOrigin( vNewPosition )
end
local entitiesToFlip =
{
"encounter_end_locator",
"encounter_boss_preview_locator",
"encounter_outpost_1_override",
"encounter_outpost_2_override",
}
for i=1, #entitiesToFlip do
local hLocator = Entities:FindByName( nil, entitiesToFlip[i] )
if hLocator ~= nil then
local vNewPosition = hLocator:GetAbsOrigin()
vNewPosition.x = -vNewPosition.x
hLocator:SetAbsOrigin( vNewPosition )
end
end
end
--------------------------------------------------------------------------------
function CAghanim:FindRoomBySpawnGroupHandle( hSpawnGroupHandle )
for k,room in pairs(self.rooms) do
if room:GetSpawnGroupHandle() == hSpawnGroupHandle then
return room
end
end
return nil
end
--------------------------------------------------------------------------------
function CAghanim:GetExitOptionData( nOptionNumber )
if self:GetCurrentRoom() == nil then
print( "GetExitOptionData has no room")
return nil
end
local exits = {}
local exitDirections = {}
local exitLocations = {}
for direction=ROOM_EXIT_LEFT,ROOM_EXIT_RIGHT do
local room = self:GetCurrentRoom():GetExit( direction )
local vExitLocation = self:GetCurrentRoom():GetExitLocation( direction )
if ( room ~= nil ) then
table.insert( exits, self:GetRoom( room ) )
table.insert( exitDirections, direction )
table.insert( exitLocations, vExitLocation )
end
end
-- No exits? we're done
if #exits == 0 then
return nil
end
-- Gotta do this before we tweak the option number for the rest of this algorithm
local hOverrideLocators = self:GetCurrentRoom():FindAllEntitiesInRoomByName( "encounter_outpost_" .. nOptionNumber .. "_override" )
if ( nOptionNumber > #exits ) then
nOptionNumber = #exits
end
local hRequestedExit = exits[nOptionNumber]
local strPreviewUnit = hRequestedExit:GetEncounter():GetPreviewUnit()
local hAscensionAbilities = hRequestedExit:GetEncounter():GetAscensionAbilities()
local szAscensionAbilities = ""
for i = 1,#hAscensionAbilities do
if i ~= 1 then
szAscensionAbilities = szAscensionAbilities .. ";"
end
szAscensionAbilities = szAscensionAbilities .. hAscensionAbilities[i]
end
local exitData =
{
nExitDirection = exitDirections[nOptionNumber],
vExitLocation = exitLocations[nOptionNumber],
szEncounterPreviewUnit = strPreviewUnit,
flEncounterPreviewModelScale = ENCOUNTER_PREVIEW_SCALES[ strPreviewUnit ],
nEncounterType = hRequestedExit:GetType(),
bIsEliteEncounter = ( hRequestedExit:GetEliteRank() > 0 ),
szNextRoomName = hRequestedExit:GetName(),
szRewardType = hRequestedExit:GetRoomChoiceReward(),
szEncounterName = hRequestedExit:GetEncounter():GetName(),
szAscensionNames = szAscensionAbilities,
nExitCount = #exits,
nDepth = hRequestedExit:GetDepth(),
flZOffset = 0,
}
--if self:GetCurrentRoom():GetExitReward( nOptionNumber ) then
-- print( "Exit option " .. nOptionNumber .. " for room " .. self:GetCurrentRoom().szRoomName .. " has reward type " .. exits[nOptionNumber]:GetRoomChoiceReward() .. " in room " .. exits[nOptionNumber].szRoomName )
-- end
if exitData.flEncounterPreviewModelScale == nil then
exitData.flEncounterPreviewModelScale = 1.0
end
if #hOverrideLocators > 0 then
exitData.vOverrideLocation = hOverrideLocators[1]:GetAbsOrigin()
end
if exits[nOptionNumber]:IsHidden() then
exitData.szEncounterPreviewUnit = "models/ui/exclamation/questionmark.vmdl"
exitData.flEncounterPreviewModelScale = 0.0015
if exitData.nEncounterType == ROOM_TYPE_TRAPS then
exitData.bIsEliteEncounter = hRequestedExit:ShouldDisplayHiddenAsElite()
end
exitData.nEncounterType = ROOM_TYPE_ENEMY
exitData.flZOffset = 25
end
return exitData
end
--------------------------------------------------------------------------------
function CAghanim:OnNextRoomSelected( event )
self:GetCurrentRoom():OnNextRoomSelected( event.selected_room )
end
--------------------------------------------------------------------------------
-- Evaluate the state of the game
function CAghanim:OnThink()
local nGameState = GameRules:State_Get()
if nGameState == DOTA_GAMERULES_STATE_HERO_SELECTION then
self:ComputeHasNewPlayers()
if not self.bStreamedStartingRoomExits then
-- Stream the starting room exits here so people don't have to wait for those
-- First exits to appear, gives more time for the streamer to do its work too
self.bStreamedStartingRoomExits = true
local room = self:GetStartingRoom()
if room ~= nil then
self:SetCurrentRoom( room )
room:LoadExitRooms()
end
end
elseif nGameState == DOTA_GAMERULES_STATE_GAME_IN_PROGRESS then
if self:GetCurrentRoom() and self:GetCurrentRoom():GetEncounter() and self:GetCurrentRoom():GetEncounter():HasStarted() and self:GetCurrentRoom():GetEncounter():NeedsToThink() then
self:GetCurrentRoom():GetEncounter():OnThink()
self:_CheckForDefeat()
end
elseif nGameState >= DOTA_GAMERULES_STATE_POST_GAME then
return nil
end
--CustomNetTables:SetTableValue( "special_ability_upgrades", tostring( 0 ), SPECIAL_ABILITY_UPGRADES[szHeroName] )
return 1
end
--------------------------------------------------------------------------------
--
--------------------------------------------------------------------------------
function CAghanim:ForceAssignHeroes()
for nPlayerID = 0, ( DOTA_MAX_TEAM_PLAYERS - 1 ) do
if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
local hPlayer = PlayerResource:GetPlayer( nPlayerID )
if hPlayer and not PlayerResource:HasSelectedHero( nPlayerID ) then
hPlayer:MakeRandomHeroSelection()
end
end
end
end
--------------------------------------------------------------------------------
function CAghanim:SetCurrentRoom( hRoom )
if hRoom ~= self.CurrentRoom then
self.CurrentRoom = hRoom
self.flCurrentRoomChangeTime = GameRules:GetGameTime()
for k,v in pairs ( self.rooms ) do
v:SendRoomToClient()
end
for nPlayerID = 0, ( DOTA_MAX_TEAM_PLAYERS - 1 ) do
if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
self:RegisterEncounterStartStats( nPlayerID, hRoom:GetDepth() )
end
end
end
end
--------------------------------------------------------------------------------
function CAghanim:GetCurrentRoom()
return self.CurrentRoom
end
--------------------------------------------------------------------------------
function CAghanim:GetCurrentRoomChangeTime()
return self.flCurrentRoomChangeTime
end
--------------------------------------------------------------------------------
function CAghanim:GetRoom( szRoomName )
return self.rooms[ szRoomName ]
end
--------------------------------------------------------------------------------
function CAghanim:GetRoomList()
return self.rooms
end
--------------------------------------------------------------------------------
function CAghanim:FindRoomForPoint( vPoint )
for _,room in pairs(self.rooms) do
if room:IsInRoomBounds( vPoint ) then
return room
end
end
return nil
end
--------------------------------------------------------------------------------
function CAghanim:GetMaxDepth()
return self.nMaxDepth
end
--------------------------------------------------------------------------------
function CAghanim:IsMapFlipped()
return self.bMapFlipped
end
--------------------------------------------------------------------------------
function CAghanim:GetBossUnitForAct( nAct )
for _,room in pairs(self.rooms) do
if room:GetType() == ROOM_TYPE_BOSS then
if room:GetAct() == nAct then
return room:GetEncounter():GetPreviewUnit()
end
end
end
return nil
end
--------------------------------------------------------------------------------
function CAghanim:AddFowOutlineBlocker( vMins, vMaxs )
self.hFowBlockerRegion:AddRectangularOutlineBlocker( vMins, vMaxs, false )
end
--------------------------------------------------------------------------------
function CAghanim:ClearFowBlockers( vMins, vMaxs )
self.hFowBlockerRegion:AddRectangularBlocker( vMins, vMaxs, true )
end
--------------------------------------------------------------------------------
function CAghanim:AddMinorAbilityUpgrade( hHero, upgradeTable )
print( "Adding minor ability upgrade for playerID " .. hHero:GetPlayerOwnerID() )
--PrintTable( upgradeTable, "upgrade" )
if hHero.MinorAbilityUpgrades == nil then
hHero.MinorAbilityUpgrades = {}
end
local szAbilityName = upgradeTable[ "ability_name" ]
if hHero.MinorAbilityUpgrades[ szAbilityName ] == nil then
hHero.MinorAbilityUpgrades[ szAbilityName ] = {}
end
local szSpecialValueName = upgradeTable[ "special_value_name" ]
if hHero.MinorAbilityUpgrades[ szAbilityName ][ szSpecialValueName ] == nil then
hHero.MinorAbilityUpgrades[ szAbilityName ][ szSpecialValueName ] = {}
end
local newUpgradeTable = {}
newUpgradeTable[ "operator" ] = upgradeTable[ "operator" ]
newUpgradeTable[ "value" ] = upgradeTable[ "value" ]
table.insert( hHero.MinorAbilityUpgrades[ szAbilityName ][ szSpecialValueName ], newUpgradeTable )
CustomNetTables:SetTableValue( "minor_ability_upgrades", tostring( hHero:GetPlayerOwnerID() ), hHero.MinorAbilityUpgrades )
--PrintTable( hHero.MinorAbilityUpgrades, " -- " )
local Buff = hHero:FindModifierByName( "modifier_minor_ability_upgrades" )
if Buff == nil then
print( "Error - No minor ability upgrade buff!" )
return
end
Buff:ForceRefresh()
end
--------------------------------------------------------------------------------
function CAghanim:VerifyStatsAbility( hHero, szAbilityName )
--print( "Verifying stats upgrade for playerID " .. hHero:GetPlayerOwnerID() )
if hHero == nil or szAbilityName == nil then
return
end
local hAbility = hHero:FindAbilityByName(szAbilityName)
if hAbility == nil then
hAbility = hHero:AddAbility(szAbilityName)
hAbility:UpgradeAbility( true )
--printf("Adding Stats Ability...", szAbilityName)
end
-- Currently there's only one buff for all the stats, so just refresh it here.
-- If we decide to spread them out, the buffs should be refreshed based on their ability.
local StatsBuff = hHero:FindModifierByName( "modifier_aghsfort_minor_stats_upgrade" )
if StatsBuff == nil then
print( "Error - No minor ability upgrade buff!" )
return
end
StatsBuff:ForceRefresh()
return
end
--------------------------------------------------------------------------------
function CAghanim:GetFragmentDropEV()
local fDropEV = 0
for _, odds in pairs( ARCANE_FRAGMENT_RANDOM_DROP_CHANCES ) do
fDropEV = fDropEV + ( ( odds.high_chance - odds.low_chance ) * odds.num_fragments )
end
fDropEV = fDropEV / 100
print( 'CAghanim:GetFragmentDropEV() calculated an average number of drops: ' .. fDropEV )
return fDropEV
end
--------------------------------------------------------------------------------
function CAghanim:RollRandomFragmentDrops()
local fRoll = RandomFloat( 0, 100.0 )
print( 'CAghanim:RollRandomFragmentDrops() rolled a ' .. fRoll )
for _, odds in pairs( ARCANE_FRAGMENT_RANDOM_DROP_CHANCES ) do
if fRoll >= odds.low_chance and fRoll <= odds.high_chance then
print( 'CAghanim:RollRandomFragmentDrops() selecting ' .. odds.num_fragments .. ' fragment drops for this room' )
return odds.num_fragments
end
end
print( 'WARNING: CAghanim:RollRandomFragmentDrops() did not find a valid entry for this roll ' .. fRoll )
return 0
end
--------------------------------------------------------------------------------
function CAghanim:RollRandomNeutralItemDrops( hEncounter )
if self.nNumNeutralItems <= 0 then
return 0
end
local nPctChance = ( 100 * self.nNumNeutralItems ) / self.nNumViableRoomsForItems
local nItemsToDrop = 0
while hEncounter:RoomRandomInt( 1, 100 ) < nPctChance do
nItemsToDrop = nItemsToDrop + 1
if nItemsToDrop == 2 then
break
end
local nRemainingItems = self.nNumNeutralItems - nItemsToDrop
if nRemainingItems < self.nNumViableRoomsForItems then
nPctChance = PCT_BASE_TWO_ITEM_DROP
else
nPctChance = ( nRemainingItems / self.nNumViableRoomsForItems ) * 100
end
end
nItemsToDrop = math.min( self.nNumNeutralItems, nItemsToDrop )
self.nNumNeutralItems = self.nNumNeutralItems - nItemsToDrop
self.nNumViableRoomsForItems = self.nNumViableRoomsForItems - 1
return nItemsToDrop
end
--------------------------------------------------------------------------------
function CAghanim:PrepareNeutralItemDrop( hRoom, bElite )
local nDepth = hRoom:GetDepth()
local vecPotentialItems = GetPricedNeutralItems( nDepth, bElite )
local vecFilteredItems = GameRules.Aghanim:FilterPreviouslyDroppedItems( vecPotentialItems )
local vecTable = vecFilteredItems
if #vecTable == 0 then
print( "WARNING! All potential items have been dropped, falling back to original depth list. ")
vecTable = vecPotentialItems
if #vecPotentialItems == 0 then
print( "WARNING! trying to drop a neutral item in a place where there is no priced list, depth " .. nDepth )
return nil
end
end
local szItemDrop = vecFilteredItems[ hRoom:RoomRandomInt( 1, #vecFilteredItems ) ]
if szItemDrop == nil then
return nil
end
if self:GetTestEncounterDebugRoom() ~= nil then
print( "adding neutral item to debug crate:" .. szItemDrop )
table.insert( self.debugItemsToStuffInCrate.RoomReward, szItemDrop )
end
self:MarkNeutralItemAsDropped( szItemDrop )
return szItemDrop
end
--------------------------------------------------------------------------------
function CAghanim:MarkNeutralItemAsDropped( szItemName )
table.insert( self.DroppedNeutralItems, szItemName )
end
--------------------------------------------------------------------------------
function CAghanim:FilterPreviouslyDroppedItems( vecPotentialItems )
for i = #vecPotentialItems,1,-1 do
local szItemName = vecPotentialItems[ i ]
for _,szDroppedItemName in pairs ( self.DroppedNeutralItems ) do
if szItemName == szDroppedItemName then
table.remove( vecPotentialItems, i )
end
end
end
return vecPotentialItems
end
--------------------------------------------------------------------------------
function CAghanim:RegisterSummonForAghanim( nDepth, szUnitName )
local Summon = {}
Summon[ "depth" ] = nDepth
Summon[ "unit_name" ] = szUnitName
table.insert( self.AghanimSummons, Summon )
end
--------------------------------------------------------------------------------
function CAghanim:GetSummonsForAghanim()
return self.AghanimSummons
end
--------------------------------------------------------------------------------
function CAghanim:InitializeMetagame()
self.SignOutTable = {}
self.SignOutTable[ "event_name" ] = "aghanim"
self.SignOutTable[ "player_list" ] = {}
self.SignOutTable[ "team_depth_list" ] = {}
for nPlayerID = 0, AGHANIM_PLAYERS - 1 do
local SignOutPlayer =
{
steam_id = 0,
hero_id = 0,
battle_points = 0,
arcane_fragments = 0,
bonus_arcane_fragments = 0,
current_ascension_level = 0,
blessings = {},
depth_list = {},
}
table.insert( self.SignOutTable[ "player_list" ], nPlayerID, SignOutPlayer )
local netTable = {}
netTable[ "arcane_fragments" ] = 0
netTable[ "battle_points" ] = 0
CustomNetTables:SetTableValue( "currency_rewards", tostring( nPlayerID ), netTable )
end
end
--------------------------------------------------------------------------------
function CAghanim:RegisterEncounterStats( stats )
if stats.selectedRoom == nil and stats.unselectedRoom == nil then
return
end
local depthInfo = {}
if stats.selectedRoom ~= nil then
depthInfo.selected_encounter = stats.selectedRoom.szEncounterName
depthInfo.selected_elite = stats.selectedRoom.bIsElite
depthInfo.selected_hidden = stats.selectedRoom.bIsHidden
depthInfo.selected_reward = stats.selectedRoom.szReward
depthInfo.selected_encounter_type = stats.selectedRoom.nRoomType
if stats.selectedRoom.ascensionAbilities ~= nil and #stats.selectedRoom.ascensionAbilities > 0 then
depthInfo.ascension_abilities = {}
for i=1,#stats.selectedRoom.ascensionAbilities do
local szAscensionAbility = stats.selectedRoom.ascensionAbilities[i]
depthInfo.ascension_abilities[ szAscensionAbility ] = GetAbilityTextureNameForAbility( szAscensionAbility )
end
end
end
if stats.unselectedRoom ~= nil then
depthInfo.unselected_encounter = stats.unselectedRoom.szEncounterName
depthInfo.unselected_elite = stats.unselectedRoom.bIsElite
depthInfo.unselected_hidden = stats.unselectedRoom.bIsHidden
depthInfo.unselected_reward = stats.unselectedRoom.szReward
end
self.SignOutTable[ "team_depth_list" ][ stats.depth ] = depthInfo
end
--------------------------------------------------------------------------------
function CAghanim:RegisterBlessingStat( nPlayerID, szBlessingName, nLevel, szActionName, nOrder )
self.SignOutTable[ "player_list" ][ nPlayerID ].blessings[szBlessingName] = nLevel
local blessings = CustomNetTables:GetTableValue( "game_global", "blessings" )
if blessings[ tostring( nPlayerID) ] == nil then
blessings[ tostring( nPlayerID ) ] = {}
end
blessings[ tostring( nPlayerID ) ][ szActionName ] = nOrder
CustomNetTables:SetTableValue( "game_global", "blessings", blessings )
end
--------------------------------------------------------------------------------
function CAghanim:EnsurePlayerStatAtDepth( nPlayerID, szRoomDepth )
if self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth] == nil then
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth] = {}
end
end
--------------------------------------------------------------------------------
function CAghanim:RegisterRewardStats( nPlayerID, szRoomDepth, stats )
self:EnsurePlayerStatAtDepth( nPlayerID, szRoomDepth )
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].rarity = stats.selected_reward.rarity
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].selected_reward = stats.selected_reward.ability_name
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].selected_reward_value = stats.selected_reward.value
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].selected_reward_texture = stats.selected_reward.ability_texture
for nIndex = 1,#stats.unselected_rewards do
local keyName = "unselected_reward" .. nIndex
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth][ keyName ] = stats.unselected_rewards[ nIndex ].ability_name
end
end
--------------------------------------------------------------------------------
function CAghanim:RegisterEncounterStartStats( nPlayerID, nDepth )
local szRoomDepth = tostring( nDepth )
self:EnsurePlayerStatAtDepth( nPlayerID, szRoomDepth )
if self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].net_worth ~= nil then
return
end
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].net_worth = PlayerResource:GetNetWorth( nPlayerID )
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].level = PlayerResource:GetLevel( nPlayerID )
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].death_count = 0
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].kills = 0
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].gold_bags = 0
--PrintTable( self.SignOutTable[ "player_list" ][ nPlayerID ] )
end
--------------------------------------------------------------------------------
function CAghanim:RegisterPlayerDeathStat( nPlayerID, nDepth )
local scores = CustomNetTables:GetTableValue( "aghanim_scores", tostring(nPlayerID) )
scores.death_count = scores.death_count + 1
CustomNetTables:SetTableValue( "aghanim_scores", tostring(nPlayerID), scores )
local szRoomDepth = tostring( nDepth )
self:EnsurePlayerStatAtDepth( nPlayerID, szRoomDepth )
if self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].death_count == nil then
return
end
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].death_count =
self.SignOutTable[ "player_list" ][ nPlayerID ].depth_list[szRoomDepth].death_count + 1
--PrintTable( self.SignOutTable[ "player_list" ][ nPlayerID ] )
end
--------------------------------------------------------------------------------
function CAghanim:GetConnectedPlayers( )
local connectedPlayers = {}
for nPlayerID = 0, AGHANIM_PLAYERS - 1 do
if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
if PlayerResource:IsValidPlayerID( nPlayerID ) and PlayerResource:GetConnectionState( nPlayerID ) ~= DOTA_CONNECTION_STATE_ABANDONED then
table.insert( connectedPlayers, nPlayerID )
end
end
end
return connectedPlayers
end
--------------------------------------------------------------------------------
function CAghanim:GrantAllPlayersPoints( nPoints, bBattlePoints, szReason )
local vecPoints = {}
local connectedPlayers = self:GetConnectedPlayers()
for i=1,#connectedPlayers do
local nPlayerID = connectedPlayers[i]
vecPoints[ tostring(nPlayerID) ] = self:GrantPlayerPoints( nPlayerID, nPoints, bBattlePoints, szReason )
end
return vecPoints
end
--------------------------------------------------------------------------------
function CAghanim:GrantPlayerPoints( nPlayerID, nDesiredPoints, bBattlePoints, szReason )
local Hero = PlayerResource:GetSelectedHeroEntity( nPlayerID )
-- Clamps the amount of points to the amount remaining
-- QUESTION: Should this also multiply the fragments by the bonus?
-- If so, we need to fix the GC to not do the 2x bonus grant,
-- or change the display to not match the underlying grant amount sent to the GC
local nPoints = self:RegisterCurrencyGrant( nPlayerID, nDesiredPoints, bBattlePoints )
if nPoints == 0 then
return 0
end
local nDigits = string.len( tostring( nPoints ) )
if bBattlePoints then
print ( "Awarding player " .. nPlayerID .. " " .. nPoints .. " battle points for " .. szReason )
self.SignOutTable[ "player_list" ][ nPlayerID ][ "battle_points" ] = self.SignOutTable[ "player_list" ][ nPlayerID ][ "battle_points" ] + nPoints
if Hero then
local nFXIndex = ParticleManager:CreateParticleForPlayer( "particles/msg_fx/msg_bp.vpcf", PATTACH_CUSTOMORIGIN, nil, Hero:GetPlayerOwner() )
ParticleManager:SetParticleControlEnt( nFXIndex, 0, Hero, PATTACH_OVERHEAD_FOLLOW, nil, Hero:GetOrigin() + Vector( 0, 64, 96 ), true )
ParticleManager:SetParticleControl( nFXIndex, 1, Vector( 0, nPoints, -1 ) )
ParticleManager:SetParticleControl( nFXIndex, 2, Vector( 1.0, nDigits + 1, 0 ) )
ParticleManager:SetParticleControl( nFXIndex, 3, Vector( 255, 255, 0 ) )
ParticleManager:ReleaseParticleIndex( nFXIndex )
local nFXIndex2 = ParticleManager:CreateParticleForPlayer( "particles/generic_gameplay/battle_point_splash.vpcf", PATTACH_WORLDORIGIN, nil, Hero:GetPlayerOwner() )
ParticleManager:SetParticleControl( nFXIndex2, 1, Hero:GetOrigin() )
ParticleManager:ReleaseParticleIndex( nFXIndex2 )
end
else
print ( "Awarding player " .. nPlayerID .. " " .. nPoints .. " arcane fragments for " .. szReason )
self.SignOutTable[ "player_list" ][ nPlayerID ][ "arcane_fragments" ] = self.SignOutTable[ "player_list" ][ nPlayerID ][ "arcane_fragments" ] + nDesiredPoints
self.SignOutTable[ "player_list" ][ nPlayerID ][ "bonus_arcane_fragments" ] = self.SignOutTable[ "player_list" ][ nPlayerID ][ "bonus_arcane_fragments" ] + ( nPoints - nDesiredPoints )
if Hero then
local nFXIndex = ParticleManager:CreateParticleForPlayer( "particles/msg_fx/msg_bp.vpcf", PATTACH_CUSTOMORIGIN, nil, Hero:GetPlayerOwner() )
ParticleManager:SetParticleControlEnt( nFXIndex, 0, Hero, PATTACH_OVERHEAD_FOLLOW, nil, Hero:GetOrigin() + Vector( 0, 64, 96 ), true )
ParticleManager:SetParticleControl( nFXIndex, 1, Vector( 0, nPoints, -1 ) )
ParticleManager:SetParticleControl( nFXIndex, 2, Vector( 1.0, nDigits + 1, 0 ) )
ParticleManager:SetParticleControl( nFXIndex, 3, Vector( 0, 255, 255 ) )
ParticleManager:ReleaseParticleIndex( nFXIndex )
local nFXIndex2 = ParticleManager:CreateParticleForPlayer( "particles/generic_gameplay/arcane_fragments_splash.vpcf", PATTACH_WORLDORIGIN, nil, Hero:GetPlayerOwner() )
ParticleManager:SetParticleControl( nFXIndex2, 1, Hero:GetOrigin() )
ParticleManager:ReleaseParticleIndex( nFXIndex2 )
end
end
local netTable = {}
netTable[ "arcane_fragments" ] = self.SignOutTable[ "player_list" ][ nPlayerID ][ "arcane_fragments" ] + self.SignOutTable[ "player_list" ][ nPlayerID ][ "bonus_arcane_fragments" ]
netTable[ "battle_points" ] = self.SignOutTable[ "player_list" ][ nPlayerID ][ "battle_points" ]
CustomNetTables:SetTableValue( "currency_rewards", tostring( nPlayerID ), netTable )
return nPoints
end
--------------------------------------------------------------------------------
function CAghanim:MarkGameWon()
self.bWonGame = true
printf("======== ENDING GAME - VICTORY ==========\n");
self:GetAnnouncer():OnGameWon()
GameRules.Aghanim:OnGameFinished()
GameRules:MakeTeamLose( DOTA_TEAM_BADGUYS )
end
--------------------------------------------------------------------------------
function CAghanim:_CheckForDefeat()
if self.bPlayerHasSpawned == false then
return
end
local bAnyHeroesAlive = false
local Heroes = HeroList:GetAllHeroes()
for _,Hero in pairs ( Heroes ) do
if Hero ~= nil then
-- TODO: make sure that servers will die when everyone DCs, since we don't want AFK checks for this mode
if Hero:IsRealHero() and Hero:GetTeamNumber() == DOTA_TEAM_GOODGUYS and
( Hero:IsAlive() or Hero:IsReincarnating() or ( Hero:GetRespawnsDisabled() == false ) ) then
bAnyHeroesAlive = true
end
end
end
if bAnyHeroesAlive == false then
printf("======== ENDING GAME ==========\n");
self:GetAnnouncer():OnGameLost()
GameRules.Aghanim:OnGameFinished()
GameRules:MakeTeamLose( DOTA_TEAM_GOODGUYS )
end
end
--------------------------------------------------------------------------------
function CAghanim:Dev_WinGame()
self:MarkGameWon()
end
--------------------------------------------------------------------------------
function CAghanim:Dev_WinEncounter()
if self:GetCurrentRoom() and self:GetCurrentRoom():IsActivated() and self:GetCurrentRoom():GetEncounter() then
self:GetCurrentRoom():GetEncounter():Dev_ForceCompleteEncounter()
else
printf( "ERROR - win_encounter command failed" )
end
end
--------------------------------------------------------------------------------
function CAghanim:Dev_SetAscensionLevel( cmdName, szLevel )
local nLevel = tonumber( szLevel )
self:SetAscensionLevel( nLevel )
end
--------------------------------------------------------------------------------
function CAghanim:Dev_SetNewPlayers( cmdName, szNewPlayers )
self.bHasAnyNewPlayers = tonumber( szNewPlayers ) > 0
end
--------------------------------------------------------------------------------
function CAghanim:Dev_ExtraLives()
local nPlayerID = Entities:GetLocalPlayer():GetPlayerID()
--for nPlayerID = 0, ( DOTA_MAX_TEAM_PLAYERS - 1 ) do
-- if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID )
if hPlayerHero ~= nil then
printf("adding lives to %d", nPlayerID)
hPlayerHero.nRespawnsRemaining = math.min( AGHANIM_MAX_LIVES, hPlayerHero.nRespawnsRemaining + 1 )
CustomGameEventManager:Send_ServerToPlayer( hPlayerHero:GetPlayerOwner(), "gained_life", {} )
CustomNetTables:SetTableValue( "respawns_remaining", string.format( "%d", hPlayerHero:entindex() ), { respawns = hPlayerHero.nRespawnsRemaining } )
end
--end
--end
end
--------------------------------------------------------------------------------
function CAghanim:Dev_TestEncounter( cmdName, szEncounterName, szIsElite )
local nEliteDepthBonus = 0
if szIsElite ~= nil then
nEliteDepthBonus = tonumber( szIsElite )
if nEliteDepthBonus > 0 then
nEliteDepthBonus = 1
end
end
local encounterDef = ENCOUNTER_DEFINITIONS[ szEncounterName ]
if encounterDef == nil then
printf( "%s: Invalid encounter %s", cmdName, szEncounterName )
return
end
local nRoomDepth = encounterDef.nMinDepth
local hFinalRoom = self:GetCurrentRoom()
if hFinalRoom == nil then
printf( "%s: Can't use since we're still loading", cmdName )
return
end
local nCurrDepth = hFinalRoom:GetDepth()
if nRoomDepth <= nCurrDepth then
printf( "%s: You're already at the same or lower depth %d than you requested [%d]", cmdName, nCurrDepth, nRoomDepth )
return
end
printf( "Running %s %s %d %d [%s %d]...", cmdName, szEncounterName, nRoomDepth, nEliteDepthBonus, hFinalRoom:GetName(), nCurrDepth )
-- Find the room we will do the encounter in, as well as the previous room
self.testEncounter =
{
hPrevPrevRoom = nil,
hPrevRoom = nil,
hFinalRoom = hFinalRoom,
rewardClaimList = {},
}
self.debugItemsToStuffInCrate =
{
RoomReward = {}
}
for i=nCurrDepth+1, nRoomDepth do
-- Find the first valid exit
self.testEncounter.hPrevPrevRoom = self.testEncounter.hPrevRoom
self.testEncounter.hPrevRoom = self.testEncounter.hFinalRoom
for nExitDirection=ROOM_EXIT_LEFT,ROOM_EXIT_RIGHT do
local szExitRoomName = self.testEncounter.hPrevRoom:GetExit( nExitDirection )
if szExitRoomName ~= nil then
self.testEncounter.hFinalRoom = GameRules.Aghanim:GetRoom( szExitRoomName )
if self.testEncounter.hFinalRoom:GetDepth() ~= i then
print( "unexpected depth in room " .. self.testEncounter.hFinalRoom:GetName() )
end
table.insert( self.testEncounter.rewardClaimList, self.testEncounter.hFinalRoom )
break
end
end
end
-- Don't claim the room we're going to, not should we claim the one right before the selected room;
-- we're going to Dev_WinEncounter that one after teleporting there
-- This is the target room
table.remove( self.testEncounter.rewardClaimList, #self.testEncounter.rewardClaimList )
-- This is the room before the target room
if #self.testEncounter.rewardClaimList > 0 then
table.remove( self.testEncounter.rewardClaimList, #self.testEncounter.rewardClaimList )
end
-- Win the current room assuming we haven't already won
if self:GetCurrentRoom():GetDepth() ~= 1 and self:GetCurrentRoom():GetEncounter():IsComplete() == false then
self:Dev_WinEncounter()
self:GetCurrentRoom():GetEncounter():TryCompletingMapEncounter()
self:GetCurrentRoom():GetEncounter():GenerateRewards()
end
-- Change the encounter in the final room to the desired one
self.testEncounter.hFinalRoom:SetEliteDepthBonus( nEliteDepthBonus )
self.testEncounter.hFinalRoom:AssignEncounter( szEncounterName )
self.testEncounter.hFinalRoom:GetEncounter():SelectAscensionAbilities()
-- We're going to be teleporting into the previous room, so we need to stream that in
if self.testEncounter.hPrevPrevRoom ~= nil then
self.testEncounter.hPrevPrevRoom:LoadExitRooms()
end
-- Also start the previous room streaming in the final room. Note that this can
-- be done simultaneously as the prev prev room, since once LoadExitRooms was called,
-- the system knows the height of the maps to spawn to correct connect to prev prev
-- NOTE that if hPrevRoom == the current room, this will be the second call to
-- LoadExitRooms on the same room, but the code is tolerant of that
self.testEncounter.hPrevRoom:LoadExitRooms()
-- Next we must start a think function where we do two things
-- 1) Wait for the exit rooms to be loaded
-- 2) Make all players claim rewards for all intervening rooms
GameRules:GetGameModeEntity():SetThink( "OnTestEncounterThink", self, "TestEncounterThink", 0.5 )
end
--------------------------------------------------------------------------------
function CAghanim:GrantEstimatedRoomRewards( hRoom )
local nMinValue, nMaxValue = GetMinMaxGoldChoiceReward( hRoom:GetEncounter():GetDepth(), false )
local nQuantity = math.random( nMinValue, nMaxValue ) / 3 -- *1/3 since it's 1/3 likely you'll pick it
for nPlayerID = 0, ( DOTA_MAX_TEAM_PLAYERS - 1 ) do
if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
PlayerResource:ModifyGold( nPlayerID, nQuantity, true, DOTA_ModifyGold_Unspecified )
end
end
end
--------------------------------------------------------------------------------
function CAghanim:DetermineRewardSelectionState()
local CurrentRoom = CustomNetTables:GetTableValue( "reward_options", "current_depth" )
if CurrentRoom == nil then
return nil
end
local szRoomDepth = CurrentRoom["1"];
local rewardOptions = CustomNetTables:GetTableValue( "reward_options", szRoomDepth )
if rewardOptions == nil then
return nil
end
local netTable = CustomNetTables:GetTableValue( "reward_choices", szRoomDepth )
if netTable == nil then
return nil
end
local vecPlayerIDs = self:GetConnectedPlayers( )
if #vecPlayerIDs == 0 then
return nil
end
local rewardState = {}
for i=1,#vecPlayerIDs do
local nPlayerID = vecPlayerIDs[i]
local bHasChosen = ( netTable[ tostring(nPlayerID) ] ~= nil )
rewardState[ tostring(nPlayerID) ] = bHasChosen
end
return rewardState
end
--------------------------------------------------------------------------------
function CAghanim:HaveAllRewardsBeenSelected()
local CurrentRoom = CustomNetTables:GetTableValue( "reward_options", "current_depth" )
if CurrentRoom == nil then
return false
end
local szRoomDepth = CurrentRoom["1"];
--print( "HaveAllRewardsBeenSelected depth " .. szRoomDepth )
-- Some depths don't give rewards. If we hit this, then it's ok; exit out.
local rewardOptions = CustomNetTables:GetTableValue( "reward_options", szRoomDepth )
if rewardOptions == nil then
return true
end
--print( "Waiting for choices " )
-- Check to see if all players have made choices yet at the current depth
local netTable = CustomNetTables:GetTableValue( "reward_choices", szRoomDepth )
if netTable == nil then
return false
end
local vecPlayerIDs = self:GetConnectedPlayers( )
for i=1,#vecPlayerIDs do
local nPlayerID = vecPlayerIDs[i]
if netTable[ tostring(nPlayerID) ] == nil then
--print( "Player not done choosing " .. nPlayerID )
return false
end
end
return true
end
--------------------------------------------------------------------------------
function CAghanim:OnTestEncounterThink()
-- First handle all rewards being selected
local CurrentRoom = CustomNetTables:GetTableValue( "reward_options", "current_depth" )
if CurrentRoom == nil then
return 1
end
local szRoomDepth = CurrentRoom["1"];
--print( "OnTestEncounterThink depth " .. szRoomDepth )
if self:HaveAllRewardsBeenSelected() == false then
return 1
end
-- Once all players have made a choice, deal with claiming the next depth
--print( "Claiming next depth " )
if #self.testEncounter.rewardClaimList > 0 then
--print( "Claiming rewards for room " .. self.testEncounter.rewardClaimList[1]:GetName() .. " depth " .. self.testEncounter.rewardClaimList[1]:GetDepth() )
if ( tonumber( szRoomDepth ) + 1 ) ~= self.testEncounter.rewardClaimList[1]:GetDepth() then
printf( "Warning!! Unexpected depth in claim list [was %d, expected %d]!!", self.testEncounter.rewardClaimList[1]:GetDepth(), tonumber( szRoomDepth ) + 1 )
end
local hRoom = self.testEncounter.rewardClaimList[1]
hRoom:GetEncounter():RegisterSummonForAghanim()
hRoom:GetEncounter():GenerateRewards()
self:GrantEstimatedRoomRewards( hRoom )
table.remove( self.testEncounter.rewardClaimList, 1 )
if self.bFastTestEncounter == true then
for nPlayerID = 0, ( DOTA_MAX_TEAM_PLAYERS - 1 ) do
if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
-- Select the first option for each player
local hChoice =
{
PlayerID = nPlayerID,
room_depth = hRoom:GetDepth(),
reward_index = 1,
}
OnRewardChoice( nil, hChoice )
end
end
end
return 1
end
-- If we're here, then all players have selected all rewards
-- Next handle streaming of the rooms
if self.testEncounter.hPrevPrevRoom ~= nil then
--print( "Testing for stream in" .. self.testEncounter.hPrevPrevRoom:GetName() )
if not self.testEncounter.hPrevPrevRoom:AreAllExitRoomsReady() then
return 1
end
--print( "Selected " .. self.testEncounter.hPrevPrevRoom:GetName() .. " -> " .. self.testEncounter.hPrevRoom:GetName() )
self.testEncounter.hPrevPrevRoom:OnNextRoomSelected( self.testEncounter.hPrevRoom:GetName() )
self.testEncounter.hPrevPrevRoom = nil
-- Wait for a tick to make sure all the spawn group shenanigans are done so we can teleport in
return 1
end
if self:GetCurrentRoom() ~= self.testEncounter.hPrevRoom then
if self.testEncounter.bHasTeleported then
return 1
end
--print( "Testing for stream in" .. self.testEncounter.hPrevRoom:GetName() )
if not self.testEncounter.hPrevRoom:AreAllExitRoomsReady() then
return 1
end
-- teleport all players into the prev room, we're ready to go
local vTeleportTarget = self.testEncounter.hPrevRoom:GetOrigin()
local hEndLocators = self.testEncounter.hPrevRoom:FindAllEntitiesInRoomByName( "encounter_end_locator", true )
if #hEndLocators > 0 then
vTeleportTarget = hEndLocators[1]:GetAbsOrigin()
end
for nPlayerID = 0, ( DOTA_MAX_TEAM_PLAYERS - 1 ) do
if PlayerResource:GetTeam( nPlayerID ) == DOTA_TEAM_GOODGUYS then
local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID )
if hPlayerHero ~= nil then
FindClearSpaceForUnit( hPlayerHero, vTeleportTarget, true )
CenterCameraOnUnit( nPlayerID, hPlayerHero )
end
end
end
self.testEncounter.bHasTeleported = true
return 1
end
-- Ok, we've teleported. Now we can finish the prev room and get its rewards
self:Dev_WinEncounter()
self.testEncounter = nil
-- Disable thinking
return nil
end
function CAghanim:GetTestEncounterDebugRoom()
if self.testEncounter == nil then
return nil
end
return self.testEncounter.hPrevRoom
end