477 lines
16 KiB
Lua
Executable File
477 lines
16 KiB
Lua
Executable File
require( "constants" )
|
|
require( "gameplay_shared" )
|
|
require( "map_room" )
|
|
require( "reward_tables" )
|
|
require( "utility_functions" )
|
|
require( "ai/shared" )
|
|
require( "aghanim_ability_upgrade_interface" )
|
|
|
|
|
|
function GetMinMaxGoldChoiceReward( nRoomDepth, bElite )
|
|
local nFixedGoldAwardOfDepth = ENCOUNTER_DEPTH_GOLD_REWARD[ nRoomDepth ]
|
|
if bElite then
|
|
--print( "Elite Room, increasing expected value of item reward " .. nFixedGoldAwardOfDepth .. " to " .. nFixedGoldAwardOfDepth * ELITE_VALUE_MODIFIER )
|
|
nFixedGoldAwardOfDepth = nFixedGoldAwardOfDepth * ELITE_VALUE_MODIFIER
|
|
end
|
|
local nMaxValue = math.ceil( nFixedGoldAwardOfDepth * GOLD_REWARD_CHOICE_MAX_PCT )
|
|
local nMinValue = math.floor( nFixedGoldAwardOfDepth * GOLD_REWARD_CHOICE_MIN_PCT )
|
|
return nMinValue, nMaxValue
|
|
end
|
|
|
|
function GetPricedNeutralItems( nRoomDepth, bElite )
|
|
local vecItemRewards = PRICED_ITEM_REWARD_LIST
|
|
local nFixedGoldAwardOfDepth = ENCOUNTER_DEPTH_GOLD_REWARD[ nRoomDepth ]
|
|
if bElite then
|
|
--print( "Elite Room, increasing expected value of item reward " .. nFixedGoldAwardOfDepth .. " to " .. nFixedGoldAwardOfDepth * ELITE_NEUTRAL_ITEM_VALUE_MODIFIER )
|
|
nFixedGoldAwardOfDepth = nFixedGoldAwardOfDepth * ELITE_NEUTRAL_ITEM_VALUE_MODIFIER
|
|
end
|
|
local flBonusPct = PRICED_ITEM_BONUS_DEPTH_PCT * nRoomDepth
|
|
local nMaxValue = math.ceil( nFixedGoldAwardOfDepth * ( PRICED_ITEM_GOLD_MAX_PCT + flBonusPct ) )
|
|
local nMinValue = math.floor( nFixedGoldAwardOfDepth * ( PRICED_ITEM_GOLD_MIN_PCT + flBonusPct ) )
|
|
local vecPossibleItems = {}
|
|
|
|
for szItemName, nValue in pairs( vecItemRewards ) do
|
|
if nValue >= nMinValue and nValue <= nMaxValue then
|
|
table.insert( vecPossibleItems, szItemName )
|
|
end
|
|
end
|
|
|
|
return vecPossibleItems
|
|
end
|
|
|
|
|
|
function GetRandomUnique( hRandomStream, Array, BlacklistValues )
|
|
if Array == nil then
|
|
return nil
|
|
end
|
|
|
|
--PrintTable( Array, "Array:" )
|
|
--PrintTable( BlacklistValues, "BlacklistValues:" )
|
|
|
|
local Whitelist = {}
|
|
if BlacklistValues == nil then
|
|
Whitelist = Array
|
|
else
|
|
for _,Value in pairs(Array) do
|
|
if not TableContainsValue( BlacklistValues, Value ) then
|
|
table.insert(Whitelist, Value)
|
|
end
|
|
end
|
|
end
|
|
|
|
local bIgnoreBlacklist = false
|
|
if #Whitelist < 1 then
|
|
bIgnoreBlacklist = true
|
|
Whitelist = Array
|
|
end
|
|
|
|
local Candidate = nil
|
|
nIndex = hRandomStream:RandomInt(1,#Whitelist)
|
|
Candidate = Whitelist[ nIndex ]
|
|
|
|
if bIgnoreBlacklist then
|
|
printf("WARNING: GetRandomUnique returning array[%d] = %s, ignoring blacklist.", nIndex, Candidate)
|
|
end
|
|
|
|
return Candidate
|
|
end
|
|
|
|
function GetRoomRewards( nRoomDepth, nRoomType, bElite, nPlayerID, vecExternalExcludeList )
|
|
|
|
local vecRewardStruct = nil
|
|
|
|
local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID )
|
|
|
|
if hPlayerHero == nil then
|
|
printf("GetRoomRewards; Aborting, no hero entity for Player %d", nPlayerID )
|
|
return nil
|
|
end
|
|
|
|
if( vecExternalExcludeList == nil ) then
|
|
vecExternalExcludeList = {}
|
|
end
|
|
|
|
local bLimitUltimateUpgrades = tonumber(nRoomDepth) == 1
|
|
|
|
local szHeroName = hPlayerHero:GetName()
|
|
local bHardRoom = bElite --or nRoomType == ROOM_TYPE_TRAPS
|
|
|
|
-- Rarity:
|
|
-- Common = 0
|
|
-- Rare = 1
|
|
-- Epic = 2
|
|
local iRewardRarity = 0
|
|
|
|
if bHardRoom == true then
|
|
iRewardRarity = 1
|
|
end
|
|
|
|
vecRewardStruct = ROOM_REWARDS[ "depth_" .. nRoomDepth].normal
|
|
|
|
local hHeroRandomStream = GameRules.Aghanim:GetHeroRandomStream( nPlayerID )
|
|
|
|
-- first, choose the appropriate reward tier for each option
|
|
local vecGeneratedRewardTiers = {}
|
|
|
|
for _,aRewardDef in pairs( vecRewardStruct ) do
|
|
|
|
local flRoll = hHeroRandomStream:RandomFloat(0, 100.0)
|
|
local flThreshold = 0.0
|
|
|
|
for eRewardTier,flPct in pairs( aRewardDef ) do
|
|
flThreshold = flThreshold + flPct
|
|
if flRoll <= flThreshold then
|
|
table.insert( vecGeneratedRewardTiers, eRewardTier )
|
|
break
|
|
end
|
|
end
|
|
end
|
|
|
|
if TableLength(vecGeneratedRewardTiers) < 1 then
|
|
return nil
|
|
end
|
|
|
|
-- shuffle the chosen reward tiers so that progressive probabilities are randomized
|
|
ShuffleListInPlace( vecGeneratedRewardTiers, hHeroRandomStream )
|
|
|
|
-- exclude any item or ability they've learned, chosen, have in inventory or are externally marked for exclusion
|
|
local vecAbilitiesToExclude = GetPlayerAbilitiesAndItems( nPlayerID )
|
|
for ii=1,nRoomDepth-1 do
|
|
local RewardChoices = CustomNetTables:GetTableValue( "reward_choices", tostring(ii) )
|
|
local RewardChoice = RewardChoices and RewardChoices[ tostring(nPlayerID) ] or nil
|
|
if RewardChoice and RewardChoice["ability_name"] and RewardChoice["reward_type"] ~= "REWARD_TYPE_MINOR_ABILITY_UPGRADE" and RewardChoice["reward_type"] ~= "REWARD_TYPE_MINOR_STATS_UPGRADE" then
|
|
table.insert( vecAbilitiesToExclude, RewardChoice["ability_name"] )
|
|
end
|
|
end
|
|
|
|
for _,ExcludeAbility in pairs(vecExternalExcludeList) do
|
|
table.insert( vecAbilitiesToExclude, ExcludeAbility )
|
|
end
|
|
|
|
local MinorUpgrades = deepcopy( MINOR_ABILITY_UPGRADES[ szHeroName ] )
|
|
local MinorStatsUpgrades = deepcopy( MINOR_ABILITY_UPGRADES ["base_stats_upgrades"])
|
|
|
|
-- then for each option, roll a reward type, and don't repeat types
|
|
local vecGeneratedRewards = {}
|
|
local vecMinorAbilityIDsToExclude = {}
|
|
for _,eRewardTier in pairs(vecGeneratedRewardTiers) do
|
|
|
|
local eGeneratedRewardType = nil
|
|
|
|
local aRewardTierDef = RebalanceRewards( REWARD_TIER_TABLE[eRewardTier], vecGeneratedRewards )
|
|
|
|
local flRoll = hHeroRandomStream:RandomFloat(0, 100.0)
|
|
local flThreshold = 0.0
|
|
local MinorAbilityUpgrade = nil
|
|
local MinorStatsUpgrade = nil
|
|
|
|
for eRewardType,flPct in pairs( aRewardTierDef ) do
|
|
|
|
flThreshold = flThreshold + flPct
|
|
if flRoll <= flThreshold then
|
|
|
|
local szAbilityName = nil
|
|
local nQuantity = nil
|
|
|
|
if eRewardType == "REWARD_TYPE_ABILITY_UPGRADE" then
|
|
|
|
szAbilityName = GetRandomUnique( hHeroRandomStream, SPECIAL_ABILITY_UPGRADES[szHeroName], vecAbilitiesToExclude )
|
|
iRewardRarity = 2
|
|
|
|
elseif eRewardType == "REWARD_TYPE_MINOR_ABILITY_UPGRADE" then
|
|
local k = hHeroRandomStream:RandomInt( 1, #MinorUpgrades )
|
|
local Upgrade = MinorUpgrades[ k ]
|
|
table.remove( MinorUpgrades, k )
|
|
MinorAbilityUpgrade = deepcopy( Upgrade )
|
|
if bHardRoom then
|
|
print( "Elite Room, increasing expected value of ability upgrade from " .. MinorAbilityUpgrade[ "value" ] .. " to " .. MinorAbilityUpgrade[ "value" ] * ELITE_VALUE_MODIFIER )
|
|
MinorAbilityUpgrade[ "value" ] = MinorAbilityUpgrade[ "value" ] * ELITE_VALUE_MODIFIER
|
|
end
|
|
--table.insert( vecMinorAbilityIDsToExclude, MinorAbilityUpgrade[ "id" ] )
|
|
elseif eRewardType == "REWARD_TYPE_MINOR_STATS_UPGRADE" then
|
|
local k = hHeroRandomStream:RandomInt( 1, #MinorStatsUpgrades )
|
|
local StatsUpgrade = MinorStatsUpgrades[ k ]
|
|
table.remove( MinorStatsUpgrades, k )
|
|
MinorStatsUpgrade = deepcopy( StatsUpgrade )
|
|
if bHardRoom then
|
|
print( "Elite Room, increasing expected value of stats upgrade from " .. MinorStatsUpgrade[ "value" ] .. " to " .. MinorStatsUpgrade[ "value" ] * ELITE_VALUE_MODIFIER )
|
|
MinorStatsUpgrade[ "value" ] = MinorStatsUpgrade[ "value" ] * ELITE_VALUE_MODIFIER
|
|
end
|
|
|
|
end
|
|
|
|
if szAbilityName ~= nil then
|
|
|
|
if bLimitUltimateUpgrades and szAbilityName and string.match(szAbilityName, ULTIMATE_ABILITY_NAMES[szHeroName] ) then
|
|
for _key,szAbilityUpgrade in pairs(SPECIAL_ABILITY_UPGRADES[szHeroName]) do
|
|
if string.match(szAbilityUpgrade, ULTIMATE_ABILITY_NAMES[szHeroName] ) then
|
|
table.insert( vecAbilitiesToExclude, szAbilityUpgrade )
|
|
end
|
|
end
|
|
end
|
|
|
|
table.insert( vecAbilitiesToExclude, szAbilityName )
|
|
end
|
|
|
|
local GeneratedReward =
|
|
{
|
|
reward_type = eRewardType,
|
|
reward_tier = eRewardTier,
|
|
ability_name = szAbilityName,
|
|
quantity = nQuantity,
|
|
rarity = iRewardRarity,
|
|
}
|
|
|
|
if bHardRoom then
|
|
GeneratedReward[ "elite" ] = 1
|
|
else
|
|
GeneratedReward[ "elite" ] = 0
|
|
end
|
|
|
|
if MinorAbilityUpgrade ~= nil then
|
|
GeneratedReward[ "ability_name" ] = MinorAbilityUpgrade[ "ability_name" ]
|
|
GeneratedReward[ "description" ] = MinorAbilityUpgrade[ "description" ]
|
|
GeneratedReward[ "value" ] = MinorAbilityUpgrade[ "value" ]
|
|
GeneratedReward[ "id" ] = MinorAbilityUpgrade[ "id" ]
|
|
end
|
|
|
|
if MinorStatsUpgrade ~= nil then
|
|
GeneratedReward[ "ability_name" ] = MinorStatsUpgrade[ "ability_name" ]
|
|
GeneratedReward[ "description" ] = MinorStatsUpgrade[ "description" ]
|
|
GeneratedReward[ "value" ] = MinorStatsUpgrade[ "value" ]
|
|
GeneratedReward[ "id" ] = MinorStatsUpgrade[ "id" ]
|
|
end
|
|
|
|
table.insert( vecGeneratedRewards, GeneratedReward )
|
|
break
|
|
end
|
|
end
|
|
|
|
table.insert( vecGeneratedRewards, GeneratedReward )
|
|
end
|
|
|
|
return vecGeneratedRewards
|
|
|
|
end
|
|
|
|
function TestRoomRewardConsoleCommand( cmdName, szRoomDepth, szIsElite, szIsTrapRoom )
|
|
|
|
--CustomNetTables:SetTableValue( "reward_options", "current_depth", { szRoomDepth } );
|
|
|
|
local bIsElite = (szIsElite == "true")
|
|
local bIsTrapRoom = (szIsTrapRoom == "true")
|
|
local szRoomDepth = tostring( tonumber( szRoomDepth ) )
|
|
local nPlayerID = Entities:GetLocalPlayer():GetPlayerID()
|
|
local nRoomType = ROOM_TYPE_ENEMY
|
|
if bIsTrapRoom == true then
|
|
nRoomType = ROOM_TYPE_TRAPS
|
|
end
|
|
|
|
--printf( "Running %s %d %s %s %s...", cmdName, nPlayerID, szRoomDepth, szIsElite, szIsTrapRoom )
|
|
|
|
CustomNetTables:SetTableValue( "reward_options", "current_depth", { szRoomDepth } )
|
|
|
|
CustomNetTables:SetTableValue( "reward_choices", szRoomDepth, {} )
|
|
|
|
local RewardOptions = {}
|
|
local vecPlayerRewards = GetRoomRewards( tonumber(szRoomDepth), nRoomType, bIsElite, nPlayerID )
|
|
RewardOptions[ tostring(nPlayerID) ] = vecPlayerRewards;
|
|
|
|
--DeepPrintTable( vecPlayerRewards )
|
|
|
|
CustomNetTables:SetTableValue( "reward_options", szRoomDepth, RewardOptions )
|
|
|
|
end
|
|
|
|
function RebalanceRewards( aRewardDef, vecPreviouslyGeneratedRewards )
|
|
|
|
local aRebalancedRewardDef = deepcopy( aRewardDef )
|
|
NormalizeFloatArrayInPlace( aRebalancedRewardDef, 100.0 )
|
|
return aRebalancedRewardDef
|
|
|
|
end
|
|
|
|
function NormalizeFloatArrayInPlace( aFloatValues, flDesiredSum )
|
|
|
|
if flDesiredSum == nil then
|
|
flDesiredSum = 1.0
|
|
end
|
|
|
|
local flSum = 0
|
|
for _,flFloatValue in pairs( aFloatValues ) do
|
|
flSum = flSum + flFloatValue
|
|
end
|
|
|
|
for key,flFloatValue in pairs( aFloatValues ) do
|
|
aFloatValues[key] = ( aFloatValues[key] / flSum ) * flDesiredSum
|
|
end
|
|
|
|
end
|
|
|
|
function GrantRewards( nPlayerID, szRoomDepth, aReward )
|
|
|
|
local hPlayerHero = PlayerResource:GetSelectedHeroEntity( nPlayerID )
|
|
if hPlayerHero == nil then
|
|
printf("Aborting grant reward, no hero entity for Player %d", nPlayerID )
|
|
return
|
|
end
|
|
|
|
local RewardChoices = CustomNetTables:GetTableValue( "reward_choices", szRoomDepth )
|
|
if RewardChoices == nil then
|
|
RewardChoices = {}
|
|
end
|
|
|
|
local aExistingReward = RewardChoices[ tostring(nPlayerID) ]
|
|
if aExistingReward ~= nil then
|
|
printf("GrantRewards: Player %d, Depth %s, aborting granting Reward %s to to existing Reward: %s", nPlayerID, szRoomDepth, DeepToString(aReward), DeepToString(aExistingReward) )
|
|
return
|
|
end
|
|
|
|
printf("granting reward to %s: %s", hPlayerHero:GetName(), DeepToString(aReward) )
|
|
|
|
local eRewardType = aReward["reward_type"]
|
|
local nQuantity = aReward["quantity"]
|
|
local szAbilityName = aReward["ability_name"]
|
|
local bEliteReward = aReward["elite"] == 1
|
|
|
|
|
|
--local eRewardTier = aReward["reward_tier"
|
|
if eRewardType == "REWARD_TYPE_ABILITY_UPGRADE" then
|
|
local data = {}
|
|
data["PlayerID"] = nPlayerID
|
|
data["AbilityName"] = szAbilityName
|
|
data["LevelReward"] = true
|
|
CAghanim:OnAbilityUpgradeButtonClicked(1, data)
|
|
GameRules.Aghanim:GetAnnouncer():OnRewardSelected( hPlayerHero, tonumber( szRoomDepth ), eRewardType, szAbilityName )
|
|
elseif eRewardType == "REWARD_TYPE_MINOR_ABILITY_UPGRADE" then
|
|
local Upgrade = deepcopy( MINOR_ABILITY_UPGRADES[ hPlayerHero:GetUnitName() ][ aReward[ "id" ] ] )
|
|
if bEliteReward then
|
|
Upgrade[ "value" ] = Upgrade[ "value" ] * ELITE_VALUE_MODIFIER
|
|
end
|
|
CAghanim:AddMinorAbilityUpgrade( hPlayerHero, Upgrade )
|
|
GameRules.Aghanim:GetAnnouncer():OnRewardSelected( hPlayerHero, tonumber( szRoomDepth ), eRewardType, Upgrade.description )
|
|
elseif eRewardType == "REWARD_TYPE_MINOR_STATS_UPGRADE" then
|
|
--Hook up via the same path as the minor ability upgrades
|
|
local StatsUpgrade = deepcopy( MINOR_ABILITY_UPGRADES[ "base_stats_upgrades" ][aReward[ "id" ] ] )
|
|
if bEliteReward then
|
|
StatsUpgrade[ "value" ] = StatsUpgrade[ "value" ] * ELITE_VALUE_MODIFIER
|
|
end
|
|
--Make sure to grant and level up the stats ability if we haven't taken this reward yet
|
|
CAghanim:AddMinorAbilityUpgrade( hPlayerHero, StatsUpgrade )
|
|
CAghanim:VerifyStatsAbility(hPlayerHero, StatsUpgrade[ "ability_name" ])
|
|
GameRules.Aghanim:GetAnnouncer():OnRewardSelected( hPlayerHero, tonumber( szRoomDepth ), eRewardType, StatsUpgrade.description )
|
|
end
|
|
|
|
RewardChoices[ tostring(nPlayerID) ] = aReward
|
|
CustomNetTables:SetTableValue( "reward_choices", szRoomDepth, RewardChoices )
|
|
CustomNetTables:SetTableValue( "reward_choices", "current_depth", { szRoomDepth } )
|
|
|
|
|
|
local gameEvent = {}
|
|
if aReward["quantity"] then
|
|
gameEvent["int_value"] = tonumber(aReward["quantity"])
|
|
end
|
|
if aReward["ability_name"] then
|
|
if eRewardType == "REWARD_TYPE_MINOR_ABILITY_UPGRADE" or eRewardType == "REWARD_TYPE_MINOR_STATS_UPGRADE" then
|
|
--PrintTable( aReward, " reward choice: " )
|
|
gameEvent["string_replace_token"] = aReward [ "description" ]
|
|
gameEvent["ability_name"] = aReward[ "ability_name" ]
|
|
gameEvent["value"] = tonumber(aReward[ "value" ])
|
|
else
|
|
gameEvent["locstring_value"] ="#DOTA_Tooltip_Ability_" .. aReward["ability_name"]
|
|
end
|
|
end
|
|
gameEvent["player_id"] = nPlayerID
|
|
gameEvent["teamnumber"] = -1
|
|
gameEvent["message"] = "#DOTA_HUD_" .. aReward["reward_type"] .. "_Toast"
|
|
|
|
--DeepPrintTable( RewardChoices )
|
|
FireGameEvent( "dota_combat_event_message", gameEvent )
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
function GenerateRewardStatsForPlayer( hPlayerHero, reward )
|
|
|
|
local szAbilityName = nil
|
|
local szAbilityTexture = nil
|
|
|
|
if reward.reward_type == "REWARD_TYPE_ABILITY_UPGRADE" then
|
|
szAbilityName = reward.ability_name
|
|
szAbilityTexture = GetAbilityTextureNameForAbility( szAbilityName )
|
|
elseif reward.reward_type == "REWARD_TYPE_MINOR_ABILITY_UPGRADE" then
|
|
szAbilityName = MINOR_ABILITY_UPGRADES[ hPlayerHero:GetUnitName() ][ reward.id ].description
|
|
szAbilityTexture = GetAbilityTextureNameForAbility( reward.ability_name )
|
|
elseif reward.reward_type == "REWARD_TYPE_MINOR_STATS_UPGRADE" then
|
|
szAbilityName = MINOR_ABILITY_UPGRADES[ "base_stats_upgrades" ][ reward.id ].description
|
|
szAbilityTexture = "attribute_bonus"
|
|
end
|
|
|
|
if szAbilityName == nil then
|
|
return nil
|
|
end
|
|
|
|
local rewardStats =
|
|
{
|
|
ability_name = szAbilityName,
|
|
rarity = reward.rarity, -- 0 - normal, 1 - elite, 2 - boss
|
|
}
|
|
|
|
if reward.value ~= nil then
|
|
rewardStats.value = reward.value
|
|
end
|
|
|
|
if szAbilityTexture ~= nil then
|
|
rewardStats.ability_texture = szAbilityTexture
|
|
end
|
|
|
|
return rewardStats
|
|
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
function GenerateRewardStats( nPlayerID, szRoomDepth, roomOptions, szRewardIndex )
|
|
|
|
local hHero = PlayerResource:GetSelectedHeroEntity( nPlayerID )
|
|
local rewardStats =
|
|
{
|
|
selected_reward = GenerateRewardStatsForPlayer( hHero, roomOptions[ szRewardIndex ] ),
|
|
unselected_rewards = {}
|
|
}
|
|
|
|
for key,reward in pairs ( roomOptions ) do
|
|
if key ~= szRewardIndex then
|
|
table.insert( rewardStats.unselected_rewards, GenerateRewardStatsForPlayer( hHero, reward ) )
|
|
end
|
|
end
|
|
|
|
GameRules.Aghanim:RegisterRewardStats( nPlayerID, szRoomDepth, rewardStats )
|
|
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
function OnRewardChoice( eventSourceIndex, data )
|
|
|
|
local nPlayerID = data["PlayerID"]
|
|
local szRoomDepth = tostring(data["room_depth"])
|
|
local szRewardIndex = tostring(data["reward_index"])
|
|
|
|
--printf("Processing reward choice for Player %d, %s", nPlayerID, DeepToString(data))
|
|
|
|
local rewardOptions = CustomNetTables:GetTableValue( "reward_options", szRoomDepth )
|
|
if rewardOptions == nil then
|
|
return
|
|
end
|
|
|
|
local roomOptions = rewardOptions[ tostring( nPlayerID ) ]
|
|
|
|
--printf("reward options data %d %s %s %s", nPlayerID, szRewardIndex, DeepToString(roomOptions.keys), DeepToString(roomOptions));
|
|
|
|
local aReward = roomOptions[szRewardIndex]
|
|
|
|
GrantRewards( nPlayerID, szRoomDepth, aReward )
|
|
|
|
GenerateRewardStats( nPlayerID, szRoomDepth, roomOptions, szRewardIndex )
|
|
end |