diff --git a/.luacheckrc b/.luacheckrc index bc13f7dada..070031f17a 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -20,6 +20,8 @@ globals = { -- these globals can be set and accessed. "BOSS_WANDERER_MIN_RESPAWN_TIME", "BOSS_WANDERER_MIN_SPAWN_TIME", "BOTTLE_DESPAWN_TIME", +"BOTTLEPASS_ARCANA_REWARDS", +"BOTTLEPASS_LEVEL_REWARDS", "BOUNTY_RUNE_INITIAL_TEAM_GOLD", "BOUNTY_RUNE_INITIAL_TEAM_XP", "BOUNTY_RUNE_SPAWN_INTERVAL", diff --git a/game/scripts/vscripts/components/bottlepass/special_rewards.lua b/game/scripts/vscripts/components/bottlepass/special_rewards.lua index 94f061e13e..1398f644ab 100644 --- a/game/scripts/vscripts/components/bottlepass/special_rewards.lua +++ b/game/scripts/vscripts/components/bottlepass/special_rewards.lua @@ -1,4 +1,3 @@ - -- special bottles, these shouldn't be given out freely: -- 1: contributor bottle -- 2: tournament winner bottle @@ -11,6 +10,34 @@ -- 5: universe -- 56: dota 1 bottle +BOTTLEPASS_LEVEL_REWARDS = { + -- Format: [level] = bottle_id + -- Available special bottles: + -- 60: frostkitten bottle (swirly blue) + -- 4: better rainbow bottle + -- 43: zapp bottle (ghost ship) + -- 5: universe bottle + -- 56: dota 1 bottle + + [10] = 60, -- Level 10: Frostkitten bottle + [20] = 4, -- Level 20: Rainbow bottle + [50] = 43, -- Level 50: Ghost ship bottle + [75] = 5, -- Level 75: Universe bottle + [100] = 56, -- Level 100: Dota 1 bottle +} + +BOTTLEPASS_ARCANA_REWARDS = { + -- Format: [level] = arcana_id + -- Available arcanas: + -- DBZSohei: Dragon Ball Z themed Sohei + -- RockElectrician: Rock-themed Electrician + -- PepsiSohei: Pepsi-themed Sohei + + [15] = 'DBZSohei', -- Level 15: DBZ Sohei + [30] = 'RockElectrician', -- Level 30: Rock Electrician + [50] = 'PepsiSohei', -- Level 50: Pepsi Sohei +} + SPECIAL_BOTTLES = { --comments with names indicate a person having access to that icon but another one being currently active diff --git a/game/scripts/vscripts/components/heroselection/heroselection.lua b/game/scripts/vscripts/components/heroselection/heroselection.lua index e8463f2634..265f25d0de 100644 --- a/game/scripts/vscripts/components/heroselection/heroselection.lua +++ b/game/scripts/vscripts/components/heroselection/heroselection.lua @@ -1,4 +1,3 @@ - if HeroSelection == nil then Debug:EnableDebugging() DebugPrint ('Starting HeroSelection') @@ -352,26 +351,74 @@ function HeroSelection:StartSelection () end function HeroSelection:BuildBottlePass() - -- Ideally the bottle info would be moved to the server with {steamId, {List of bottles}} local special_bottles = {} local special_arcanas = {} HeroSelection.SelectedBottle = {} HeroSelection.SelectedArcana = {} + for playerID = 0, DOTA_MAX_TEAM_PLAYERS - 1 do if PlayerResource:IsValidPlayerID(playerID) and PlayerResource:IsValidPlayer(playerID) then local steamid = PlayerResource:GetSteamAccountID(playerID) + -- get bottlepass level from server component + local bottlepassLevel = Bottlepass.userData[steamid].bottlepassLevel if steamid ~= 0 then + -- Handle bottles + local playerBottles = {} if SPECIAL_BOTTLES[steamid] then - special_bottles[playerID] = { SteamId = steamid, PlayerId = playerID, Bottles = SPECIAL_BOTTLES[steamid]} - HeroSelection.SelectedBottle[playerID] = SPECIAL_BOTTLES[steamid][#(SPECIAL_BOTTLES[steamid])] + playerBottles = SPECIAL_BOTTLES[steamid] + end + + for level, bottleId in pairs(BOTTLEPASS_LEVEL_REWARDS) do + if bottlepassLevel >= level then + local hasBottle = false + for _, existingBottle in ipairs(playerBottles) do + if existingBottle == bottleId then + hasBottle = true + break + end + end + + if not hasBottle then + table.insert(playerBottles, bottleId) + end + end + end + + if #playerBottles > 0 then + special_bottles[playerID] = { SteamId = steamid, PlayerId = playerID, Bottles = playerBottles } + HeroSelection.SelectedBottle[playerID] = playerBottles[#playerBottles] + end + + -- Handle arcanas + local playerArcanas = {} + if SPECIAL_ARCANAS[steamid] then + playerArcanas = SPECIAL_ARCANAS[steamid] + end + + for level, arcanaId in pairs(BOTTLEPASS_ARCANA_REWARDS) do + if bottlepassLevel >= level then + local hasArcana = false + for _, existingArcana in ipairs(playerArcanas) do + if existingArcana == arcanaId then + hasArcana = true + break + end + end + + if not hasArcana then + table.insert(playerArcanas, arcanaId) + end + end + end + + if #playerArcanas > 0 then + special_arcanas[playerID] = { SteamId = steamid, PlayerId = playerID, Arcanas = playerArcanas } end - special_arcanas[playerID] = { SteamId = steamid, PlayerId = playerID, Arcanas = {'DBZSohei', 'RockElectrician', 'PepsiSohei'}} end end end - -- Populate table with playerIds and list of bottles/arcanas for players CustomNetTables:SetTableValue( 'bottlepass', 'special_bottles', special_bottles ) CustomNetTables:SetTableValue( 'bottlepass', 'special_arcanas', special_arcanas ) end diff --git a/game/scripts/vscripts/components/zonecontrol/cleaner.lua b/game/scripts/vscripts/components/zonecontrol/cleaner.lua index 2dd8ddde35..77cb4d8407 100644 --- a/game/scripts/vscripts/components/zonecontrol/cleaner.lua +++ b/game/scripts/vscripts/components/zonecontrol/cleaner.lua @@ -1,4 +1,3 @@ - if ZoneCleaner == nil then Debug.EnabledModules['zonecontrol:cleaner'] = true DebugPrint('Creating ZoneCleaner') @@ -31,21 +30,44 @@ ZoneCleaner.ForbiddenEntities = { } function ZoneCleaner:CleanZone(state) - --DebugDrawBox(state.origin, state.bounds.Mins, state.bounds.Maxs, 255, 100, 0, 0, 30) - --DebugDrawSphere(state.origin, Vector(255, 100, 0), 0, max(state.bounds.Maxs.x + state.bounds.Maxs.y, state.bounds.Mins.x + state.bounds.Mins.y), true, 30) - local radius = math.max(math.max(math.abs(state.bounds.Mins.x), math.abs(state.bounds.Maxs.x)), math.max(math.abs(state.bounds.Mins.y), math.abs(state.bounds.Maxs.y))) + 600 + -- Calculate zone bounds + local minX = state.origin.x + state.bounds.Mins.x + local maxX = state.origin.x + state.bounds.Maxs.x + local minY = state.origin.y + state.bounds.Mins.y + local maxY = state.origin.y + state.bounds.Maxs.y + + -- Use a smaller padding for the search radius + local padding = 200 + local radius = math.max(math.max(math.abs(state.bounds.Mins.x), math.abs(state.bounds.Maxs.x)), + math.max(math.abs(state.bounds.Mins.y), math.abs(state.bounds.Maxs.y))) + padding + local entities = Entities:FindAllInSphere(state.origin, radius) for _,entity in pairs(entities) do - for _,entry in pairs(ZoneCleaner.ForbiddenEntities) do - if not entity:IsNull() then - if entry == entity:GetName() then - entity:RemoveSelf() + if not entity:IsNull() then + -- Get entity position + local pos = entity:GetAbsOrigin() + + -- Only remove if entity is actually inside the zone bounds + if pos.x >= minX and pos.x <= maxX and + pos.y >= minY and pos.y <= maxY then + for _,entry in pairs(self.ForbiddenEntities) do + if entry == entity:GetName() then + entity:RemoveSelf() + break + end end end end end - -- Clean up trees - GridNav:DestroyTreesAroundPoint(state.origin, radius, true) + -- Calculate minimum radius needed to encompass the entire rectangular arena + -- Using Pythagorean theorem: radius = sqrt(width^2 + height^2) / 2 + local width = maxX - minX + local height = maxY - minY + local treeRadius = math.sqrt(width * width + height * height) / 2 + -- Add small padding to ensure we get trees right at the edges + treeRadius = treeRadius + 50 + + GridNav:DestroyTreesAroundPoint(state.origin, treeRadius, true) end