From 3109a439ae500b52613858c74d59c69277e1ed7b Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 17 May 2021 18:13:58 -0400 Subject: [PATCH 01/77] Armored Enemies lose outlines when mod is off for a biome, didn't before --- Colorblind/colorblind.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Colorblind/colorblind.lua b/Colorblind/colorblind.lua index 8e0798d..3a52f0a 100644 --- a/Colorblind/colorblind.lua +++ b/Colorblind/colorblind.lua @@ -114,7 +114,10 @@ ModUtil.BaseOverride("DoEnemyHealthBufferDeplete", function ( enemy ) _G[enemy.OnHealthBufferDepleteFunctionName]( enemy ) end -- Mod start - -- RemoveOutline({ Id = enemy.ObjectId }) + local biome = CurrentRun.CurrentRoom.RoomSetName + if not ColorblindMod.config[(biome or "nil") .. "Enabled"] then + RemoveOutline({ Id = enemy.ObjectId }) + end -- Mod end if enemy.TetherIds ~= nil then for k, tetherId in ipairs( enemy.TetherIds ) do From 0872bfccaa2553dd5e5dc011c4c125311b768f02 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 17 May 2021 19:14:00 -0400 Subject: [PATCH 02/77] Bugfixes addressing issues 32, 31, 30, 29, 27, and more. Also includes features for boon selector mod (toggles for different controls in config) in addition to bugfixes there as well. --- DontGetVorimed/DontGetVorimed.lua | 36 +- .../Scripts/EllosBoonSelectorMod.lua | 332 +++++++++++++++--- InteractableChaos/InteractableChaos.lua | 3 + LootChoiceExt/LootChoiceExt.lua | 99 ++++-- 4 files changed, 372 insertions(+), 98 deletions(-) diff --git a/DontGetVorimed/DontGetVorimed.lua b/DontGetVorimed/DontGetVorimed.lua index 648b25a..7ae1361 100644 --- a/DontGetVorimed/DontGetVorimed.lua +++ b/DontGetVorimed/DontGetVorimed.lua @@ -2,6 +2,7 @@ DontGetVorimed v1.0 Authors: ellomenop (Discord: ellomenop#2254) + cgull (Discord: cgull#4469) Makes the first boon reward offer all 4 core boons ]] ModUtil.RegisterMod("DontGetVorimed") @@ -11,29 +12,56 @@ local config = { Enabled = true } DontGetVorimed.config = config +DontGetVorimed.BoonTakenFlag = nil -- When the first room is created, set the number of loot choices to 4 -- This will persist until the first boon reward is received or rolled over ModUtil.WrapBaseFunction("ChooseStartingRoom", function ( baseFunc, currentRun, roomSetName ) if config.Enabled then LootChoiceExt.Choices = 4 + DontGetVorimed.BoonTakenFlag = false end return baseFunc(currentRun, roomSetName) end, DontGetVorimed) +ModUtil.WrapBaseFunction("CreateLoot", function( baseFunc, args ) + + local lootData = args.LootData or LootData[args.Name] + if config.Enabled and not lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then + LootChoiceExt.Choices = 3 + LootChoiceExt.LastLootChoices = 3 + local loot = baseFunc(args) + return loot + else + return baseFunc(args) + end +end, DontGetVorimed) + -- After first boon reward has been selected, return to normal number of choices ModUtil.WrapBaseFunction("HandleUpgradeChoiceSelection", function ( baseFunc, screen, button ) - if config.Enabled and button.Data.God ~= nil then - LootChoiceExt.Choices = 3 + if config.Enabled and not DontGetVorimed.BoonTakenFlag then + if button.Data.God ~= nil then + LootChoiceExt.Choices = 3 + LootChoiceExt.LastLootChoices = 3 + DontGetVorimed.BoonTakenFlag = true + else + -- reset to 4 options after selecting a hammer/chaos before first boon has been taken + LootChoiceExt.Choices = 4 + LootChoiceExt.LastLootChoices = 4 + + end end + baseFunc(screen, button) end, DontGetVorimed) -- If the player ever rerolls, reduce to 3 options ModUtil.WrapBaseFunction("DestroyBoonLootButtons", function ( baseFunc, lootData ) baseFunc(lootData) - if config.Enabled and lootData.GodLoot then + if config.Enabled and lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then LootChoiceExt.Choices = 3 LootChoiceExt.LastLootChoices = 3 + DontGetVorimed.BoonTakenFlag = true end -end, DontGetVorimed) + +end, DontGetVorimed) \ No newline at end of file diff --git a/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua b/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua index e406f79..cd3b23b 100644 --- a/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua +++ b/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua @@ -1,17 +1,23 @@ ModUtil.RegisterMod("EllosBoonSelectorMod") local config = { - ShowPreview = true + showPreview = true, + showChaosPreview = false, + showNextRoomPreview = false, + chaosFiltersActive = false, + boonRarityFiltersActive = false, } EllosBoonSelectorMod.config = config EllosBoonSelectorMod.RarityFilter = {} EllosBoonSelectorMod.GodFilter = "" +EllosBoonSelectorMod.ChaosFilter = "" EllosBoonSelectorMod.CurrentHammerIndex = 1 EllosBoonSelectorMod.RarityColors = {Color.BoonPatchCommon, Color.BoonPatchRare, Color.BoonPatchEpic, Color.BoonPatchLegendary} EllosBoonSelectorMod.BoonGods = {"Aphrodite", "Ares", "Artemis", "Athena", "Demeter", "Dionysus", "Poseidon", "Zeus"} +EllosBoonSelectorMod.ChaosLocations = { "No Chaos", "Room 1 Chaos", "Room 2 Chaos" } EllosBoonSelectorMod.PriorityBoons = {"WeaponTrait","SecondaryTrait","RangedTrait","RushTrait"} EllosBoonSelectorMod.PriorityBoonsOrder = {WeaponTrait = 1, SecondaryTrait = 2, RangedTrait = 3, RushTrait = 4} EllosBoonSelectorMod.PriorityBoonCannonicalNameToCodeName = { @@ -169,7 +175,7 @@ function OpenSeedControlScreen( args ) }) -- Roll seed for filters button - components.ClearFiltersButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 320, Y = 760 }) + components.ClearFiltersButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 320, Y = 810 }) components.ClearFiltersButton.OnPressedFunctionName = "ClearFilters" CreateTextBox({ Id = components.ClearFiltersButton.Id, Text = "Clear Filters", @@ -186,7 +192,7 @@ function OpenSeedControlScreen( args ) }) -- Roll seed for filters button - components.RollSeedForFiltersButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 625, Y = 760 }) + components.RollSeedForFiltersButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 625, Y = 810 }) components.RollSeedForFiltersButton.OnPressedFunctionName = "RollSeedForFilters" CreateTextBox({ Id = components.RollSeedForFiltersButton.Id, Text = "Roll Seed For Filters", @@ -203,8 +209,12 @@ function OpenSeedControlScreen( args ) }) components.FiltersTitle = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 475, Y = 520 }) + local displayText = "Filter for a Specific God" + if EllosBoonSelectorMod.config.boonRarityFiltersActive then + displayText = displayText.." and Boon" + end CreateTextBox({ Id = components.FiltersTitle.Id, - Text = "Filter for a Specific God and Boon", + Text = displayText, OffsetX = 0, OffsetY = -100, FontSize = 28, Color = Color.White, @@ -245,31 +255,59 @@ function OpenSeedControlScreen( args ) end end + if EllosBoonSelectorMod.config.chaosFiltersActive then + -- Chaos filter buttons + x = -225 + y = 290 + for _, location in pairs(EllosBoonSelectorMod.ChaosLocations) do + components[location .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.66, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) + components[location .. "Filter"].OnPressedFunctionName = "ToggleChaosFilter" + components[location .. "Filter"].ChaosLocation = location + CreateTextBox({ Id = components[location .. "Filter"].Id, + Text = location, + OffsetX = 0, OffsetY = 0, + FontSize = 16, + Color = Color.BoonPatchCommon, + Font = "AlegreyaSansSCRegular", + ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, + Justification = "Center", + DataProperties = + { + OpacityWithOwner = true, + }, + }) + + x = x + 200 + end + end + -- Rarity filter buttons x = -250 y = 150 - for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do - components[priorityBoon .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.5, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) - components[priorityBoon .. "Filter"].OnPressedFunctionName = "CycleRarityFilter" - components[priorityBoon .. "Filter"].PriorityBoon = EllosBoonSelectorMod.PriorityBoonCannonicalNameToCodeName[priorityBoon] - CreateTextBox({ Id = components[priorityBoon .. "Filter"].Id, - Text = priorityBoon, - OffsetX = 0, OffsetY = 0, - FontSize = 16, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - x = x + 150 - if x > 250 then - x = -250 - y = y + 60 - end + if EllosBoonSelectorMod.config.boonRarityFiltersActive then + for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do + components[priorityBoon .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.5, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) + components[priorityBoon .. "Filter"].OnPressedFunctionName = "CycleRarityFilter" + components[priorityBoon .. "Filter"].PriorityBoon = EllosBoonSelectorMod.PriorityBoonCannonicalNameToCodeName[priorityBoon] + CreateTextBox({ Id = components[priorityBoon .. "Filter"].Id, + Text = priorityBoon, + OffsetX = 0, OffsetY = 0, + FontSize = 16, + Color = Color.White, + Font = "AlegreyaSansSCRegular", + ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, + Justification = "Center", + DataProperties = + { + OpacityWithOwner = true, + }, + }) + x = x + 150 + if x > 250 then + x = -250 + y = y + 60 + end + end end -- Hammer filter button @@ -350,12 +388,29 @@ function OpenSeedControlScreen( args ) OpacityWithOwner = true, }, }) - components.ChaosRoomIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.ChaosRoomIndicator.Id, + if EllosBoonSelectorMod.config.showChaosPreview then + components.ChaosRoomIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) + CreateTextBox({ Id = components.ChaosRoomIndicator.Id, + Text = "", + OffsetX = 400, OffsetY = 40, + FontSize = 22, + Color = Color.Purple, + Font = "AlegreyaSansSCRegular", + ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, + Justification = "Center", + DataProperties = + { + OpacityWithOwner = true, + }, + }) + end + if EllosBoonSelectorMod.config.showNextRoomPreview then + components.NextRoomNameIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) + CreateTextBox({ Id = components.NextRoomNameIndicator.Id, Text = "", - OffsetX = 400, OffsetY = 40, + OffsetX = 400, OffsetY = 70, FontSize = 22, - Color = Color.Purple, + Color = Color.White, Font = "AlegreyaSansSCRegular", ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, Justification = "Center", @@ -364,6 +419,21 @@ function OpenSeedControlScreen( args ) OpacityWithOwner = true, }, }) + components.NextRoomRewardIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) + CreateTextBox({ Id = components.NextRoomRewardIndicator.Id, + Text = "", + OffsetX = 400, OffsetY = 100, + FontSize = 22, + Color = Color.White, + Font = "AlegreyaSansSCRegular", + ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, + Justification = "Center", + DataProperties = + { + OpacityWithOwner = true, + }, + }) + end local roomReward = PredictStartingRoomReward((NextSeeds[1] or 000000)) UpdateRewardPreview( screen, roomReward ) @@ -384,6 +454,14 @@ function OpenSeedControlScreen( args ) end function DoesRewardMatchFilters(roomReward) + if EllosBoonSelectorMod.ChaosFilter == "No Chaos" and (roomReward.FirstRoomChaos or roomReward.SecondRoomChaos) then + return false + elseif EllosBoonSelectorMod.ChaosFilter == "Room 1 Chaos" and not roomReward.FirstRoomChaos then + return false + elseif EllosBoonSelectorMod.ChaosFilter == "Room 2 Chaos" and not roomReward.SecondRoomChaos then + return false + end + local targetReward = "Boon" if EllosBoonSelectorMod.GodFilter == "" and next(EllosBoonSelectorMod.RarityFilter) == nil then @@ -401,14 +479,14 @@ function DoesRewardMatchFilters(roomReward) return false end - -- Rarities much match all active rarity filters + -- Rarities much match all active rarity filters for priorityBoon, rarityFilter in pairs(EllosBoonSelectorMod.RarityFilter) do local thisFilterPassed = false; for index, boonOption in ipairs(roomReward.BoonData.Options) do if boonOption.Blocked == nil then if rarityFilter == nil or rarityFilter == 0 then thisFilterPassed = true - elseif rarityFilter > 0 and priorityBoon == boonOption.Boon and rarityFilter <= boonOption.Rarity then + elseif rarityFilter > 0 and priorityBoon == PriorityBoonType(boonOption.Boon) and rarityFilter <= boonOption.Rarity then thisFilterPassed = true end end @@ -469,14 +547,22 @@ end function ClearFilters ( screen, button ) EllosBoonSelectorMod.GodFilter = "" + EllosBoonSelectorMod.ChaosFilter = "" EllosBoonSelectorMod.RarityFilter = {} -- TODO: Use a constant for the priority boons so we don't hardcode them in multiple places for _, god in pairs(EllosBoonSelectorMod.BoonGods) do ModifyTextBox({ Id = screen.Components[god .. "Filter"].Id, Color = Color.BoonPatchCommon }) end - for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do - ModifyTextBox({ Id = screen.Components[priorityBoon .. "Filter"].Id, Color = Color.BoonPatchCommon }) + if EllosBoonSelectorMod.config.chaosFiltersActive then + for _, location in pairs(EllosBoonSelectorMod.ChaosLocations) do + ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.BoonPatchCommon }) + end + end + if EllosBoonSelectorMod.config.boonRarityFiltersActive then + for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do + ModifyTextBox({ Id = screen.Components[priorityBoon .. "Filter"].Id, Color = Color.BoonPatchCommon }) + end end end @@ -494,6 +580,19 @@ function ToggleGodFilter ( screen, button ) end end +function ToggleChaosFilter( screen, button ) + local chaosLocation = button.ChaosLocation + EllosBoonSelectorMod.ChaosFilter = chaosLocation + + for _, location in pairs(EllosBoonSelectorMod.ChaosLocations) do + if location == chaosLocation then + ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.BoonPatchCommon }) + else + ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.Gray }) + end + end +end + function CycleRarityFilter ( screen, button ) local priorityBoon = button.PriorityBoon @@ -609,6 +708,58 @@ function CloseSeedControlScreen( screen, button ) OnScreenClosed({ Flag = screen.Name }) end +function IsSecondRoomRewardEligible(requirements, firstRoomReward) + if requirements.RequiredUpgradeableGodTraits ~= nil and requirements.RequiredUpgradeableGodTraits >= 1 and firstRoomReward ~= "Boon" then + return false + end + if requirements.RequiredMaxWeaponUpgrades ~= nil and requirements.RequiredMaxWeaponUpgrades < 1 and firstRoomReward == "Hammer" then + return false + end + if requirements.RequiredMinDepth ~= nil and requirements.RequiredMinDepth >= 2 then + return false + end + if requirements.RequiredMinBiomeDepth ~= nil and requirements.RequiredMinBiomeDepth >= 2 then + return false + end + if requirements.RequiredFalseCosmetics ~= nil then + return false + end + return true +end + +function PredictSecondRoomReward(seedForPrediction, firstRoomReward, firstRoomShrine, secondRoomName) + RandomSetNextInitSeed( {Seed = seedForPrediction} ) + local rewardStore = "MetaProgress" + if firstRoomShrine or secondRoomName == "RoomSimple01" then + rewardStore = "RunProgress" + end + local eligibleRewards = {} + for key, reward in pairs(RewardStoreData[rewardStore]) do + if IsSecondRoomRewardEligible(reward.GameStateRequirements, firstRoomReward) then + table.insert(eligibleRewards, key) + end + end + RandomSynchronize(4) + local selectedKey = GetRandomValue( eligibleRewards ) + local reward = RewardStoreData[rewardStore][selectedKey].Name + RemoveValueAndCollapse( eligibleRewards, selectedKey ) + local eligibleGods = DeepCopyTable(EllosBoonSelectorMod.BoonGods) + if reward == "Boon" then + reward = GetRandomValue( eligibleGods ) + RemoveValueAndCollapse( eligibleGods, reward ) + end + if firstRoomShrine then + -- roll for the actual exit second + RandomSynchronize(4) + selectedKey = GetRandomValue( eligibleRewards ) + reward = RewardStoreData.RunProgress[selectedKey].Name + if reward == "Boon" then + reward = GetRandomValue( eligibleGods ) + end + end + return reward +end + --- Takes in a seed and returns the predicted RoomReward object for the starting room. -- TODO: Update this to use a separate RNG entirely rather than using the current RNG and putting it back again -- @@ -628,21 +779,63 @@ function PredictStartingRoomReward( seedForPrediction, currentSeed ) -- Get exact boon rewards and rarity and update the menu roomReward.BoonData.Options = PredictStartingGodBoonOptions( roomReward.BoonData.God, seedForPrediction, currentSeed ) roomReward.FirstRoomChaos = PredictChaos(6, seedForPrediction) + if roomReward.FirstRoomChaos then + roomReward.FirstRoomShrine = false + else + roomReward.FirstRoomShrine = PredictChaos(7, seedForPrediction) + end elseif roomReward.Type == "Hammer" then roomReward.HammerData = {} local currentWeapon = GetEquippedWeapon() roomReward.HammerData.Options = PredictHammerOptionsForWeapon(currentWeapon, GetEquippedWeaponTraitIndex( currentWeapon ), seedForPrediction, currentSeed) roomReward.FirstRoomChaos = PredictChaos(5, seedForPrediction) + + if roomReward.FirstRoomChaos then + roomReward.FirstRoomShrine = false + else + roomReward.FirstRoomShrine = PredictChaos(6, seedForPrediction) + end end - roomReward.SecondRoomChaos = PredictChaos(2, seedForPrediction) + if roomReward.FirstRoomChaos then + roomReward.SecondRoomChaos = false + else + roomReward.SecondRoomChaos = PredictChaos(2, seedForPrediction) + end + RandomSynchronize(1) -- Known offset at which the RNG rolls the room + roomReward.SecondRoomName = GetRandomValue({ + "A_Combat01", + "A_Combat02", + "A_Combat03", + "A_Combat04", + "A_Combat05", + "A_Combat06", + "A_Combat07", + "A_Combat08A", + "A_Combat09", + "A_Combat10", + "A_Combat12", + "A_Combat13", + "A_Combat14", + "A_Combat15", + "A_Combat16", + "A_Combat19", + "A_Combat21", + "A_Combat24", + "RoomSimple01" + }) + roomReward.SecondRoomReward = PredictSecondRoomReward( + seedForPrediction, + roomReward.Type, + roomReward.FirstRoomShrine, + roomReward.SecondRoomName) return roomReward end function GetBlockedIndicesForAP() -- Resync and calculate blocked indices from AP - RandomSynchronize(1) -- Sometimes 0 sometimes 1, maybe chaos makes 0? + RandomSynchronize(2) local blockedIndices = {} for i = 1, 3 do table.insert( blockedIndices, i ) @@ -760,11 +953,6 @@ function PredictHammerOptionsForWeapon( weapon, aspectIndex, seedForPrediction, end end - for _ , value in pairs(blockedIndices) do - if value == index then - hammerOptions[index].Blocked = true - end - end end -- Reset RNG to the pre-call state @@ -772,9 +960,22 @@ function PredictHammerOptionsForWeapon( weapon, aspectIndex, seedForPrediction, RandomSetNextInitSeed( {Seed = currentSeed} ) end + for _ , value in pairs(blockedIndices) do + hammerOptions[value].Blocked = true + end + return hammerOptions end +function PriorityBoonType(boon) + for _, boonType in pairs(EllosBoonSelectorMod.PriorityBoons) do + if string.find(boon, boonType) then + return boonType + end + end + return "No boon type found." +end + --- Takes in a gods name and seed and returns a list of BoonOption objects for the starting room only. -- TODO: Update this to use a separate RNG entirely rather than using the current RNG and putting it back again -- @@ -790,8 +991,21 @@ function PredictStartingGodBoonOptions( god, seedForPrediction, currentSeed ) local blockedIndices = GetBlockedIndicesForAP() RandomSynchronize(1) -- Offset that when the boons start being rolled at + -- Checking for Aspect of Beowulf for cast changes, ignoring poseidon/dionysus + local beowulfFlag = HeroHasTrait("ShieldLoadAmmoTrait") and god ~= "Poseidon" and god ~= "Dionysus" + -- First room always offers 3 priority boons (selected by excluding one of the 4 options) local boonRewards = DeepCopyTable(EllosBoonSelectorMod.PriorityBoons) + + for i, boonReward in ipairs(boonRewards) do + if boonReward == "RangedTrait" and beowulfFlag then + boonRewards[3] = "ShieldLoadAmmo_"..god..boonReward + else + boonRewards[i] = god..boonReward + end + end + + local excluded = RemoveRandomValue(boonRewards) boonRewards = CollapseTableOrdered(boonRewards) @@ -807,12 +1021,6 @@ function PredictStartingGodBoonOptions( god, seedForPrediction, currentSeed ) else startingBoons[index].Rarity = 0 end - - for key, value in pairs(blockedIndices) do - if value == index then - startingBoons[index].Blocked = true - end - end end -- Reset RNG to the pre-call state @@ -820,7 +1028,11 @@ function PredictStartingGodBoonOptions( god, seedForPrediction, currentSeed ) RandomSetNextInitSeed( {Seed = currentSeed} ) end - table.sort( startingBoons, function(boon1, boon2) return EllosBoonSelectorMod.PriorityBoonsOrder[boon1.Boon] < EllosBoonSelectorMod.PriorityBoonsOrder[boon2.Boon] end) + table.sort( startingBoons, function(boon1, boon2) return EllosBoonSelectorMod.PriorityBoonsOrder[PriorityBoonType(boon1.Boon)] < EllosBoonSelectorMod.PriorityBoonsOrder[PriorityBoonType(boon2.Boon)] end) + + for key, value in pairs(blockedIndices) do + startingBoons[value].Blocked = true + end return startingBoons end @@ -878,7 +1090,7 @@ function ElloGetBoonRarityChances( godName, roomRarityOverride ) end function UpdateRewardPreview( screen, roomReward ) - if config.ShowPreview == false then + if config.showPreview == false then return end if roomReward.Type == "Boon" then @@ -890,7 +1102,7 @@ function UpdateRewardPreview( screen, roomReward ) if boonOption.Blocked == true then color = Color.Red end - ModifyTextBox({ Id = screen.Components["Reward" .. index].Id, Text = roomReward.BoonData.God .. boonOption.Boon, Color = color}) + ModifyTextBox({ Id = screen.Components["Reward" .. index].Id, Text = boonOption.Boon, Color = color}) end elseif roomReward.Type == "Hammer" then SetAnimation({ Name = "WeaponUpgradePreview", DestinationId = screen.Components.GodIcon.Id, OffsetX = 640, OffsetY = -45}) @@ -905,12 +1117,18 @@ function UpdateRewardPreview( screen, roomReward ) end end - if roomReward.FirstRoomChaos then - ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "Room 1 Chaos" }) - elseif roomReward.SecondRoomChaos then - ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "Room 2 Chaos" }) - else - ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "No Chaos" }) + if EllosBoonSelectorMod.config.showChaosPreview then + if roomReward.FirstRoomChaos then + ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "Room 1 Chaos" }) + elseif roomReward.SecondRoomChaos then + ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "Room 2 Chaos" }) + else + ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "No Chaos" }) + end + end + if EllosBoonSelectorMod.config.showNextRoomPreview then + ModifyTextBox({ Id = screen.Components.NextRoomNameIndicator.Id, Text = roomReward.SecondRoomName}) + ModifyTextBox({ Id = screen.Components.NextRoomRewardIndicator.Id, Text = roomReward.SecondRoomReward}) end end @@ -938,4 +1156,4 @@ ModUtil.WrapBaseFunction("CreatePrimaryBacking", function ( baseFunc ) Attach({ Id = components.RngSeedButton.Id, DestinationId = components.RngSeedButton, OffsetX = 500, OffsetY = 500 }) end baseFunc() -end, EllosBoonSelectorMod) +end, EllosBoonSelectorMod) \ No newline at end of file diff --git a/InteractableChaos/InteractableChaos.lua b/InteractableChaos/InteractableChaos.lua index 9018b9f..69527c3 100644 --- a/InteractableChaos/InteractableChaos.lua +++ b/InteractableChaos/InteractableChaos.lua @@ -62,6 +62,9 @@ ModUtil.WrapBaseFunction("HandleLootPickup", function ( baseFunc, currentRun, lo Damage( CurrentRun.Hero, { triggeredById = CurrentRun.Hero.ObjectId, DamageAmount = healthCost, PureDamage = true } ) end baseFunc(currentRun, loot) + -- Removing Chaos visual artifacts + AdjustRadialBlurDistance({ Fraction = 0, Duration = 2 }) + AdjustRadialBlurStrength({ Fraction = 0, Duration = 2 }) return -- If Zag does not have enough hp, push him away and show a text box saying why he cannot pick up the boon diff --git a/LootChoiceExt/LootChoiceExt.lua b/LootChoiceExt/LootChoiceExt.lua index 89b0260..a0e0e98 100644 --- a/LootChoiceExt/LootChoiceExt.lua +++ b/LootChoiceExt/LootChoiceExt.lua @@ -28,9 +28,9 @@ ModUtil.BaseOverride("CalcNumLootChoices", function() return numChoices end, LootChoiceExt) -ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) +ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData, reroll ) - -- BASE CODE ... + -- BASE CODE ... (From Live Steam version v1.37996, pulled on 2021.04.27) local components = ScreenAnchors.ChoiceScreen.Components local upgradeName = lootData.Name local upgradeChoiceData = LootData[upgradeName] @@ -43,7 +43,9 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) if not lootData.StackNum then lootData.StackNum = 1 end - lootData.StackNum = lootData.StackNum + GetTotalHeroTraitValue("PomLevelBonus") + if not reroll then + lootData.StackNum = lootData.StackNum + GetTotalHeroTraitValue("PomLevelBonus") + end local tooltipData = {} local itemLocationY = 370 @@ -105,6 +107,13 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) local squashY = 3/(3+excess) local fsquashT1 = 0 if excess < 3 then fsquashT1 = 1 end + local iconScaling + if excess == 0 then + iconScaling = 0.85 + else + iconScaling = 1.0 + end + for itemIndex, itemData in ipairs( upgradeOptions ) do local squashT1 = fsquashT1 @@ -112,7 +121,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) components[itemBackingKey] = CreateScreenComponent({ Name = "TraitBacking", Group = "Combat_Menu", X = ScreenCenterX, Y = itemLocationY }) SetScaleY({ Id = components[itemBackingKey].Id, Fraction = 1.25*squashY }) - -- BASE CODE ... + -- BASE CODE ...(From Live Steam version v1.37996, pulled on 2021.04.27) local upgradeData = nil local upgradeTitle = nil @@ -124,11 +133,11 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) upgradeTitle = "TraitLevel_Upgrade" upgradeData.Title = upgradeData.Name else - upgradeTitle = upgradeData.Name + upgradeTitle = GetTraitTooltipTitle( TraitData[itemData.ItemName] ) - upgradeData.Title = upgradeData.Name .."_Initial" + upgradeData.Title = GetTraitTooltipTitle( TraitData[itemData.ItemName] ) .."_Initial" if not HasDisplayName({ Text = upgradeData.Title }) then - upgradeData.Title = upgradeData.Name + upgradeData.Title = GetTraitTooltipTitle( TraitData[itemData.ItemName] ) end end @@ -139,15 +148,24 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) tooltipData = GetProcessedTraitData({ Unit = CurrentRun.Hero, TraitName = itemData.ItemName, FakeStackNum = existingNum, RarityMultiplier = upgradeData.RarityMultiplier}) if existingNum > 1 then upgradeTitle = "TraitLevel_Exchange" - tooltipData.Title = upgradeData.Name + tooltipData.Title = GetTraitTooltipTitle( TraitData[upgradeData.Name]) tooltipData.Level = existingNum end elseif lootData.StackOnly then tooltipData = GetProcessedTraitData({ Unit = CurrentRun.Hero, TraitName = itemData.ItemName, FakeStackNum = lootData.StackNum, RarityMultiplier = upgradeData.RarityMultiplier}) tooltipData.OldLevel = traitNum; tooltipData.NewLevel = traitNum + lootData.StackNum; - tooltipData.Title = upgradeData.Name + tooltipData.Title = GetTraitTooltipTitle( TraitData[itemData.ItemName] ) + upgradeData.Title = tooltipData.Title else + if upgradeData.Rarity == "Legendary" then + if TraitData[upgradeData.Name].IsDuoBoon then + CreateAnimation({ Name = "BoonEntranceDuo", DestinationId = components[itemBackingKey].Id }) + else + CreateAnimation({ Name = "BoonEntranceLegendary", DestinationId = components[itemBackingKey].Id }) + end + end + tooltipData = upgradeData end SetTraitTextData( tooltipData ) @@ -158,7 +176,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) upgradeData = GetRampedConsumableData(ConsumableData[itemData.ItemName], itemData.Rarity) upgradeTitle = upgradeData.Name - upgradeDescription = upgradeTitle + upgradeDescription = GetTraitTooltip(upgradeData) if upgradeData.UseFunctionArgs ~= nil then if upgradeData.UseFunctionName ~= nil and upgradeData.UseFunctionArgs.TraitName ~= nil then @@ -224,20 +242,20 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) -- NEW CODE ... - local iconOffsetX = -323 + local iconOffsetX = -338 --(From Live Steam version v1.37996, pulled on 2021.04.27) local iconOffsetY = -2*squashY local exchangeIconPrefix = nil - local overlayLayer = "Combat_Menu_Overlay" + local overlayLayer = "Combat_Menu_Overlay_Backing" --(From Live Steam version v1.37996, pulled on 2021.04.27) components[purchaseButtonKey] = CreateScreenComponent({ Name = "BoonSlot"..RandomInt(1,3), Group = "Combat_Menu", Scale = 1, X = itemLocationX + buttonOffsetX, Y = itemLocationY }) SetScaleY({Id = components[purchaseButtonKey].Id, Fraction = squashY}) if upgradeData.CustomRarityColor then - components[purchaseButtonKey.."Patch"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = iconOffsetX + itemLocationX + buttonOffsetX + 15, Y = iconOffsetY + itemLocationY }) + components[purchaseButtonKey.."Patch"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = iconOffsetX + itemLocationX + buttonOffsetX + 38, Y = iconOffsetY + itemLocationY }) SetAnimation({ DestinationId = components[purchaseButtonKey.."Patch"].Id, Name = "BoonRarityPatch"}) SetColor({ Id = components[purchaseButtonKey.."Patch"].Id, Color = upgradeData.CustomRarityColor }) SetScaleY({Id = components[purchaseButtonKey.."Patch"].Id, Fraction = squashY}) elseif itemData.Rarity ~= "Common" then - components[purchaseButtonKey.."Patch"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = iconOffsetX + itemLocationX + buttonOffsetX + 15, Y = iconOffsetY + itemLocationY }) + components[purchaseButtonKey.."Patch"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = iconOffsetX + itemLocationX + buttonOffsetX + 38, Y = iconOffsetY + itemLocationY }) SetAnimation({ DestinationId = components[purchaseButtonKey.."Patch"].Id, Name = "BoonRarityPatch"}) SetColor({ Id = components[purchaseButtonKey.."Patch"].Id, Color = Color["BoonPatch" .. itemData.Rarity] }) SetScaleY({Id = components[purchaseButtonKey.."Patch"].Id, Fraction = squashY}) @@ -251,7 +269,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) Text = "ReducedLootChoicesKeyword", OffsetX = textOffset, OffsetY = -30*squashY, Color = Color.Transparent, - Width = 615, + Width = 675, --(From Live Steam version v1.37996, pulled on 2021.04.27) }) thread( TraitLockedPresentation, { squashY = squashY, squashTI = squashTI, Components = components, Id = purchaseButtonKey, OffsetX = itemLocationX + buttonOffsetX, OffsetY = iconOffsetY + itemLocationY }) end @@ -259,8 +277,8 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) if upgradeData.Icon ~= nil then components[purchaseButtonKey.."Icon"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = iconOffsetX + itemLocationX + buttonOffsetX, Y = iconOffsetY + itemLocationY }) SetAnimation({ DestinationId = components[purchaseButtonKey.."Icon"].Id, Name = upgradeData.Icon .. "_Large" }) - SetScaleY({Id = components[purchaseButtonKey.."Icon"].Id, Fraction = squashY}) - SetScaleX({Id = components[purchaseButtonKey.."Icon"].Id, Fraction = squashY}) + SetScaleY({Id = components[purchaseButtonKey.."Icon"].Id, Fraction = iconScaling*squashY}) + SetScaleX({Id = components[purchaseButtonKey.."Icon"].Id, Fraction = iconScaling*squashY}) end local locScaleModifiers = @@ -304,7 +322,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) OffsetX = textOffset, OffsetY = -12*squashY*squashT1 - blockedIconOffset, FontSize = 20, Color = {160, 160, 160, 255}, - Width = 615, + Width = 675, --(From Live Steam version v1.37996, pulled on 2021.04.27) Font = "AlegreyaSansSCRegular", ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, Justification = "Left", @@ -317,7 +335,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) OffsetX = textOffset + 150, OffsetY = -12*squashY*squashT1 - blockedIconOffset, FontSize = 20, Color = Color["BoonPatch" .. itemData.OldRarity], - Width = 615, + Width = 675, --(From Live Steam version v1.37996, pulled on 2021.04.27) Font = "AlegreyaSansSCRegular", ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, Justification = "Left", @@ -327,16 +345,16 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) end components[purchaseButtonKey.."Frame"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = iconOffsetX + itemLocationX + buttonOffsetX, Y = iconOffsetY + itemLocationY }) - SetScaleY({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = squashY}) - SetScaleX({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = squashY}) + SetScaleY({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = iconScaling*squashY}) + SetScaleX({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = iconScaling*squashY}) if upgradeData.Frame then SetAnimation({ DestinationId = components[purchaseButtonKey.."Frame"].Id, Name = "Frame_Boon_Menu_".. upgradeData.Frame}) - SetScaleY({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = squashY}) - SetScaleX({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = squashY}) + SetScaleY({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = iconScaling*squashY}) + SetScaleX({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = iconScaling*squashY}) else SetAnimation({ DestinationId = components[purchaseButtonKey.."Frame"].Id, Name = "Frame_Boon_Menu_".. itemData.Rarity}) - SetScaleY({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = squashY}) - SetScaleX({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = squashY}) + SetScaleY({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = iconScaling*squashY}) + SetScaleX({Id = components[purchaseButtonKey.."Frame"].Id, Fraction = iconScaling*squashY}) end -- Button data setup components[purchaseButtonKey].OnPressedFunctionName = "HandleUpgradeChoiceSelection" @@ -349,7 +367,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) components[components[purchaseButtonKey].Id] = purchaseButtonKey -- Creates upgrade slot text - SetInteractProperty({ DestinationId = components[purchaseButtonKey].Id, Property = "TooltipOffsetX", Value = 640 }) + SetInteractProperty({ DestinationId = components[purchaseButtonKey].Id, Property = "TooltipOffsetX", Value = 675 }) --(From Live Steam version v1.37996, pulled on 2021.04.27) local selectionString = "UpgradeChoiceMenu_PermanentItem" local selectionStringColor = Color.Black @@ -362,7 +380,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) selectionString = upgradeData.UpgradeChoiceText or "UpgradeChoiceMenu_PermanentItem" end - local textOffset = 135 - buttonOffsetX + local textOffset = 115 - buttonOffsetX --(From Live Steam version v1.37996, pulled on 2021.04.27) local exchangeIconOffset = 0 local lineSpacing = 8*squashY local text = "Boon_"..tostring(itemData.Rarity) @@ -376,8 +394,8 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) end CreateTextBox({ Id = components[purchaseButtonKey].Id, Text = text , - FontSize = 25, - OffsetX = textOffset + 600, OffsetY = -60*squashY*squashT1, + FontSize = 27, --(From Live Steam version v1.37996, pulled on 2021.04.27) + OffsetX = textOffset + 630, OffsetY = -60*squashY*squashT1, --(From Live Steam version v1.37996, pulled on 2021.04.27), minus the squashes Width = 720, Color = color, Font = "AlegreyaSansSCLight", @@ -387,7 +405,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) if exchangeIconPrefix then CreateTextBox({ Id = components[purchaseButtonKey].Id, Text = exchangeIconPrefix , - FontSize = 25, + FontSize = 27, --(From Live Steam version v1.37996, pulled on 2021.04.27) OffsetX = textOffset, OffsetY = -55*squashY*squashT1, Color = color, Font = "AlegreyaSansSCLight", @@ -402,7 +420,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) end CreateTextBox({ Id = components[purchaseButtonKey].Id, Text = upgradeTitle, - FontSize = 25, + FontSize = 27, --(From Live Steam version v1.37996, pulled on 2021.04.27) OffsetX = textOffset + exchangeIconOffset, OffsetY = -55*squashY*squashT1, Color = color, Font = "AlegreyaSansSCLight", @@ -411,17 +429,24 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) LuaKey = "TooltipData", LuaValue = tooltipData, }) + -- (From Live Steam version v1.37996, pulled on 2021.04.27) + -- Chaos curse/blessing traits need VariableAutoFormat disabled + local autoFormat = "BoldFormatGraft" + if upgradeDescription == "ChaosBlessingFormat" or itemData.Type == "TransformingTrait" then + autoFormat = nil + end + CreateTextBoxWithFormat(MergeTables({ Id = components[purchaseButtonKey].Id, Text = upgradeDescription, OffsetX = textOffset+2*ScreenCenterX*(1-fsquashT1), OffsetY = -30*squashY, - Width = 615, + Width = GetLocalizedValue(675, { { Code = "ja", Value = 670 }, }), -- (From Live Steam version v1.37996, pulled on 2021.04.27) Justification = "Left", VerticalJustification = "Top", LineSpacingBottom = lineSpacing, UseDescription = true, LuaKey = "TooltipData", LuaValue = tooltipData, Format = "BaseFormat", - VariableAutoFormat = "BoldFormatGraft", + VariableAutoFormat = autoFormat, -- (From Live Steam version v1.37996, pulled on 2021.04.27) TextSymbolScale = 0.8*squashY, }, locScaleModifiers)) @@ -433,7 +458,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) end if needsQuestIcon then - components[purchaseButtonKey.."QuestIcon"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = itemLocationX + 112, Y = itemLocationY - 55*squashY*squashT1 }) + components[purchaseButtonKey.."QuestIcon"] = CreateScreenComponent({ Name = "BlankObstacle", Group = "Combat_Menu", X = itemLocationX + 92, Y = itemLocationY - 55*squashY*squashT1 }) ---- (From Live Steam version v1.37996, pulled on 2021.04.27), except for squashing SetAnimation({ DestinationId = components[purchaseButtonKey.."QuestIcon"].Id, Name = "QuestItemFound" }) -- Silent toolip CreateTextBox({ Id = components[purchaseButtonKey].Id, TextSymbolScale = 0, Text = "TraitQuestItem", Color = Color.Transparent, LuaKey = "TooltipData", LuaValue = tooltipData, }) @@ -453,8 +478,8 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData ) end -- BASE CODE ... - - if IsMetaUpgradeActive("RerollPanelMetaUpgrade") then + -- (From Live Steam version v1.37996, pulled on 2021.04.27) + if IsMetaUpgradeSelected( "RerollPanelMetaUpgrade" ) then local cost = -1 if lootData.BlockReroll then cost = -1 @@ -542,4 +567,4 @@ ModUtil.BaseOverride("DestroyBoonLootButtons", function( lootData ) ScreenAnchors.ChoiceScreen.ActivateSwapId = nil end Destroy({ Ids = toDestroy }) -end, LootChoiceExt) +end, LootChoiceExt) \ No newline at end of file From 46d735e159721334318043dcb3c63872beab13d6 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 17 May 2021 19:16:08 -0400 Subject: [PATCH 03/77] Added missing trailing newline --- LootChoiceExt/LootChoiceExt.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LootChoiceExt/LootChoiceExt.lua b/LootChoiceExt/LootChoiceExt.lua index a0e0e98..bf4006f 100644 --- a/LootChoiceExt/LootChoiceExt.lua +++ b/LootChoiceExt/LootChoiceExt.lua @@ -567,4 +567,4 @@ ModUtil.BaseOverride("DestroyBoonLootButtons", function( lootData ) ScreenAnchors.ChoiceScreen.ActivateSwapId = nil end Destroy({ Ids = toDestroy }) -end, LootChoiceExt) \ No newline at end of file +end, LootChoiceExt) From cd22bd6e29aeb20d7c93390749d66c513e14ab25 Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 21 May 2021 21:43:23 -0400 Subject: [PATCH 04/77] DontGetVorimed: reformat, simplify CreateLoot wrapper --- DontGetVorimed/DontGetVorimed.lua | 67 ++++++++++++++----------------- 1 file changed, 31 insertions(+), 36 deletions(-) diff --git a/DontGetVorimed/DontGetVorimed.lua b/DontGetVorimed/DontGetVorimed.lua index 7ae1361..dc1bbd6 100644 --- a/DontGetVorimed/DontGetVorimed.lua +++ b/DontGetVorimed/DontGetVorimed.lua @@ -16,52 +16,47 @@ DontGetVorimed.BoonTakenFlag = nil -- When the first room is created, set the number of loot choices to 4 -- This will persist until the first boon reward is received or rolled over -ModUtil.WrapBaseFunction("ChooseStartingRoom", function ( baseFunc, currentRun, roomSetName ) - if config.Enabled then - LootChoiceExt.Choices = 4 - DontGetVorimed.BoonTakenFlag = false - end - return baseFunc(currentRun, roomSetName) +ModUtil.WrapBaseFunction("ChooseStartingRoom", function ( baseFunc, ... ) + if config.Enabled then + LootChoiceExt.Choices = 4 + DontGetVorimed.BoonTakenFlag = false + end + return baseFunc(...) end, DontGetVorimed) -ModUtil.WrapBaseFunction("CreateLoot", function( baseFunc, args ) +ModUtil.WrapBaseFunction("CreateLoot", function( baseFunc, args ) + local lootData = args.LootData or LootData[args.Name] + if config.Enabled and not lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then + LootChoiceExt.Choices = 3 + LootChoiceExt.LastLootChoices = 3 + end - local lootData = args.LootData or LootData[args.Name] - if config.Enabled and not lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then - LootChoiceExt.Choices = 3 - LootChoiceExt.LastLootChoices = 3 - local loot = baseFunc(args) - return loot - else return baseFunc(args) - end end, DontGetVorimed) -- After first boon reward has been selected, return to normal number of choices ModUtil.WrapBaseFunction("HandleUpgradeChoiceSelection", function ( baseFunc, screen, button ) - if config.Enabled and not DontGetVorimed.BoonTakenFlag then - if button.Data.God ~= nil then - LootChoiceExt.Choices = 3 - LootChoiceExt.LastLootChoices = 3 - DontGetVorimed.BoonTakenFlag = true - else - -- reset to 4 options after selecting a hammer/chaos before first boon has been taken - LootChoiceExt.Choices = 4 - LootChoiceExt.LastLootChoices = 4 - + if config.Enabled and not DontGetVorimed.BoonTakenFlag then + if button.Data.God ~= nil then + LootChoiceExt.Choices = 3 + LootChoiceExt.LastLootChoices = 3 + DontGetVorimed.BoonTakenFlag = true + else + -- reset to 4 options after selecting a hammer/chaos before first boon has been taken + LootChoiceExt.Choices = 4 + LootChoiceExt.LastLootChoices = 4 + end end - end - - baseFunc(screen, button) + + baseFunc(screen, button) end, DontGetVorimed) -- If the player ever rerolls, reduce to 3 options ModUtil.WrapBaseFunction("DestroyBoonLootButtons", function ( baseFunc, lootData ) - baseFunc(lootData) - if config.Enabled and lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then - LootChoiceExt.Choices = 3 - LootChoiceExt.LastLootChoices = 3 - DontGetVorimed.BoonTakenFlag = true - end - -end, DontGetVorimed) \ No newline at end of file + baseFunc(lootData) + if config.Enabled and lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then + LootChoiceExt.Choices = 3 + LootChoiceExt.LastLootChoices = 3 + DontGetVorimed.BoonTakenFlag = true + end +end, DontGetVorimed) From 7af761f6d14adb367ae297bde6d41ac375cdf644 Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 21 May 2021 22:04:40 -0400 Subject: [PATCH 05/77] InteractableChaos, LootChoiceExt: minor formatting changes --- InteractableChaos/InteractableChaos.lua | 2 +- LootChoiceExt/LootChoiceExt.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/InteractableChaos/InteractableChaos.lua b/InteractableChaos/InteractableChaos.lua index 69527c3..5b1dd18 100644 --- a/InteractableChaos/InteractableChaos.lua +++ b/InteractableChaos/InteractableChaos.lua @@ -64,7 +64,7 @@ ModUtil.WrapBaseFunction("HandleLootPickup", function ( baseFunc, currentRun, lo baseFunc(currentRun, loot) -- Removing Chaos visual artifacts AdjustRadialBlurDistance({ Fraction = 0, Duration = 2 }) - AdjustRadialBlurStrength({ Fraction = 0, Duration = 2 }) + AdjustRadialBlurStrength({ Fraction = 0, Duration = 2 }) return -- If Zag does not have enough hp, push him away and show a text box saying why he cannot pick up the boon diff --git a/LootChoiceExt/LootChoiceExt.lua b/LootChoiceExt/LootChoiceExt.lua index bf4006f..e467278 100644 --- a/LootChoiceExt/LootChoiceExt.lua +++ b/LootChoiceExt/LootChoiceExt.lua @@ -162,7 +162,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData, reroll ) if TraitData[upgradeData.Name].IsDuoBoon then CreateAnimation({ Name = "BoonEntranceDuo", DestinationId = components[itemBackingKey].Id }) else - CreateAnimation({ Name = "BoonEntranceLegendary", DestinationId = components[itemBackingKey].Id }) + CreateAnimation({ Name = "BoonEntranceLegendary", DestinationId = components[itemBackingKey].Id }) end end From cc99e5e2141d961a9e06ef5fcb81702220c33884 Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 21 May 2021 22:55:28 -0400 Subject: [PATCH 06/77] Leaderboard: add presets and update default ruleset --- .gitignore | 5 ++ HadesSpeedrunningModpack/RngBalanceMenu.lua | 4 +- HadesSpeedrunningModpack/SettingsHashMenu.lua | 24 ++++----- MinibossControl/MinibossControl.lua | 53 ++++++++++++++++++- 4 files changed, 70 insertions(+), 16 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e915029 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +################################################################################ +# This .gitignore file was automatically created by Microsoft(R) Visual Studio. +################################################################################ + +/.vs diff --git a/HadesSpeedrunningModpack/RngBalanceMenu.lua b/HadesSpeedrunningModpack/RngBalanceMenu.lua index f746036..a4a1099 100644 --- a/HadesSpeedrunningModpack/RngBalanceMenu.lua +++ b/HadesSpeedrunningModpack/RngBalanceMenu.lua @@ -411,9 +411,9 @@ function HSMConfigMenu.CreateRNGMenu( screen ) local minibossDropdownOptions = {["Default"] = { event = function(dropdown) - MinibossControl.config.MinibossSetting = "HyperDelivery" + MinibossControl.config.MinibossSetting = "Leaderboard" end, - Text = "HyperDelivery", + Text = "Leaderboard", }} for k, v in pairs(MinibossControl.Presets) do table.insert(minibossDropdownOptions, diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index 3c1980d..d0055e8 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -6,7 +6,7 @@ HSMConfigMenu.RulesetSettings = { {Key = "DoorVisualIndicators.config.ShowMinibossDoorIndicator", Values = {false, true}, Default = false}, {Key = "DoorVisualIndicators.config.ShowFountainDoorIndictor", Values = {false, true}, Default = true}, - {Key = "EllosBoonSelectorMod.config.ShowPreview", Values = {false, true}, Default = true}, + {Key = "EllosBoonSelectorMod.config.ShowPreview", Values = {false, true}, Default = false}, {Key = "FixedHammers.config.Enabled", Values = {false, true}, Default = true}, {Key = "FixedHammers.config.AchillesRebalance", Values = {false, true}, Default = true}, @@ -14,7 +14,10 @@ HSMConfigMenu.RulesetSettings = { {Key = "InteractableChaos.config.Enabled", Values = {false, true}, Default = false}, - {Key = "MinibossControl.config.MinibossSetting", Values = {"Vanilla", "HyperDelivery"}, Default = "HyperDelivery"}, + {Key = "MinibossControl.config.MinibossSetting", Values = {"Vanilla", "HyperDelivery1", "HyperDelivery", "Leaderboard"}, Default = "Leaderboard"}, + + {Key = "RemoveCutscenes.config.RemoveIntro", Values = {false, true}, Default = true}, + {Key = "RemoveCutscenes.config.RemoveOutro", Values = {false, true}, Default = true}, {Key = "RoomDeterminism.config.Enabled", Values = {false, true}, Default = false}, {Key = "RoomDeterminism.config.RoomGenerationAlgorithm", Values = {"Vanilla"}, Default = "Vanilla"}, @@ -23,33 +26,28 @@ HSMConfigMenu.RulesetSettings = { {Key = "SatyrSackControl.config.MinSack", Values = {1, 2, 3, 4, 5}, Default = 2}, {Key = "SatyrSackControl.config.MaxSack", Values = {1, 2, 3, 4, 5}, Default = 2}, - {Key = "ThanatosControl.config.ThanatosSetting", Values = {"Vanilla", "Rebalanced", "Removed"}, Default = "Removed"}, + {Key = "ShowChamberNumber.config.ShowDepth", Values = {false, true}, Default = true}, - {Key = "EllosBoonSelectorMod.config.ShowPreview", Values = {false, true}, Default = true}, + {Key = "ThanatosControl.config.ThanatosSetting", Values = {"Vanilla", "Removed"}, Default = "Removed"}, } HSMConfigMenu.NonRulesetSettings = { {Key = "QuickRestart.config.Enabled", Values = {false, true}, Default = false}, - {Key = "RemoveCutscenes.config.RemoveIntro", Values = {false, true}, Default = true}, - {Key = "RemoveCutscenes.config.RemoveOutro", Values = {false, true}, Default = true}, - {Key = "ColorblindMod.config.TartarusEnabled", Values = {false, true}, Default = false}, {Key = "ColorblindMod.config.AsphodelEnabled", Values = {false, true}, Default = false}, {Key = "ColorblindMod.config.ElysiumEnabled", Values = {false, true}, Default = false}, {Key = "ColorblindMod.config.StyxEnabled", Values = {false, true}, Default = false}, - {Key = "ShowChamberNumber.config.ShowDepth", Values = {false, true}, Default = true}, - - {Key = "RtaTimer.config.DisplayTimer", Values = {false, true}, Default = true}, - {Key = "RtaTimer.config.MultiWeapon", Values = {false, true}, Default = true}, + {Key = "RtaTimer.config.DisplayTimer", Values = {false, true}, Default = false}, + {Key = "RtaTimer.config.MultiWeapon", Values = {false, true}, Default = false}, {Key = "EmoteMod.config.Enabled", Values = {false, true}, Default = false}, } HSMConfigMenu.SettingsDefaults = { - RulesetSettings = 780877, - NonRulesetSettings = 782 + RulesetSettings = 2866471, + NonRulesetSettings = 0 } -- Register Room Override diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 5c6e496..4dd9d63 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -9,7 +9,7 @@ ModUtil.RegisterMod("MinibossControl") local config = { - MinibossSetting = "HyperDelivery" + MinibossSetting = "Leaderboards" } MinibossControl.config = config @@ -38,12 +38,63 @@ MinibossControl.Presets = { -- If true, Tiny Vermin will not spawn RemoveTinyVermin = false, }, + HyperDelivery1 = { + -- Tartarus_Bombers + A_MiniBoss01 = 1, + -- Tartarus_Doomstone TODO: Handle middle management? + A_MiniBoss04 = 1, + -- Tartarus_Sneak + A_MiniBoss03 = 1, + + -- Asphodel_Barge + B_Wrapping01 = 0, + -- Asphodel_PowerCouple, + B_MiniBoss01 = 1, + -- Asphodel_Witches + B_MiniBoss02 = 2, + + -- Elysium_ButterflyBall + C_MiniBoss02 = 1, + -- Elysium_Asterius + C_MiniBoss01 = 1, + + -- If true, Tiny Vermin will not spawn + RemoveTinyVermin = true, + }, HyperDelivery = { + -- Tartarus_Bombers A_MiniBoss01 = 1, + -- Tartarus_Doomstone TODO: Handle middle management? A_MiniBoss04 = 1, + -- Tartarus_Sneak A_MiniBoss03 = 1, + -- Asphodel_Barge B_Wrapping01 = 0, + -- Asphodel_PowerCouple, + B_MiniBoss01 = 1, + -- Asphodel_Witches + B_MiniBoss02 = 2, + + -- Elysium_ButterflyBall + C_MiniBoss02 = 0, + -- Elysium_Asterius + C_MiniBoss01 = 2, + + -- If true, Tiny Vermin will not spawn + RemoveTinyVermin = true, + }, + Leaderboard = { + -- Tartarus_Bombers + A_MiniBoss01 = 1, + -- Tartarus_Doomstone TODO: Handle middle management? + A_MiniBoss04 = 1, + -- Tartarus_Sneak + A_MiniBoss03 = 1, + + -- Asphodel_Barge + B_Wrapping01 = 0, + -- Asphodel_PowerCouple, B_MiniBoss01 = 1, -- Asphodel_Witches B_MiniBoss02 = 2, From 48056adc05fd69f71e028864373aba8fe1325693 Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 21 May 2021 23:31:55 -0400 Subject: [PATCH 07/77] Rulesets: Update hashes and save settings to file properly --- HadesSpeedrunningModpack/HSMConfigMenu.lua | 10 +++++++--- HadesSpeedrunningModpack/SettingsHashMenu.lua | 7 ++++++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/HadesSpeedrunningModpack/HSMConfigMenu.lua b/HadesSpeedrunningModpack/HSMConfigMenu.lua index 1280efe..1e551d9 100644 --- a/HadesSpeedrunningModpack/HSMConfigMenu.lua +++ b/HadesSpeedrunningModpack/HSMConfigMenu.lua @@ -30,9 +30,7 @@ ModUtil.LoadOnce(function() HSMConfigMenu_SavedNonRuleset = {HashInt = hashInt} end) -ModUtil.WrapBaseFunction("ModConfigMenu__Close", function ( baseFunc, screen, button ) - baseFunc(screen, button) - +function HSMConfigMenu.SaveSettingsToGlobal() -- Save Ruleset to global local hashInt = CalculateHash(HSMConfigMenu.RulesetSettings, _G) HSMConfigMenu_SavedRuleset = {HashInt = hashInt} @@ -47,6 +45,12 @@ ModUtil.WrapBaseFunction("ModConfigMenu__Close", function ( baseFunc, screen, bu HSMConfigMenu_SavedPersonalization.ModdedWarningWarningMessage = ModdedWarning.config.WarningMessage HSMConfigMenu_SavedPersonalization.ModdedWarningWarningColor = nil HSMConfigMenu_SavedPersonalization.ModdedWarningColor = ModdedWarning.config.Color +end + +ModUtil.WrapBaseFunction("UnfreezePlayerUnit", function ( baseFunc, flag ) + DebugPrint({Text="This is in the wrapper"}) + HSMConfigMenu.SaveSettingsToGlobal() + baseFunc(flag) end, HSMConfigMenu) OnAnyLoad{function() diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index d0055e8..dc34d26 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -147,13 +147,18 @@ function HSMConfigMenu.CreateSettingsHashMenu( screen ) Font = "AlegrayaSansSCRegular", Items = { ["Default"] = {Text = "Select a Preset", event = function() end}, - {Text = "Default Ruleset", event = function() + {Text = "Multirun Leaderboard Ruleset", event = function() HSMConfigMenu.LoadSettings("RulesetSettings") local rulesetHashInt = CalculateHash(HSMConfigMenu.RulesetSettings, _G) local rulesetHash = HSMConfigMenu.ConvertIntToBase25(rulesetHashInt, 5) + HSMConfigMenu.CurrentRulesetHash = rulesetHash + for i = 1, #rulesetHash do SetAnimation({ Name = HSMConfigMenu.HashImages[rulesetHash[i]], DestinationId = screen.Components["RulesetHashImage" .. i].Id, OffsetX = 0, OffsetY = 0}) end + + HSMConfigMenu.updateRulesetHashDisplay() + HSMConfigMenu.SaveSettingsToGlobal() end}, {Text = "Any Heat Speedrun Ruleset v1.0", IsEnabled = false, event = function() end} }, From 03cf24f60384de43e5fed95043c9ab5332247516 Mon Sep 17 00:00:00 2001 From: Museus Date: Sat, 29 May 2021 14:06:20 -0400 Subject: [PATCH 08/77] MinibossControl: Fix variable names, run UpdateMaxCreations on load --- MinibossControl/MinibossControl.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 4dd9d63..957e231 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -115,7 +115,7 @@ function MinibossControl.RegisterPreset(name, preset) end -- Apply the configured miniboss settings to the game data -function updateMinibossControl() +function MinibossControl.UpdateMaxCreations() ModUtil.MapSetTable(RoomSetData, { -- [[ Tartarus Miniboss Counts ]] Tartarus = { @@ -137,10 +137,10 @@ function updateMinibossControl() -- [[ Asphodel Miniboss Counts ]] Asphodel = { B_MiniBoss01 = { - MaxCreationsThisRun = MinibossControl.Presets[config.MinibossSetting].Asphodel_PowerCouple, + MaxCreationsThisRun = MinibossControl.Presets[config.MinibossSetting].B_MiniBoss01, }, B_MiniBoss02 = { - MaxCreationsThisRun = MinibossControl.Presets[config.MinibossSetting].Asphodel_Witches, + MaxCreationsThisRun = MinibossControl.Presets[config.MinibossSetting].B_MiniBoss02, }, B_Wrapping01 = { MaxCreationsThisRun = MinibossControl.Presets[config.MinibossSetting].B_Wrapping01, @@ -188,8 +188,12 @@ ModUtil.WrapBaseFunction("ChooseNextRoomData", function( baseFunc, currentRun, a return baseFunc(currentRun, args) end, MinibossControl) +ModUtil.LoadOnce( function() + MinibossControl.UpdateMaxCreations() +end) + -- When a new run is started, make sure to apply the miniboss modifications ModUtil.WrapBaseFunction("StartNewRun", function ( baseFunc, currentRun ) - updateMinibossControl() + MinibossControl.UpdateMaxCreations() return baseFunc(currentRun) end, MinibossControl) From 32a1307984861a397ed95bf342d99e5abe0d1ecf Mon Sep 17 00:00:00 2001 From: Museus Date: Sat, 29 May 2021 14:32:58 -0400 Subject: [PATCH 09/77] QuickRestart: set conditions for illegal restarts to avoid bugs, print overhead --- QuickRestart/QuickRestart.lua | 41 ++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index beb1806..098bf03 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -5,50 +5,75 @@ local config = { } QuickRestart.config = config +function QuickRestart.CanReset() + -- QuickRestart must be Enabled + if not config.Enabled then return false end + + -- Zag must not be in the House + if ModUtil.PathGet("CurrentDeathAreaRoom") then return false end + + -- Zag must not be frozen + if not IsEmpty( CurrentRun.Hero.FreezeInputKeys ) then return false end + + -- Combat UI must be visible + if not (ShowingCombatUI or false) then return false end + + return true +end + +function QuickRestart.ResetRun(triggerArgs) + if not QuickRestart.CanReset() then + ModUtil.Hades.PrintOverhead("Can't reset now!", 2) + return + end + + KillHero( CurrentRun.Hero, triggerArgs ) +end + OnControlPressed{ "Assist", function(triggerArgs) - if config.Enabled and not ModUtil.PathGet("CurrentDeathAreaRoom") then + if config.Enabled then if IsControlDown({ Name = "Assist" }) and IsControlDown({ Name = "Use" }) and IsControlDown({ Name = "Shout" }) and IsControlDown({ Name = "Reload" }) then - KillHero(CurrentRun.Hero, triggerArgs) + QuickRestart.ResetRun(triggerArgs) end end end} OnControlPressed{ "Use", function(triggerArgs) - if config.Enabled and not ModUtil.PathGet("CurrentDeathAreaRoom") then + if config.Enabled then if IsControlDown({ Name = "Assist" }) and IsControlDown({ Name = "Use" }) and IsControlDown({ Name = "Shout" }) and IsControlDown({ Name = "Reload" }) then - KillHero(CurrentRun.Hero, triggerArgs) + QuickRestart.ResetRun(triggerArgs) end end end} OnControlPressed{ "Shout", function(triggerArgs) - if config.Enabled and not ModUtil.PathGet("CurrentDeathAreaRoom") then + if config.Enabled then if IsControlDown({ Name = "Assist" }) and IsControlDown({ Name = "Use" }) and IsControlDown({ Name = "Shout" }) and IsControlDown({ Name = "Reload" }) then - KillHero(CurrentRun.Hero, triggerArgs) + QuickRestart.ResetRun(triggerArgs) end end end} OnControlPressed{ "Reload", function(triggerArgs) - if config.Enabled and not ModUtil.PathGet("CurrentDeathAreaRoom") then + if config.Enabled then if IsControlDown({ Name = "Assist" }) and IsControlDown({ Name = "Use" }) and IsControlDown({ Name = "Shout" }) and IsControlDown({ Name = "Reload" }) then - KillHero(CurrentRun.Hero, triggerArgs) + QuickRestart.ResetRun(triggerArgs) end end end} From 7b1e5ba93b5dc48cca4333d0c93424db6547ccbd Mon Sep 17 00:00:00 2001 From: Museus Date: Sat, 29 May 2021 15:07:08 -0400 Subject: [PATCH 10/77] BoonSelector: enable filters by default, hide rarity filter if DontGetVorimed is enabled --- .../Scripts/EllosBoonSelectorMod.lua | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua b/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua index cd3b23b..5a95e42 100644 --- a/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua +++ b/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua @@ -2,10 +2,10 @@ ModUtil.RegisterMod("EllosBoonSelectorMod") local config = { showPreview = true, - showChaosPreview = false, - showNextRoomPreview = false, - chaosFiltersActive = false, - boonRarityFiltersActive = false, + showChaosPreview = true, + showNextRoomPreview = true, + chaosFiltersActive = true, + boonRarityFiltersActive = true, } EllosBoonSelectorMod.config = config @@ -83,6 +83,10 @@ end function OpenSeedControlScreen( args ) + if DontGetVorimed then + EllosBoonSelectorMod.boonRarityFiltersActive = (config.boonRarityFiltersActive and not DontGetVorimed.config.Enabled) + end + local screen = DeepCopyTable( ScreenData.SeedControl ) screen.Components = {} local components = screen.Components @@ -210,7 +214,7 @@ function OpenSeedControlScreen( args ) components.FiltersTitle = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 475, Y = 520 }) local displayText = "Filter for a Specific God" - if EllosBoonSelectorMod.config.boonRarityFiltersActive then + if EllosBoonSelectorMod.boonRarityFiltersActive then displayText = displayText.." and Boon" end CreateTextBox({ Id = components.FiltersTitle.Id, @@ -284,7 +288,7 @@ function OpenSeedControlScreen( args ) -- Rarity filter buttons x = -250 y = 150 - if EllosBoonSelectorMod.config.boonRarityFiltersActive then + if EllosBoonSelectorMod.boonRarityFiltersActive then for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do components[priorityBoon .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.5, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) components[priorityBoon .. "Filter"].OnPressedFunctionName = "CycleRarityFilter" @@ -479,7 +483,7 @@ function DoesRewardMatchFilters(roomReward) return false end - -- Rarities much match all active rarity filters + -- Rarities much match all active rarity filters for priorityBoon, rarityFilter in pairs(EllosBoonSelectorMod.RarityFilter) do local thisFilterPassed = false; for index, boonOption in ipairs(roomReward.BoonData.Options) do @@ -559,7 +563,7 @@ function ClearFilters ( screen, button ) ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.BoonPatchCommon }) end end - if EllosBoonSelectorMod.config.boonRarityFiltersActive then + if EllosBoonSelectorMod.boonRarityFiltersActive then for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do ModifyTextBox({ Id = screen.Components[priorityBoon .. "Filter"].Id, Color = Color.BoonPatchCommon }) end @@ -967,7 +971,7 @@ function PredictHammerOptionsForWeapon( weapon, aspectIndex, seedForPrediction, return hammerOptions end -function PriorityBoonType(boon) +function PriorityBoonType(boon) for _, boonType in pairs(EllosBoonSelectorMod.PriorityBoons) do if string.find(boon, boonType) then return boonType @@ -1004,7 +1008,7 @@ function PredictStartingGodBoonOptions( god, seedForPrediction, currentSeed ) boonRewards[i] = god..boonReward end end - + local excluded = RemoveRandomValue(boonRewards) boonRewards = CollapseTableOrdered(boonRewards) From d97bb0e2d1599dca84274794fb7693c7346e9406 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 30 May 2021 12:53:56 -0400 Subject: [PATCH 11/77] MinibossControl: fix typo in config --- MinibossControl/MinibossControl.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 957e231..48aabfd 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -9,7 +9,7 @@ ModUtil.RegisterMod("MinibossControl") local config = { - MinibossSetting = "Leaderboards" + MinibossSetting = "Leaderboard" } MinibossControl.config = config From 68ed751c8dd01697d94c10df3f566107dddd5dc5 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 30 May 2021 13:50:27 -0400 Subject: [PATCH 12/77] EmoteMod: disable import for now --- EmoteMod/modfile.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/EmoteMod/modfile.txt b/EmoteMod/modfile.txt index 0a1e26a..003ff68 100644 --- a/EmoteMod/modfile.txt +++ b/EmoteMod/modfile.txt @@ -1 +1,3 @@ -Import "EmoteMod.lua" +:: Disabled until functional and/or the following issue is resolved +:: https://github.com/ellomenop/HadesSpeedrunningModPack/issues/37 +:: Import "EmoteMod.lua" From 56af131214db46c180976711135d0911ba2ae7c1 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 30 May 2021 13:06:43 -0400 Subject: [PATCH 13/77] Release version 1.0.0-rc.2 --- CHANGELOG.md | 55 +++++++++++++++++++ .../HadesSpeedrunningModpack.lua | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..69c958e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,55 @@ + +# Change Log +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](http://keepachangelog.com/), and will follow [Semantic Versioning](https://semver.org/). + +## [1.0.0-rc.2] - 2021-05-30 + +### Changed + +- Quick Restart (insta-kill) can only be activated if + - Zagreus is not in the house + - Zagreus is not frozen (cutscenes, boon pickups, room endings) + - Combat UI is visible + + This is to prevent a variety of bugs, from duplicated boons to crashes. [#39](https://github.com/ellomenop/HadesSpeedrunningModPack/pull/39) + +- BoonSelector now has all filters enabled by default, and the rarity filter will be disabled automatically if "DontGetVorimed" is enabled. [#45](https://github.com/ellomenop/HadesSpeedrunningModPack/pull/45) + +- EmoteMod has been disabled due to an unknown issue with MacOS [#48](https://github.com/ellomenop/HadesSpeedrunningModPack/pull/48) + +### Fixed + +- MinibossControl now forces the correct amount of miniboss doors in Asphodel. [#38](https://github.com/ellomenop/HadesSpeedrunningModPack/pull/38) + +- MinibossControl now enforces changes on save reload. [#38](https://github.com/ellomenop/HadesSpeedrunningModPack/pull/38) + +## [1.0.0-rc.1] - 2021-05-27 + +Initial "Beta" release of the modpack. This includes: + +- Colorblind +- DontGetVorimed +- DoorVisualIndicators +- StartingBoonSelector +- EmoteMod +- ErumiUILib +- FixedHammers +- HadesSpeedrunningModpack +- InteractableChaos +- LootChoiceExt +- MinibossControl +- ModConfigMenu +- ModUtil +- ModdedWarning +- PrintUtil +- QuickRestart +- RemoveCutscenes +- RoomDeterminism +- RTATimer +- SatyrSackControl +- ShowChamberNumber +- ThanatosControl + +as well as a ruleset preset for "Multiweapon Leaderboard" runs. \ No newline at end of file diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index c9ba011..f26e414 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v0.3" + Version = "v1.0.0-rc.2" } HadesSpeedrunningModpack.config = config From 7ce1c0e97c773c4521e82146486f518dc7e10df8 Mon Sep 17 00:00:00 2001 From: Museus <69139347+Museus@users.noreply.github.com> Date: Fri, 4 Jun 2021 12:29:24 -0400 Subject: [PATCH 14/77] Bump version to 1.0.0 No changes are required for leaderboards to be created, so just bumping version to v1.0.0. Still plenty of bugfixes to be made in the future, but nothing board-resetting as of now --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index f26e414..35eeb98 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.0.0-rc.2" + Version = "v1.0.0" } HadesSpeedrunningModpack.config = config From 0ecea31741abdad1579ebd5cd0ca3f0c881d34e1 Mon Sep 17 00:00:00 2001 From: John Redding Date: Fri, 2 Jul 2021 12:56:43 -0400 Subject: [PATCH 15/77] QuickRestart: if RtaTimer exists, restart it --- QuickRestart/QuickRestart.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 098bf03..83e9806 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -27,6 +27,10 @@ function QuickRestart.ResetRun(triggerArgs) return end + if RtaTimer then + RtaTimer__ResetRtaTimer() + end + KillHero( CurrentRun.Hero, triggerArgs ) end From b78bd974d18495977ea43ccd728dff3e6bc5462a Mon Sep 17 00:00:00 2001 From: Caleb Date: Tue, 6 Jul 2021 20:05:56 -0400 Subject: [PATCH 16/77] Removed FixedHammers, added RunStartControl, updated ModUtil --- .gitmodules | 6 + Colorblind/colorblind.lua | 2 +- FixedHammers/FixedHammers.lua | 96 - FixedHammers/LICENSE | 21 - FixedHammers/modfile.txt | 1 - HadesSpeedrunningModpack/RngBalanceMenu.lua | 95 +- HadesSpeedrunningModpack/SettingsHashMenu.lua | 8 +- ModUtil/ModUtil.lua | 1581 ----------------- ModUtil/ModUtilHades.lua | 402 ----- ModUtil/modfile.txt | 5 - RunStartControl | 1 + sgg-mod-modutil | 1 + 12 files changed, 21 insertions(+), 2198 deletions(-) create mode 100644 .gitmodules delete mode 100644 FixedHammers/FixedHammers.lua delete mode 100644 FixedHammers/LICENSE delete mode 100644 FixedHammers/modfile.txt delete mode 100644 ModUtil/ModUtil.lua delete mode 100644 ModUtil/ModUtilHades.lua delete mode 100644 ModUtil/modfile.txt create mode 160000 RunStartControl create mode 160000 sgg-mod-modutil diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2911fe7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "RunStartControl"] + path = RunStartControl + url = https://github.com/Museus/RunStartControl.git +[submodule "sgg-mod-modutil"] + path = sgg-mod-modutil + url = https://github.com/SGG-Modding/sgg-mod-modutil.git diff --git a/Colorblind/colorblind.lua b/Colorblind/colorblind.lua index 3a52f0a..4d07906 100644 --- a/Colorblind/colorblind.lua +++ b/Colorblind/colorblind.lua @@ -131,7 +131,7 @@ ModUtil.BaseOverride("DoEnemyHealthBufferDeplete", function ( enemy ) end, ColorblindMod) OnAnyLoad{function () - local biome = ModUtil.SafeGet(_G, ModUtil.PathArray("CurrentRun.CurrentRoom.RoomSetName")) + local biome = ModUtil.SafeGet(_G, ModUtil.PathToIndexArray("CurrentRun.CurrentRoom.RoomSetName")) while ColorblindMod.config[(biome or "nil") .. "Enabled"] do local ammoIds = GetIdsByType({ Name = "AmmoPack" }) if ammoIds ~= nil then diff --git a/FixedHammers/FixedHammers.lua b/FixedHammers/FixedHammers.lua deleted file mode 100644 index 5dce820..0000000 --- a/FixedHammers/FixedHammers.lua +++ /dev/null @@ -1,96 +0,0 @@ -ModUtil.RegisterMod("FixedHammers") - -local config = { - ModName = "Fixed Hammers", - Enabled = true, - AchillesRebalance = true, - NemesisRebalance = true, -} -FixedHammers.config = config - -local function GetEligibleHammers() - - local loot = DeepCopyTable( LootData["WeaponUpgrade"] ) - loot.RarityChances = {} - loot.ForceCommon = true - return GetEligibleUpgrades({}, loot, LootData["WeaponUpgrade"]) -end - -ModUtil.WrapBaseFunction("StartNewRun", function(baseFunc, ...) - -- initialize everything else first - local run = baseFunc(...) - - -- reset the mod - CurrentRun.Hammers = {} - - -- determine eligible hammers (for the current weapon) - local eligibleHammers = GetEligibleHammers() - - -- shuffle the hammers, creating two permutations - CurrentRun.Hammers[1] = FYShuffle(eligibleHammers) - CurrentRun.Hammers[2] = FYShuffle(eligibleHammers) - - if config.AchillesRebalance and HeroHasTrait( "SpearTeleportTrait" ) then - RemoveValueAndCollapse(CurrentRun.Hammers[1], {Type = "Trait", ItemName = "SpearAutoAttack"}) - table.insert(CurrentRun.Hammers[1], 1, {Type = "Trait", ItemName = "SpearAutoAttack"}) - end - - if config.NemesisRebalance and HeroHasTrait( "SwordCriticalParryTrait" ) then - RemoveValueAndCollapse(CurrentRun.Hammers[1], {Type = "Trait", ItemName = "SwordDoubleDashAttackTrait"}) - table.insert(CurrentRun.Hammers[1], 1, {Type = "Trait", ItemName = "SwordDoubleDashAttackTrait"}) - end - - return run -end, FixedHammers) - -ModUtil.WrapBaseFunction("SetTraitsOnLoot", function(baseFunc, lootData, args) - if lootData.Name == "WeaponUpgrade" and config.Enabled and CurrentRun.Hammers then - local previousHammers = CurrentRun.PreviousHammers or 0 - local hammersInOrder = CurrentRun.Hammers[previousHammers + 1] - - if hammersInOrder == nil then - DebugPrint({Text = "Something went wrong with Fixed Hammers and there are no preset hammers"}) - FixedHammers.Busted = lootData - return baseFunc(lootData, args) - end - - local eligibleHammers = GetEligibleHammers() - - for idx, hammer in pairs(hammersInOrder) do - DebugPrint({Text = idx .. ": " .. hammer.ItemName}) - end - for _, hammer in pairs(eligibleHammers) do - DebugPrint({Text = "-- : " .. hammer.ItemName}) - end - local upgradeOptions = {} - local upgradesSelected = 0 - - -- pick the first three hammers from the fixed permutation - for _, currentHammer in pairs(hammersInOrder) do - for _, eligibleHammer in pairs(eligibleHammers) do - if currentHammer.ItemName == eligibleHammer.ItemName then - eligibleHammer.Rarity = "Common" - table.insert(upgradeOptions, eligibleHammer) - upgradesSelected = upgradesSelected + 1 - break - end - end - if upgradesSelected == 3 then break end - end - baseFunc(lootData, args) - lootData.UpgradeOptions = upgradeOptions - FixedHammers.After = lootData - return - else - return baseFunc(lootData, args) - end -end, FixedHammers) - -ModUtil.WrapBaseFunction("AddTraitToHero", function(baseFunc, trait) - -- Track previous hammers manually because sometimes the SetTraitsOnLoot gets called multiple times - -- both before AND after the LootHistory gets incremented. First room hammer does not do this. - if ModUtil.SafeGet(trait, ModUtil.PathArray("TraitData.Frame")) == "Hammer" then - CurrentRun.PreviousHammers = (CurrentRun.PreviousHammers or 0) + 1 - end - baseFunc(trait) -end, FixedHammers) diff --git a/FixedHammers/LICENSE b/FixedHammers/LICENSE deleted file mode 100644 index 4909372..0000000 --- a/FixedHammers/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 paradigmsort - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/FixedHammers/modfile.txt b/FixedHammers/modfile.txt deleted file mode 100644 index d8a5f6e..0000000 --- a/FixedHammers/modfile.txt +++ /dev/null @@ -1 +0,0 @@ -Import "FixedHammers.lua" diff --git a/HadesSpeedrunningModpack/RngBalanceMenu.lua b/HadesSpeedrunningModpack/RngBalanceMenu.lua index a4a1099..0e373e1 100644 --- a/HadesSpeedrunningModpack/RngBalanceMenu.lua +++ b/HadesSpeedrunningModpack/RngBalanceMenu.lua @@ -1,6 +1,6 @@ HSMConfigMenu__UpdateGenericConfigCheckbox = function(screen, button) local radioButtonValue = "RadioButton_Unselected" - if ModUtil.SafeGet(_G, ModUtil.PathArray(button.Config)) then + if ModUtil.SafeGet(_G, ModUtil.PathToIndexArray(button.Config)) then radioButtonValue = "RadioButton_Selected" end SetThingProperty({ @@ -27,7 +27,7 @@ function HSMConfigMenu__ToggleGenericConfigCheckBox(screen, button, overrideValu if button.Enabled == false then return end - local configPathArray = ModUtil.PathArray(button.Config) + local configPathArray = ModUtil.PathToIndexArray(button.Config) local configValue = ModUtil.SafeGet(_G, configPathArray) if configValue ~= nil then if overrideValue ~= nil then @@ -616,7 +616,7 @@ function HSMConfigMenu.CreateRNGMenu( screen ) itemLocationY = itemLocationY + itemSpacingY -- Hammers - screen.Components["HammerDeterminismTextBox"] = CreateScreenComponent({ + screen.Components["HammerControlTextBox"] = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1, X = itemLocationX, @@ -624,8 +624,8 @@ function HSMConfigMenu.CreateRNGMenu( screen ) Group = "Combat_Menu" }) CreateTextBox({ - Id = screen.Components["HammerDeterminismTextBox"].Id, - Text = "Deterministic Hammers by Seed: ", + Id = screen.Components["HammerControlTextBox"].Id, + Text = "Set Hammers by Aspect: ", Color = Color.BoonPatchCommon, FontSize = 16, OffsetX = 0, OffsetY = 0, @@ -633,95 +633,18 @@ function HSMConfigMenu.CreateRNGMenu( screen ) ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, Justification = "Left" }) - screen.Components["HammerDeterminismCheckBox"] = CreateScreenComponent({ + screen.Components["HammerControlCheckBox"] = CreateScreenComponent({ Name = "RadioButton", Scale = 1, X = itemLocationX + itemSpacingX, Y = itemLocationY, Group = "CombatMenu" }) - screen.Components["HammerDeterminismCheckBox"].Config = "FixedHammers.config.Enabled" - screen.Components["HammerDeterminismCheckBox"].Children = {{Textbox = "AchillesRebalanceTextBox", Checkbox = "AchillesRebalanceCheckBox"}, {Textbox = "NemesisRebalanceTextBox", Checkbox = "NemesisRebalanceCheckBox"}} - screen.Components["HammerDeterminismCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" + screen.Components["HammerControlCheckBox"].Config = "RunStartControl.config.Enabled" + screen.Components["HammerControlCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" itemLocationY = itemLocationY + itemSpacingY - screen.Components["AchillesRebalanceTextBox"] = CreateScreenComponent({ - Name = "BlankObstacle", - Scale = 1, - X = itemLocationX, - Y = itemLocationY, - Group = "Combat_Menu" - }) - CreateTextBox({ - Id = screen.Components["AchillesRebalanceTextBox"].Id, - Text = " - Achilles Hammer Rebalance: ", - Color = Color.BoonPatchCommon, - FontSize = 16, - OffsetX = 0, OffsetY = 0, - Font = "AlegrayaSansSCRegular", - ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, - Justification = "Left" - }) - screen.Components["AchillesRebalanceCheckBox"] = CreateScreenComponent({ - Name = "RadioButton", - Scale = 1, - X = itemLocationX + itemSpacingX, - Y = itemLocationY, - Group = "CombatMenu" - }) - screen.Components["AchillesRebalanceCheckBox"].Config = "FixedHammers.config.AchillesRebalance" - screen.Components["AchillesRebalanceCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" - CreateTextBox({ - Id = screen.Components["AchillesRebalanceTextBox"].Id, - Text = "(First Hammer always includes Flurry Jab) ", - Color = Color.BoonPatchCommon, - FontSize = 10, - OffsetX = 38, OffsetY = 22, - Font = "AlegrayaSansSCRegular", - ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, - Justification = "Left", - }) - itemLocationY = itemLocationY + itemSpacingY - - screen.Components["NemesisRebalanceTextBox"] = CreateScreenComponent({ - Name = "BlankObstacle", - Scale = 1, - X = itemLocationX, - Y = itemLocationY, - Group = "Combat_Menu" - }) - CreateTextBox({ - Id = screen.Components["NemesisRebalanceTextBox"].Id, - Text = " - Nemesis Hammer Rebalance: ", - Color = Color.BoonPatchCommon, - FontSize = 16, - OffsetX = 0, OffsetY = 0, - Font = "AlegrayaSansSCRegular", - ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, - Justification = "Left" - }) - screen.Components["NemesisRebalanceCheckBox"] = CreateScreenComponent({ - Name = "RadioButton", - Scale = 1, - X = itemLocationX + itemSpacingX, - Y = itemLocationY, - Group = "CombatMenu" - }) - screen.Components["NemesisRebalanceCheckBox"].Config = "FixedHammers.config.NemesisRebalance" - screen.Components["NemesisRebalanceCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" - CreateTextBox({ - Id = screen.Components["NemesisRebalanceTextBox"].Id, - Text = "(First Hammer always includes Double Edge) ", - Color = Color.BoonPatchCommon, - FontSize = 10, - OffsetX = 38, OffsetY = 22, - Font = "AlegrayaSansSCRegular", - ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, - Justification = "Left", - }) - HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["HammerDeterminismCheckBox"]) - HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["AchillesRebalanceCheckBox"]) - HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["NemesisRebalanceCheckBox"]) + HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["HammerControlCheckBox"]) itemLocationY = itemLocationY + itemSpacingY end diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index dc34d26..44a32ad 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -8,9 +8,7 @@ HSMConfigMenu.RulesetSettings = { {Key = "EllosBoonSelectorMod.config.ShowPreview", Values = {false, true}, Default = false}, - {Key = "FixedHammers.config.Enabled", Values = {false, true}, Default = true}, - {Key = "FixedHammers.config.AchillesRebalance", Values = {false, true}, Default = true}, - {Key = "FixedHammers.config.NemesisRebalance", Values = {false, true}, Default = true}, + {Key = "RunStartControl.config.Enabled", Values = {false, true}, Default = true}, {Key = "InteractableChaos.config.Enabled", Values = {false, true}, Default = false}, @@ -216,7 +214,7 @@ end function GetConfigBits(parentTable, config) for i, val in ipairs(config.Values) do - local currentConfigVal = ModUtil.SafeGet(parentTable, ModUtil.PathArray(config.Key)) + local currentConfigVal = ModUtil.SafeGet(parentTable, ModUtil.PathToIndexArray(config.Key)) if currentConfigVal == nil then currentConfigVal = config.Default -- One liner will not work for boolean values as it will be interpreted as a logical or end @@ -235,7 +233,7 @@ function SetConfigBits(parentTable, config, configBits) setValue = config.Default -- One liner will not work for boolean values as it will be interpreted as a logical or end - ModUtil.SafeSet(parentTable, ModUtil.PathArray(config.Key), setValue) + ModUtil.SafeSet(parentTable, ModUtil.PathToIndexArray(config.Key), setValue) end function CalculateHash(settings, parentTable) diff --git a/ModUtil/ModUtil.lua b/ModUtil/ModUtil.lua deleted file mode 100644 index 9eb593e..0000000 --- a/ModUtil/ModUtil.lua +++ /dev/null @@ -1,1581 +0,0 @@ ---[[ -Mod: Mod Utility -Author: MagicGonads - - Library to allow mods to be more compatible with eachother and expand capabilities. - Use the mod importer to import this mod to ensure it is loaded in the right position. - -]] - --- BETA VERSION - -local Config = { - AutoCollapse = true, -} - -ModUtil = { - Config = Config, - ModName = "ModUtil", - WrapCallbacks = {}, - Mods = {}, - Overrides = {}, - PerFunctionEnv = {}, - Anchors = {Menu={},CloseFuncs={}}, - Context = {}, - Metatables = {}, - FuncsToLoad = {}, - MarkedForCollapse = {}, -} -SaveIgnores["ModUtil"]=true - --- Management - ---[[ - Create a namespace that can be used for the mod's functions - and data, and ensure that it doesn't end up in save files. - - modName - the name of the mod - parent - the parent mod, or nil if this mod stands alone -]] -function ModUtil.RegisterMod( modName, parent ) - if not parent then - parent = _G - SaveIgnores[modName]=true - end - if not parent[modName] then - parent[modName] = {} - table.insert( ModUtil.Mods, parent[modName] ) - end - parent[modName].ModName = modName - parent[modName].ModParent = parent - return parent[modName] -end - --- internal -function ModUtil.LoadFuncs( triggerArgs ) - for _,v in pairs(ModUtil.FuncsToLoad) do - v(triggerArgs) - end - ModUtil.FuncsToLoad = {} -end -OnAnyLoad{ModUtil.LoadFuncs} - ---[[ - Run the provided function once on the next in-game load. - - triggerFunction - the function to run -]] -function ModUtil.LoadOnce( triggerFunction ) - table.insert( ModUtil.FuncsToLoad, triggerFunction ) -end - ---[[ - Tell each screen anchor that they have been forced closed by the game -]] -function ModUtil.ForceClosed( triggerArgs ) - for _,v in pairs( ModUtil.Anchors.CloseFuncs ) do - v( nil, nil, triggerArgs ) - end - ModUtil.Anchors.CloseFuncs = {} - ModUtil.Anchors.Menu = {} -end -OnAnyLoad{ModUtil.ForceClosed} - --- Data Misc - -function ModUtil.ValueString( o ) - if type( o ) == 'string' then - return '"'..o..'"' - end - return tostring( o ) -end - -function ModUtil.KeyString( o ) - if type( o ) == 'number' then o = o..'.' end - return tostring( o ) -end - -function ModUtil.TableKeysString( o ) - if type( o ) == 'table' then - local first = true - local s = '' - for k,_ in pairs( o ) do - if not first then s = s .. ', ' else first = false end - s = s .. ModUtil.KeyString( k ) - end - return s - end -end - -function ModUtil.ToString( o ) - --https://stackoverflow.com/a/27028488 - if type( o ) == 'table' then - local first = true - local s = '{' - for k,v in pairs( o ) do - if not first then s = s .. ', ' else first = false end - s = s .. ModUtil.KeyString( k ) ..' = ' .. ModUtil.ToString( v ) - end - return s .. '}' - else - return ModUtil.ValueString( o ) - end -end - -function ModUtil.ToStringLimited( o, n, m, t, j) - if type( o ) == 'table' then - local first = true - local s = '' - local i = 0 - local go = true - if not j then j = 1 end - if not m then m = 0 end - if not t then t = {} end - for k,v in pairs( o ) do - if t[j] then go = type( v ) == t[j] or t[j] == true end - if go then - i = i + 1 - if n then if i > n+m then return s end end - if m < i then - if not first then s = s .. ', ' else first = false end - if type( v ) == "table" and t[j+1] then - s = s .. ModUtil.KeyString( k ) ..' = ('..ModUtil.ToStringLimited( v, n, m, t, j+1 )..')' - else - s = s .. ModUtil.KeyString( k ) ..' = '..ModUtil.ValueString( v ) - end - end - end - end - return s - else - return ModUtil.ValueString( o ) - end -end - -function ModUtil.ChunkText( text, chunkSize, maxChunks ) - local chunks = {""} - local cs = 0 - local ncs = 1 - for chr in text:gmatch( "." ) do - cs = cs + 1 - if cs > chunkSize or chr == "\n" then - ncs = ncs + 1 - if maxChunks then - if ncs > maxChunks then - return chunks - end - end - chunks[ncs] = "" - cs = 0 - end - if chr ~= "\n" then - chunks[ncs] = chunks[ncs] .. chr - end - end - return chunks -end - -function ModUtil.InvertTable( tableArg ) - local inverseTable = {} - for key,value in ipairs( tableArg ) do - inverseTable[value] = key - end - return inverseTable -end - -function ModUtil.IsUnKeyed( tableArg ) - local lk = 0 - for k, _ in pairs( tableArg ) do - if type( k ) ~= "number" then - return false - end - if lk+1 ~= k then - return false - end - lk = k - end - return true -end - -function ModUtil.AutoIsUnKeyed( tableArg ) - if ModUtil.Config.AutoCollapse then - if not ModUtil.MarkedForCollapse[ tableArg ] then - return ModUtil.IsUnKeyed( tableArg ) - else - return false - end - end - return false -end - --- Data Manipulation - ---[[ - Safely create a new empty table at Table.key. - - Table - the table to modify - key - the key at which to store the new empty table -]] -function ModUtil.NewTable( tableArg, key ) - if type( tableArg ) ~= "table" then return end - if tableArg[key] == nil then - tableArg[key] = {} - end -end - ---[[ - Safely retrieve the a value from deep inside a table, given - an array of indices into the table. - - For example, if indexArray is ["a", 1, "c"], then - Table["a"][1]["c"] is returned. If any of Table["a"], - Table["a"][1], or Table["a"][1]["c"] are nil, then nil - is returned instead. - - Table - the table to retrieve from - indexArray - the list of indices -]] -function ModUtil.SafeGet( baseTable, indexArray ) - local node = baseTable - for _, k in ipairs( indexArray ) do - if type( node ) ~= "table" then - return nil - end - node = node[k] - end - return node -end - ---[[ - Safely set a value deep inside a table, given an array of - indices into the table, and creating any necessary tables - along the way. - - For example, if indexArray is ["a", 1, "c"], then - Table["a"][1]["c"] = Value once this function returns. - If any of Table["a"] or Table["a"][1] does not exist, they - are created. - - baseTable - the table to set the value in - indexArray - the list of indices - value - the value to add -]] -function ModUtil.SafeSet( baseTable, indexArray, value ) - if next( indexArray ) == nil then - return false -- can't set the input argument - end - local n = #indexArray - local node = baseTable - for i = 1, n-1 do - local k = indexArray[i] - ModUtil.NewTable( node, k ) - node = node[k] - end - if ( node[indexArray[n]] == nil ) ~= ( value == nil ) then - if ModUtil.AutoIsUnKeyed( baseTable ) then - ModUtil.MarkForCollapse( node ) - end - end - node[indexArray[n]] = value - return true -end - ---[[ - Set all the values in inTable corresponding to keys - in nilTable to nil. - - For example, if inTable is - { - Foo = 5, - Bar = 6, - Baz = { - InnerFoo = 5, - InnerBar = 6 - } - } - - and nilTable is - { - Foo = true, - Baz = { - InnerBar = true - } - } - - then the result will be - { - Foo = nil - Bar = 6, - Baz = { - InnerFoo = 5, - InnerBar = nil - } - } -]] -function ModUtil.MapNilTable( inTable, nilTable ) - local unkeyed = ModUtil.AutoIsUnKeyed( inTable ) - for nilKey, nilVal in pairs( nilTable ) do - local inVal = inTable[nilKey] - if type(nilVal) == "table" and type( inVal ) == "table" then - ModUtil.MapNilTable( inVal, nilVal ) - else - inTable[nilKey] = nil - if unkeyed then - ModUtil.MarkForCollapse( inTable ) - end - end - end -end - ---[[ - Set all the the values in inTable corresponding to values - in setTable to their values in setTable. - - For example, if inTable is - { - Foo = 5, - Bar = 6 - } - - and setTable is - { - Foo = 7, - Baz = { - InnerBar = 8 - } - } - - then the result will be - { - Foo = 7, - Bar = 6, - Baz = { - InnerBar = 8 - } - } -]] -function ModUtil.MapSetTable( inTable, setTable ) - local unkeyed = ModUtil.AutoIsUnKeyed( inTable ) - for setKey, setVal in pairs( setTable ) do - local inVal = inTable[setKey] - if type( setVal ) == "table" and type( inVal ) == "table" then - ModUtil.MapSetTable( inVal, setVal ) - else - inTable[setKey] = setVal - if type( setKey ) ~= "number" and unkeyed then - ModUtil.MarkForCollapse( inTable ) - end - end - end -end - -local function CollapseTable( tableArg ) - -- from UtilityScripts.lua - if tableArg == nil then - return - end - - local collapsedTable = {} - local index = 1 - for _, v in pairs( tableArg ) do - collapsedTable[index] = v - index = index + 1 - end - - return collapsedTable - -end - -function ModUtil.CollapseMarked() - for tbl,_ in pairs( ModUtil.MarkedForCollapse ) do - local ctbl = CollapseTable( tbl ) - for k,_ in pairs(tbl) do - tbl[k]=nil - end - for k,v in pairs(ctbl) do - tbl[k] = v - end - end - ModUtil.MarkedForCollapse = {} -end -OnAnyLoad{ModUtil.CollapseMarked} - -function ModUtil.MarkForCollapse( table ) - ModUtil.MarkedForCollapse[table] = true -end - --- Path Manipulation - ---[[ - Concatenates two index arrays, in order. - - a, b - the index arrays -]] -function ModUtil.JoinIndexArrays( a, b ) - local c = {} - local j = 0 - for i,v in ipairs(a) do - c[i] = v - j = i - end - for i,v in ipairs(b) do - c[i+j] = v - end - return c -end - ---[[ - Create an index array from the provided Path. - - The returned array can be used as an argument to the safe table - manipulation functions, such as ModUtil.SafeSet and ModUtil.SafeGet. - - path - a dot-separated string that represents a path into a table -]] -function ModUtil.PathArray( path ) - local s = "" - local i = {} - for c in path:gmatch( "." ) do - if c ~= "." then - s = s .. c - else - table.insert( i, s ) - s = "" - end - end - if #s > 0 then - table.insert( i,s ) - end - return i -end - ---[[ - Safely get a value from a Path. - - For example, ModUtil.PathGet("a.b.c") returns a.b.c. - If either a or a.b is nil, nil is returned instead. - - path - the path to get the value - base - (optional) The table to retreive the value from. - If not provided, retreive a global. -]] -function ModUtil.PathGet( path, base ) - return ModUtil.SafeGet( base or _G, ModUtil.PathArray( path ) ) -end - ---[[ - Safely get set a value to a Path. - - For example, ModUtil.PathSet("a.b.c", 1) sets a.b.c = 1. - If either a or a.b is nil, they are created. - - path - the path to get the value - base - (optional) The table to retreive the value from. - If not provided, retreive a global. -]] -function ModUtil.PathSet( path, value, base ) - return ModUtil.SafeSet( base or _G, ModUtil.PathArray( path ), value ) -end - -function ModUtil.PathNilTable( path, nilTable, base ) - return ModUtil.MapNilTable( ModUtil.PathGet( path, base ), nilTable ) -end - -function ModUtil.PathSetTable( path, setTable, base ) - return ModUtil.MapSetTable( ModUtil.PathGet( path, base ), setTable ) -end - --- Metaprogramming Shenanigans - -function getfenv( fn ) - local i = 1 - while true do - local name, val = debug.getupvalue(fn, i) - if name == "_ENV" then - return val - elseif not name then - break - end - i = i + 1 - end -end - ---[[ - Replace a function's _ENV with a new environment table. - - Global variable lookups (including function calls) in that function - will use the new environment table rather than the normal one. - - This is useful for function-specific overrides. The new environment - table should generally have _G as its __index, so that any globals - other than those being overridden can still be read. -]] -function setfenv( fn, env ) - local i = 1 - while true do - local name = debug.getupvalue( fn, i ) - if name == "_ENV" then - debug.upvaluejoin( fn, i, (function() - return env - end), 1 ) - break - elseif not name then - break - end - i = i + 1 - end - return fn -end - -rawnext = next -function next(t,k) - local m = getmetatable(t) - local n = m and m.__next or rawnext - return n(t,k) -end - - - -ModUtil.Metatables.LocalLevel = { - __index = function( self, idx ) - return debug.getlocal( rawget(self,"level") + 1, idx ) - end, - __newindex = function( self, idx, value ) - local level = rawget(self,"level") + 1 - local name = debug.getlocal( level, idx) - if name ~= nil then - debug.setlocal( level, idx, value ) - end - end, - __next = function( self, idx ) - if idx == nil then - idx = 0 - end - idx = idx + 1 - local name, val = debug.getlocal( rawget(self,"level") + 1, idx ) - if val ~= nil then - return idx, name, val - end - end, - __pairs = function( self ) - return getmetatable( self ).__next, self, nil - end, - __ipairs = function( self ) - return getmetatable( self ).__next, self, 0 - end -} - ---[[ - Example Use: - for i, name, value in pairs(ModUtil.LocalLevel(level)) do - -- - end -]] -function ModUtil.LocalLevel(level) - local localLevel = { level = level } - setmetatable( localLevel, ModUtil.Metatables.LocalLevel ) - return localLevel -end - -ModUtil.Metatables.LocalLevels = { - __index = function( _, level ) - return level, ModUtil.LocalLevel( level ) - end, - __next = function( _, level ) - if level == nil then - level = 0 - end - level = level + 1 - if debug.getinfo(level + 1, "f") then - return level, ModUtil.LocalLevel( level ) - end - end, - __pairs = function( self ) - return getmetatable( self ).__next, self, nil - end, - __ipairs = function( self ) - return getmetatable( self ).__next, self, 0 - end -} ---[[ - Example Use: - for level,localLevel in pairs(ModUtil.LocalLevels) do - for i, name, value in pairs(localLevel) do - -- - end - end -]] -ModUtil.LocalLevels = {} -setmetatable(ModUtil.LocalLevels, ModUtil.Metatables.LocalLevels) - -ModUtil.Metatables.LocalsInterface = { - __index = function( self, name ) - local pair = rawget(self,"index")[name] - if pair ~= nil then - local _,value = debug.getlocal( pair.level + 1, pair.i ) - return value - end - end, - __newindex = function( self, name, value ) - local pair = rawget(self,"index")[name] - if pair ~= nil then - debug.setlocal( pair.level + 1, pair.i, value ) - end - end, - __next = function( self, name ) - local pair - name, pair = next( rawget(self,"index"), name ) - if pair ~= nil then - local _, value = debug.getlocal( pair.level + 1, pair.i ) - return name, value - end - end, - __pairs = function( self ) - return getmetatable( self ).__next, self, nil - end, - __ipairs = function( self ) - return function( self, i ) - local name - i, name = next( rawget(t,"order"), i ) - pair = rawget(self,"index")[name] - if pair ~= nil then - local _, value = debug.getlocal( pair.level + 1, pair.i ) - return i, value - end - end, self, 0 - end -} - ---[[ - Interface only valid within the scope it was constructed -]] -function ModUtil.GetLocalsInterface( names ) - local lookup = {} - if names then - -- assume names is strictly either a list or a lookup table - lookup = names - if #lookup ~= 0 then - lookup = ModUtil.InvertTable(names) - end - end - - local order = {} - local index = {} - - local level,localLevel = next(ModUtil.LocalLevels, 1) - while level do - for i, name, value in pairs(localLevel) do - if (not names or lookup[name]) and index[name] == nil then - index[name] = {level = level - 1, i = i} - table.insert( order, name ) - end - end - level,localLevel = next(ModUtil.LocalLevels, level) - end - local interface = {index = index, order = order} - setmetatable(interface, ModUtil.Metatables.LocalsInterface) - - return interface, index, order -end - -ModUtil.Metatables.Locals = { - __index = function( _, name) - local level = 2 - while debug.getinfo(level, "f") do - local idx = 1 - while true do - local n, v = debug.getlocal(level, idx) - if n == name then - return v - elseif not n then - break - end - idx = idx + 1 - end - level = level + 1 - end - end, - __newindex = function( _, name, value ) - local level = 2 - while debug.getinfo(level, "f") do - local idx = 1 - while true do - local n = debug.getlocal(level, idx) - if n == name then - debug.setlocal(level, idx, value) - return - elseif not n then - break - end - idx = idx + 1 - end - level = level + 1 - end - end, - __next = function( _, name ) - if name == nil then - return debug.getlocal( 2, 1 ) - end - local level = 2 - while debug.getinfo(level, "f") do - local idx = 1 - while true do - local n = debug.getlocal( level, idx ) - if n == name then - return debug.getlocal( level, idx + 1) - elseif not n then - break - end - idx = idx + 1 - end - level = level + 1 - end - end, - __pairs = function( self ) - return getmetatable(self).__next, self, nil - end, - __ipairs = function( self ) - return function( self, i ) - local level = 2 - local j = 0 - while debug.getinfo(level, "f") do - local idx = 1 - while true do - j = j + 1 - local n, v = debug.getlocal(level, idx) - if not n then - break - elseif j > i then - return j, v - end - idx = idx + 1 - end - level = level + 1 - end - end, self, 0 - end -} - ---[[ - Access to local variables, in the current function and callers. - The most recent definition with a given name on the call stack will - be used. - - For example, if your function is called from CreateTraitRequirements, - you could access its 'local screen' as ModUtil.Locals.screen - and its 'local hasRequirement' as ModUtil.Locals.hasRequirement. -]] -ModUtil.Locals = {} -setmetatable(ModUtil.Locals, ModUtil.Metatables.Locals) - -ModUtil.Metatables.UpValues = { - __index = function( self, name ) - local _, v = debug.getupvalue( rawget(self,"func"), rawget(self,"ind")[name] ) - return v - end, - __newindex = function( self, name, value ) - debug.setupvalue( rawget(self,"func"), rawget(self,"ind")[name], value ) - end, - __next = function ( self, name ) - name, i = next( rawget(self,"ind"), name ) - if i ~= nil then - return debug.getupvalue( rawget(self,"func"), i ) - end - end, - __pairs = function( self ) - return getmetatable(self).__next, self, nil - end, - __ipairs = function( self ) - return function( self, i ) - i = i + 1 - local name,v = debug.getupvalue( rawget(self,"func"), i ) - if name ~= nil then - return i, v - end - end, self, 0 - end -} - ---[[ - Return a table representing the upvalues of a function. - - Upvalues are those variables captured by a function from it's - creation context. For example, locals defined in the same file - as the function are accessible to the function as upvalues. - - func - the function to get upvalues from -]] -function ModUtil.GetUpValues( func ) - local ind = {} - local name - local i = 1 - while true do - name = debug.getupvalue( func, i ) - if name == nil then break end - ind[name] = i - i = i + 1 - end - local ups = {func = func, ind = ind} - setmetatable(ups, ModUtil.Metatables.UpValues) - return ups, ind -end - -function ModUtil.GetBottomUpValues( baseTable, indexArray ) - local baseValue = ModUtil.SafeGet( ModUtil.Overrides[baseTable], indexArray ) - if baseValue then - baseValue = baseValue[#baseValue].Base - else - baseValue = ModUtil.SafeGet( ModUtil.WrapCallbacks[baseTable], indexArray ) - if baseValue then - baseValue = baseValue[1].func - else - baseValue = ModUtil.SafeGet( baseTable, indexArray ) - end - end - return ModUtil.GetUpValues( baseValue ) -end - ---[[ - Return a table representing the upvalues of the base function identified - by basePath (ie. ignoring all wrappers that other mods may have placed - around the function). - - basePath - the path to the function, as a string -]] -function ModUtil.GetBaseBottomUpValues( basePath ) - return ModUtil.GetBottomUpValues( _G, ModUtil.PathArray( basePath ) ) -end - --- Globalisation - -ModUtil.Metatables.GlobalisedFunc = { - __call = function(self, ...) - return ModUtil.SafeGet( rawget(self,"table"), rawget(self,"array") )(...) - end -} - -function ModUtil.GlobaliseFunc( baseTable, indexArray, key ) - local funcTable = { table = baseTable, array = indexArray } - setmetatable( funcTable, ModUtil.Metatables.GlobalisedFunc ) - _G[key] = funcTable -end - ---[[ - Sets a unique global variable equal to the value stored at Path. - - For example, the OnPressedFunctionName of a button must refer to a single key - in the globals table (_G). If you have a function defined in your module's - table that you would like to use, ie. - - function YourModName.FunctionName(...) - - then ModUtil.GlobalizePath("YourModName.FunctionName") will create a global - variable for that function, and you can then set OnPressedFunctionName to - ModUtil.JoinPath("YourModName.FunctionName"). - - path - the path to be globalised - prefixPath - (optional) if present, add this path as a prefix to the - path from the root of the table. -]] -function ModUtil.GlobaliseFuncPath( path, prefixPath ) - if prefixPath == nil then - prefixPath = "" - else - prefixPath = prefixPath .. '.' - end - ModUtil.GlobaliseFunc( _G, ModUtil.PathArray( path ), prefixPath..path ) -end - ---[[ - Makes all the functions in Table available globally, as if - ModUtil.GlobalisePath had been called on each of them. - - If you have a lot of functions you need to export for UI or - other by-name callbacks, it will be eaiser to maintain a single - call to ModUtil.GlobaliseFuncs at the bottom of your mod file, - rather than having a bunch of GlobalisePath calls that need to - be maintained. - - For example, if you have a table at YourModName.UIFunctions with - - function YourModName.UIFunctions.OnButton1(...) - function YourModName.UIFunctiosn.OnButton2(...) - - then ModUtil.GlobaliseFuncs(YourModName.UIFunctions) will create global - functions called OnButton1 and OnButton2. If you are worried about - collisions with other global functions, consider using a prefix ie. - - ModUtil.GlobaliseFuncs(YourModName.UIFunctions, "YourModNameUI") - - So that the global functions are called YourModNameUI__OnButton1 etc. - - tableArg - The table containing the functions to globalise - prefixPath - (optional) if present, add this path as a prefix to the - path from the root of the table. -]] -function ModUtil.GlobaliseFuncs( tableArg, prefixPath ) - if prefixPath == nil then - prefixPath = "" - else - prefixPath = prefixPath .. '.' - end - for k,v in pairs( tableArg ) do - if type( k ) == "string" then - if type(v) == "function" then - ModUtil.GlobaliseFunc( v, {k}, prefixPath..'.'..k ) - elseif type(v) == "table" then - ModUtil.GlobaliseFuncs( v, prefixPath..'.'..k ) - end - end - end -end - ---[[ - Globalise all the functions in your mod object. - - modObject - The mod object created by ModUtil.RegisterMod -]] -function ModUtil.GlobaliseModFuncs( modObject ) - local parent = modObject - local prefix = modObject.ModName - while parent.ModParent do - parent = parent.ModParent - if parent == _G then break end - prefix = parent.ModName .. "." .. prefix - end - ModUtil.GlobaliseFuncs( modObject, prefix ) -end - --- Function Wrapping - ---[[ - Wrap a function, so that you can insert code that runs before/after that function - whenever it's called, and modify the return value if needed. - - Generally, you should use ModUtil.WrapBaseFunction instead for a more modder-friendly - interface. - - Multiple wrappers can be applied to the same function. - - As an example, for WrapFunction(_G, ["UIFunctions", "OnButton1Pushed"], wrapper, MyModObject) - - Wrappers are stored in a structure like this: - - ModUtil.WrapCallbacks[_G].UIFunctions.OnButton1Pushed = { - {id:1, mod=MyModObject, wrap=wrapper, func=} - } - - If a second wrapper is applied via - WrapFunction(_G, ["UIFunctions", "OnButton1Pushed"], wrapperFunction2, SomeOtherMod) - then the resulting structure will be like: - - ModUtil.WrapCallbacks[_G].UIFunctions.OnButton1Pushed = { - {id:1, mod=MyModObject, wrap=wrapper, func=} - {id:2, mod=SomeOtherMod, wrap=wrapper2, func=} - } - - This allows several mods to apply wrappers to the same base function, and then: - - unwrap again later - - reapply the same wrappers to a new base function when it's overridden - - This function also updates the entry in funcTable at indexArray to be the completely - wrapped function, ie. in our example with two wrappers it would do - - UIFunctions.OnButton1Pushed = - - funcTable - the table the function is stored in (usually _G) - indexArray - the array of path elements to the function in the table - wrapFunc - the wrapping function - modObject - (optional) the mod installing the wrapper, for informational purposes -]] -function ModUtil.WrapFunction( funcTable, indexArray, wrapFunc, modObject ) - if type( wrapFunc ) ~= "function" then return end - if not funcTable then return end - local func = ModUtil.SafeGet( funcTable, indexArray ) - if type( func ) ~= "function" then return end - - ModUtil.NewTable( ModUtil.WrapCallbacks, funcTable ) - local tempTable = ModUtil.SafeGet( ModUtil.WrapCallbacks[funcTable], indexArray ) - if tempTable == nil then - tempTable = {} - ModUtil.SafeSet( ModUtil.WrapCallbacks[funcTable], indexArray, tempTable ) - end - table.insert( tempTable, { Id = #tempTable + 1, Mod = modObject, Wrap = wrapFunc, Func = func } ) - - ModUtil.SafeSet( funcTable, indexArray, function( ... ) - return wrapFunc( func, ... ) - end ) -end - ---[[ - Internal utility that reapplies the list of wrappers when the base function changes. - - For example. if the list of wrappers looks like: - ModUtil.WrapCallbacks[_G].UIFunctions.OnButton1Pushed = { - {id:1, mod=MyModObject, wrap=wrapper, func=} - {id:2, mod=SomeOtherMod, wrap=wrapper2, func=} - {id:3, mod=ModNumber3, wrap=wrapper3, func=} - } - - and the base function is modified by setting [1].func to a new value, like so: - ModUtil.WrapCallbacks[_G].UIFunctions.OnButton1Pushed = { - {id:1, mod=MyModObject, wrap=wrapper, func=} - {id:2, mod=SomeOtherMod, wrap=wrapper2, func=} - {id:3, mod=ModNumber3, wrap=wrapper3, func=} - } - - Then rewrap function will fix up eg. [2].func, [3].func so that the correct wrappers are applied - ModUtil.WrapCallbacks[_G].UIFunctions.OnButton1Pushed = { - {id:1, mod=MyModObject, wrap=wrapper, func=} - {id:2, mod=SomeOtherMod, wrap=wrapper2, func=} - {id:3, mod=ModNumber3, wrap=wrapper3, func=} - } - and also update the entry in funcTable to be the completely wrapped function, ie. - - UIFunctions.OnButton1Pushed = - - funcTable - the table the function is stored in (usually _G) - indexArray - the array of path elements to the function in the table -]] -function ModUtil.RewrapFunction( funcTable, indexArray ) - local wrapCallbacks = ModUtil.SafeGet( ModUtil.WrapCallbacks[funcTable], indexArray ) - local preFunc = nil - - for i,t in ipairs( wrapCallbacks ) do - if preFunc then - t.Func = preFunc - end - preFunc = function( ... ) - return t.Wrap( t.Func, ... ) - end - ModUtil.SafeSet( funcTable, indexArray, preFunc ) - end - -end - ---[[ - Removes the most recent wrapper from a function, and restore it to its - previous value. - - Generally, you should use ModUtil.UnwrapBaseFunction instead for a more - modder-friendly interface. - - funcTable - the table the function is stored in (usually _G) - indexArray - the array of path elements to the function in the table -]] -function ModUtil.UnwrapFunction( funcTable, indexArray ) - if not funcTable then return end - local func = ModUtil.SafeGet( funcTable, indexArray ) - if type( func ) ~= "function" then return end - - local tempTable = ModUtil.SafeGet( ModUtil.WrapCallbacks[funcTable], indexArray ) - if not tempTable then return end - local funcData = table.remove( tempTable ) -- removes the last value - if not funcData then return end - - ModUtil.SafeSet( funcTable, indexArray, funcData.Func ) - return funcData -end - - ---[[ - Wraps the function with the path given by baseFuncPath, so that you - can execute code before or after the original function is called, - or modify the return value. - - For example: - - ModUtil.WrapBaseFunction("CreateNewHero", function(baseFunc, prevRun, args) - local hero = baseFunc(prevRun, args) - hero.Health = 1 - return hero - end, YourMod) - - will cause the function CreateNewHero to be wrapped so that the hero's - health is set to 1 before the hero is returned. - - This provides better compatibility with other mods that overriding the - function, since multiple mods can wrap the same function. - - baseFuncPath - the (global) path to the function, as a string - for most SGG-provided functions, this is just the function's name - eg. "CreateRoomReward" or "SetTraitsOnLoot" - wrapFunc - the function to wrap around the base function - this function receives the base function as its first parameter. - all subsequent parameters should be the same as the base function - modObject - (optional) the object for your mod, for debug purposes -]] -function ModUtil.WrapBaseFunction( baseFuncPath, wrapFunc, modObject ) - local pathArray = ModUtil.PathArray( baseFuncPath ) - ModUtil.WrapFunction( _G, pathArray, wrapFunc, modObject ) -end - ---[[ - Internal function that reapplies all the wrappers to a function. -]] -function ModUtil.RewrapBaseFunction( baseFuncPath ) - local pathArray = ModUtil.PathArray( baseFuncPath ) - ModUtil.RewrapFunction( _G, pathArray ) -end - ---[[ - Remove the most recent wrapper from the function at baseFuncPath, - restoring it to its previous state - - Note that this does _not_ remove overrides, and it removes the most - recent wrapper regardless of which mod added it, so be careful! - - baseFuncPath - the (global) path to the function, as a string - for most SGG-provided functions, this is just the function's name - eg. "CreateRoomReward" or "SetTraitsOnLoot" -]] -function ModUtil.UnwrapBaseFunction( baseFuncPath ) - local pathArray = ModUtil.PathArray( baseFuncPath ) - ModUtil.UnwrapFunction( _G, pathArray ) -end - --- Override Management - -local function getBaseValueForWraps( baseTable, indexArray ) - local baseValue = ModUtil.SafeGet( baseTable, indexArray ) - local wrapCallbacks = nil - wrapCallbacks = ModUtil.SafeGet( ModUtil.WrapCallbacks[baseTable], indexArray ) - if wrapCallbacks then if wrapCallbacks[1] then - baseValue = wrapCallbacks[1].Func - end end - - return baseValue -end - -local function setBaseValueForWraps( baseTable, indexArray, value) - local baseValue = ModUtil.SafeGet( baseTable, indexArray ) - - if type(baseValue) ~= "function" or type(value) ~= "function" then return false end - - local wrapCallbacks = nil - wrapCallbacks = ModUtil.SafeGet( ModUtil.WrapCallbacks[baseTable], indexArray ) - if wrapCallbacks then if wrapCallbacks[1] then - baseValue = wrapCallbacks[1].Func - wrapCallbacks[1].Func = value - ModUtil.RewrapFunction( baseTable, indexArray ) - return true - end end - - return false -end - ---[[ - Override a value in baseTable. - - Generally, you should use ModUtil.BaseOverride instead for a more modder-friendly - interface. - - If the value is a function, overrides only the base function, - preserving all the wraps added with ModUtil.WrapFunction. - - The previous value is stored so that it can be restored later if desired. - For example, ModUtil.Override(_G, ["UIFunctions", "OnButton1"], overrideFunc, MyMod) - will result in a data structure like: - - ModUtil.Overrides[_G].UIFunctions.OnButton1 = { - {id=1, mod=MyMod, value=overrideFunc, base=} - } - - and subsequent overides wil be added as subsequent entries in the same table. - - baseTable - the table in which to override, usually _G (globals) - indexArray - the list of indices - value - the new value - modObject - (optional) the mod that performed the override, for debugging purposes -]] -function ModUtil.Override( baseTable, indexArray, value, modObject ) - if not baseTable then return end - - local baseValue = getBaseValueForWraps( baseTable, indexArray ) - - ModUtil.NewTable( ModUtil.Overrides, baseTable ) - local tempTable = ModUtil.SafeGet( ModUtil.Overrides[baseTable], indexArray ) - if tempTable == nil then - tempTable = {} - ModUtil.SafeSet( ModUtil.Overrides[baseTable], indexArray, tempTable ) - end - table.insert( tempTable, { Id = #tempTable + 1, Mod = modObject, Value = value, Base = baseValue } ) - - if not setBaseValueForWraps( baseTable, indexArray, value) then - ModUtil.SafeSet( baseTable, indexArray, value ) - end -end - ---[[ - Undo the most recent override performed with ModUtil.Override, restoring - the previous value. - - Generally, you should use ModUtil.BaseRestore instead for a more modder-friendly - interface. - - If the previous value is a function, the current stack of wraps for that - IndexArray will be reapplied to it. - - baseTable = the table in which to undo the override, usually _G (globals) - indexArray - the list of indices -]] -function ModUtil.Restore( baseTable, indexArray ) - if not baseTable then return end - local tempTable = ModUtil.SafeGet( ModUtil.Overrides[baseTable], indexArray ) - if not tempTable then return end - local baseData = table.remove( tempTable ) -- remove the last entry - if not baseData then return end - - if not setBaseValueForWraps( baseTable, indexArray, baseData.Base ) then - ModUtil.SafeSet( baseTable, indexArray, baseData.Base ) - end - return baseData -end - ---[[ - Override the global value at the given basePath. - - If the Value is a function, preserves the wraps - applied with ModUtil.WrapBaseFunction et. al. - - basePath - the path to override, as a string - value - the new value to store at the path - modObject - (optional) the mod performing the override, - for debug purposes -]] -function ModUtil.BaseOverride( basePath, value, modObject ) - local pathArray = ModUtil.PathArray( basePath ) - ModUtil.Override( _G, pathArray, value, modObject ) -end - ---[[ - Undo the most recent override performed with ModUtil.Override, - or ModUtil.BaseOverride, restoring the previous value. - - Use this carefully - if you are not the most recent mod to - override the given path, the results may be unexpected. - - basePath - the path to restore, as a string -]] -function ModUtil.BaseRestore( basePath ) - local pathArray = ModUtil.PathArray( basePath ) - ModUtil.Restore( _G, pathArray ) -end - ---[[ - Create a new table whose getters and setters will default to the given - baseTable, except in cases where values are setOverride into the overrideTable. - - This allows pinpoint overriding of values in the override table, while allowing - all other accesses to continue to operate as if they were operating on baseTable. - - The overrides are stored in the _Overrides subtable. For example: - - _Overrides = { - CurrentRun = { - CurrentRoom = { - _IsModUtilOverride = true, - _Value = { - Name = "RoomSimple01", - ... - } - } - } - } - - would be the result of overriding "CurrentRun.CurrentRoom" with a table represnting a room. - - Any accesses that happen above the override point (ie. reads/writes to CurrentRun) are - redirected to the base table. Any accesses that happen at or below the override point (ie. - reads / writes to CurrentRun.CurrentRoom or CurrentRun.CurrentRoom.Name) are intercepted - and apply to the overridden value instead. - - baseTable - the table to access for entries not specifically overridden -]] -local function makeOverrideTable( baseTable, overrides ) - local overrideTable = { - _IsModUtilOverrideTable = true, - _Overrides = overrides or {}, - _BaseTable = baseTable - } - setmetatable(overrideTable, { - __index = function( self, name ) - local baseResult = self._BaseTable[name] - local overridesResult = self._Overrides[name] - if overridesResult == nil then - return baseResult - elseif overridesResult._IsModUtilOverride then - return overridesResult._Value - elseif type(baseResult) == "table" then - return makeOverrideTable( baseResult, overridesResult ) - else - return makeOverrideTable( {}, overridesResult ) - end - end, - __newindex = function( self, name, value ) - local currentOverride = self._Overrides[name] - if currentOverride == nil then - self._BaseTable[name] = value - elseif currentOverride._IsModUtilOverride then - currentOverride._Value = value - else - -- There is an override that is a child of this name, but the parent is being - -- overwritten with a new table. Assign directly to the baseTable. - -- The previous override will still remain in place, so eg. with - -- Overides = { Parent = { Child = {_IsModUtilOverride = true, _Value = 6 } } - -- Table.Parent = { Child = 5 }, Table.Parent.Child will still return 6. - -- I'm not sure what alternate behavior would be more correct. - self._BaseTable[name] = value - end - end - }) - return overrideTable -end - ---[[ - Override the entry at indexArray in table with value. - - table - the table whose entry should be overridden - must come from a previous call to makeOverrideTable() - indexArray - the list of indexes - value - the value to override -]] -local function setOverride( table, indexArray, value ) - if type(table) ~= "table" or not table._IsModUtilOverrideTable then return end - ModUtil.SafeSet( table._Overrides, indexArray, {_IsModUtilOverride = true, _Value = value} ) -end - ---[[ - Remove the override entry at indexArray in table. - - No effect if the indexArray does not identify an override point. - - table - the table whose override should be removed - indexArray - the list of indexes -]] -local function removeOverride( table, indexArray ) - if type(table) ~= "table" or not table._IsModUtilOverrideTable then return end - local currentOverride = ModUtil.SafeGet( table._Overrides, indexArray) - if currentOverride ~= nil and currentOverride._IsModUtilOverride then - ModUtil.SafeSet( table._Overrides, indexArray, nil ) - end -end - ---[[ - Check whether there is an override for indexArray in table. - - Only returns true for exact matches. For example, if you override CurrentRun in table T, - hasOverride( T, ["CurrentRun", "CurrentRoom"] ) will return false. - - table - the table to check for an override - indexArray - the list of indexes -]] -local function hasOverride( table, indexArray ) - if type(table) ~= "table" or not table._IsModUtilOverrideTable then return false end - local value = ModUtil.SafeGet( table._Overrides, indexArray ) - return value ~= nil and value._IsModUtilOverride -end - ---[[ - Gets the function's local (partially-overriden) environment, or - create a new one and attach it to the function if not yet present. - - baseTable - the base table, on which to base the function's environment - indexArray - the list of indexes -]] -local function getFunctionEnv( baseTable, indexArray ) - local func = getBaseValueForWraps( baseTable, indexArray ) - if type(func) ~= "function" then return nil end - - ModUtil.NewTable( ModUtil.PerFunctionEnv, baseTable ) - local env = ModUtil.SafeGet(ModUtil.PerFunctionEnv[baseTable], indexArray) - if not env then - env = makeOverrideTable( baseTable ) - ModUtil.SafeSet( ModUtil.PerFunctionEnv[baseTable], indexArray, env ) - setfenv( func, env ) - end - return env -end - -local function DeepCopyTable( orig ) - local orig_type = type(orig) - local copy - if orig_type == 'table' then - copy = {} - -- slightly more efficient to call next directly instead of using pairs - for k,v in next, orig, nil do - copy[k] = DeepCopyTable(v) - end - else - copy = orig - end - - return copy -end - ---[[ - Overrides the value at envIndexArray within the function referred to by - indexArray in baseTable, by replacing it with value. - - Accesses to the value at envIndexArray from within other functions are - unaffected. - - Generally, you should use ModUtil.BaseOverrideWithinFunction for a more - modder-friendly interface. - - For example, after you do - ModUtil.OverrideWithinFunction( _G, ["CreateRoom"], ["CurrentRun.CurrentRoom"], ) - - 1. Any reads to CurrentRun.CurrentRoom from CreateRoom will return - 2. Any writes to CurrentRun.CurrentRoom from CreateRoom will replace , which will - with the new value, which will be returned for subsequent accesses to CurrentRun.CurrentRoom. - 3. Any writes to CurrentRun.CurrentRoom from CreateRoom will not be visible from other functions. - 4. If CreateRoom performs writes within (eg. CurrentRun.CurrentRoom.Name = "foo") - these will be visible to from any function that has access to it. If you want - full isolation, make sure you pass in an object that nobody else has a reference to, eg. by - creating one fresh or making a copy). - - baseTable - the base table for function and environment lookups (usually _G) - indexArray - the list of indices identifying the function whose environment is to be overridden - envIndexArray - the list of indices identifying the value to be overridden in the function's environment - value - the value with which to override -]] -function ModUtil.OverrideWithinFunction( baseTable, indexArray, envIndexArray, value ) - if not baseTable then return end - local env = getFunctionEnv( baseTable, indexArray ) - - if not env then return end - if hasOverride( env, envIndexArray ) then - -- we might have wraps to reapply - local overrideEnvIndexArray = DeepCopyTable( envIndexArray ) - table.insert(overrideEnvIndexArray, "_Value") - if not setBaseValueForWraps(env._Overrides, overrideEnvIndexArray, value) then - setOverride( env, envIndexArray, value ) - end - else - setOverride( env, envIndexArray, value ) - end -end - ---[[ - Remove the override at envIndexArray for the function at indexArray in baseTable, - so that reads and writes to envIndexArray have their usual effects on the base environment. - - baseTable - the base table for function and environment lookups (usually _G) - indexArray - the list of indices identifying the function whose environment is to be overridden - envIndexArray - the list of indices identifying the value to be overridden in the function's environment -]] -function ModUtil.RestoreWithinFunction( baseTable, indexArray, envIndexArray ) - if not baseTable then return end - - local env = getFunctionEnv( baseTable, indexArray ) - if not env then return end - - removeOverride( env, envIndexArray ) -end - - ---[[ - Wrap a function, so that you can insert code that runs before/after that function whenever - it's called from within a particular other function, and modify the return value if needed. - - Generally, you should use ModUtil.WrapBaseWithinFunction for a more modder-friendly interface. - - baseTable - the base table for function and environment lookups (usually _G) - indexArray - the list of indices identifying the function within with the wrap will apply - envIndexArray - the list of indices identifying the function whose calls will be wrapped - wrapFunc - the wrapping function - modObject - (optional) the mod performing the wrapping, for debug purposes -]] -function ModUtil.WrapWithinFunction( baseTable, indexArray, envIndexArray, wrapFunc, modObject ) - if type(wrapFunc) ~= "function" then return end - if not baseTable then return end - local env = getFunctionEnv( baseTable, indexArray ) - if not env then return end - - if not hasOverride( env, envIndexArray ) then - -- Resolve the entry in baseTable at call time (not now), - -- in case further wraps or overrides are applied to it. - setOverride( - env, - envIndexArray, - function(...) - local resolvedFunc = ModUtil.SafeGet( baseTable, envIndexArray ) - return resolvedFunc(...) - end) - end - - ModUtil.WrapFunction( env, envIndexArray, wrapFunc, modObject ) -end - ---[[ - Override the global value at the given path, when accessed from - within the global function at funcPath. - - If the Value is a function, preserves the wraps - applied with ModUtil.WrapBaseWithinFunction et. al. - - basePath - the path to override, as a string - envPath - the path to override, as a string - value - the new value to store at the path -]] -function ModUtil.BaseOverrideWithinFunction( funcPath, basePath, value ) - local indexArray = ModUtil.PathArray( funcPath ) - local envIndexArray = ModUtil.PathArray( basePath ) - ModUtil.OverrideWithinFunction( _G, indexArray, envIndexArray, value ) -end - ---[[ - Wraps the function with the path given by baseFuncPath, when it is called from - the function given by funcPath. - - This lets you insert code within a function, without affecting other functions - that might make similar calls. - - For example, to insert code to display a "cancel" at the point in "CreateBoonLootButtons" - where it calls IsMetaUpgradeSelected( "RerollPanelMetaUpgrade" ), do: - - ModUtil.WrapBaseWithinFunction("CreateBoonLootButtons", "IsMetaUpgradeSelected", function(baseFunc, name) - if name == "RerollPanelMetaUpgrade" and CalcNumLootChoices() == 0 then - < code to display the cancel button > - return false - else - return baseFunc(name) - end - end, YourMod) - - This provides better compatibility between mods that just wrapping IsMetaUpgradeSelected, because - you new code only executes when within CreateBoonLootButtons, so there are less chances for collisions - and side effects. - - It also provides better compatibility than using BaseOverride on "CreateBoonLootButtons", since only one - override for a function can be active at a time, but multiple wraps can be active. - - funcPath - the (global) path to the function within which the wrap will apply, as a string - for most SGG-provided functions, this is just the function's name - eg. "CreateRoomReward" or "SetTraitsOnLoot" - baseFuncPath - the (global) path to the function to wrap, as a string - for most SGG-provided functions, this is just the function's name - eg. "CreateRoomReward" or "SetTraitsOnLoot" - wrapFunc - the function to wrap around the base function - this function receives the base function as its first parameter. - all subsequent parameters should be the same as the base function - modObject - (optional) the object for your mod, for debug purposes -]] -function ModUtil.WrapBaseWithinFunction( funcPath, baseFuncPath, wrapFunc, modObject ) - local indexArray = ModUtil.PathArray( funcPath ) - local envIndexArray = ModUtil.PathArray( baseFuncPath ) - ModUtil.WrapWithinFunction( _G, indexArray, envIndexArray, wrapFunc, modObject ) -end - --- Context Managers (High WIP) - -function ModUtil.Context.Within(path, func) - local parentEnv = ModUtil.Locals.env or _G - local env = getFunctionEnv(parentEnv, path) - return env -end - -function ModUtil.Context.Meta(path, func) - --- -end diff --git a/ModUtil/ModUtilHades.lua b/ModUtil/ModUtilHades.lua deleted file mode 100644 index 9bf4679..0000000 --- a/ModUtil/ModUtilHades.lua +++ /dev/null @@ -1,402 +0,0 @@ - -ModUtil.RegisterMod("Hades",ModUtil) -ModUtil.Hades={ - PrintStackHeight = 10, - PrintStackCapacity = 80 -} - -ModUtil.Anchors.PrintOverhead = {} - --- Screen Handling - -OnAnyLoad{ function() - if ModUtil.Hades.UnfreezeLoop then return end - ModUtil.Hades.UnfreezeLoop = true - thread( function() - while ModUtil.Hades.UnfreezeLoop do - wait(15) - if ModUtil.SafeGet(CurrentRun,{'Hero','FreezeInputKeys'}) then - if (not AreScreensActive()) and (not IsInputAllowed({})) then - UnfreezePlayerUnit() - DisableShopGamepadCursor() - end - end - end - end) -end} - --- Menu Handling - -function ModUtil.Hades.CloseMenu( screen, button ) - CloseScreen(GetAllIds(screen.Components), 0.1) - ModUtil.Anchors.Menu[screen.Name] = nil - screen.KeepOpen = false - OnScreenClosed({ Flag = screen.Name }) - SetConfigOption({ Name = "FreeFormSelectWrapY", Value = false }) - SetConfigOption({ Name = "UseOcclusion", Value = true }) - if TableLength(ModUtil.Anchors.Menu) == 0 then - UnfreezePlayerUnit() - DisableShopGamepadCursor() - end - if ModUtil.Anchors.CloseFuncs[screen.Name] then - ModUtil.Anchors.CloseFuncs[screen.Name]( screen, button ) - ModUtil.Anchors.CloseFuncs[screen.Name]=nil - end -end - -function ModUtil.Hades.OpenMenu( group, closeFunc, openFunc ) - if ModUtil.Anchors.Menu[group] then - ModUtil.Hades.CloseMenu(ModUtil.Anchors.Menu[group]) - end - if closeFunc then ModUtil.Anchors.CloseFuncs[group]=closeFunc end - - local screen = { Name = group, Components = {} } - local components = screen.Components - ModUtil.Anchors.Menu[group] = screen - - OnScreenOpened({ Flag = screen.Name, PersistCombatUI = true }) - SetConfigOption({ Name = "UseOcclusion", Value = false }) - - components.Background = CreateScreenComponent({ Name = "BlankObstacle", Group = group }) - - if openFunc then openFunc(screen) end - - return screen -end - -function ModUtil.Hades.DimMenu( screen ) - if not screen then return end - if not screen.Components.BackgroundDim then - screen.Components.BackgroundDim = CreateScreenComponent({ Name = "rectangle01", Group = screen.Name }) - SetScale({ Id = screen.Components.BackgroundDim.Id, Fraction = 4 }) - end - SetColor({ Id = screen.Components.BackgroundDim.Id, Color = {0.090, 0.090, 0.090, 0.8} }) -end - -function ModUtil.Hades.UndimMenu( screen ) - if not screen then return end - if not screen.Components.BackgroundDim then return end - SetColor({ Id = screen.Components.BackgroundDim.Id, Color = {0.090, 0.090, 0.090, 0} }) -end - -function ModUtil.Hades.PostOpenMenu( screen ) - FreezePlayerUnit() - EnableShopGamepadCursor() - thread(HandleWASDInput, screen) - HandleScreenInput(screen) - return screen -end - -function ModUtil.Hades.GetMenuScreen( group ) - return ModUtil.Anchors.Menu[group] -end - --- Debug Printing - -function ModUtil.Hades.PrintDisplay( text , delay, color ) - if type(text) ~= "string" then - text = tostring(text) - end - text = " "..text.." " - if color == nil then - color = Color.Yellow - end - if delay == nil then - delay = 5 - end - if ModUtil.Anchors.PrintDisplay then - Destroy({Ids = {ModUtil.Anchors.PrintDisplay.Id}}) - end - ModUtil.Anchors.PrintDisplay = CreateScreenComponent({Name = "BlankObstacle", Group = "PrintDisplay", X = ScreenCenterX, Y = 40 }) - CreateTextBox({ Id = ModUtil.Anchors.PrintDisplay.Id, Text = text, FontSize = 22, Color = color, Font = "UbuntuMonoBold"}) - - if delay > 0 then - thread(function() - wait(delay) - Destroy({Ids = {ModUtil.Anchors.PrintDisplay.Id}}) - ModUtil.Anchors.PrintDisplay = nil - end) - end -end - -function ModUtil.Hades.PrintOverhead(text, delay, color, dest) - if type(text) ~= "string" then - text = tostring(text) - end - text = " "..text.." " - if dest == nil then - dest = CurrentRun.Hero.ObjectId - end - if color == nil then - color = Color.Yellow - end - if delay == nil then - delay = 5 - end - Destroy({Ids = {ModUtil.Anchors.PrintOverhead[dest]}}) - local id = SpawnObstacle({ Name = "BlankObstacle", Group = "PrintOverhead", DestinationId = dest }) - ModUtil.Anchors.PrintOverhead[dest] = id - Attach({ Id = id, DestinationId = dest }) - CreateTextBox({ Id = id, Text = text, FontSize = 32, OffsetX = 0, OffsetY = -150, Color = color, Font = "AlegreyaSansSCBold", Justification = "Center" }) - if delay > 0 then - thread(function() - wait(delay) - if ModUtil.Anchors.PrintOverhead[dest] then - Destroy({Ids = {id}}) - ModUtil.Anchors.PrintOverhead[dest] = nil - end - end) - end -end - -local function ClosePrintStack() - if ModUtil.Anchors.PrintStack then - ModUtil.Anchors.PrintStack.CullEnabled = false - PlaySound({ Name = "/SFX/Menu Sounds/GeneralWhooshMENU" }) - ModUtil.Anchors.PrintStack.KeepOpen = false - - CloseScreen(GetAllIds(ModUtil.Anchors.PrintStack.Components),0) - ModUtil.Anchors.PrintStack = nil - end -end - -OnAnyLoad{ ClosePrintStack } - -local function OrderPrintStack(screen,components) - - if screen.CullPrintStack then - local v = screen.TextStack[1] - if v.obj then - Destroy({Ids = {v.obj.Id}}) - components["TextStack_" .. v.tid] = nil - v.obj = nil - screen.TextStack[v.tid]=nil - end - thread( function() - local v = screen.TextStack[2] - if v then - wait(v.data.Delay) - if v.obj then - screen.CullPrintStack = true - end - end - end) - else - thread( function() - local v = screen.TextStack[1] - if v then - wait(v.data.Delay) - if v.obj then - screen.CullPrintStack = true - end - end - end) - end - screen.CullPrintStack = false - - for k,v in pairs(screen.TextStack) do - components["TextStack_" .. k] = nil - Destroy({Ids = {v.obj.Id}}) - end - - screen.TextStack = CollapseTable(screen.TextStack) - for i,v in pairs(screen.TextStack) do - v.tid = i - end - if #screen.TextStack == 0 then - return ClosePrintStack() - end - - local Ymul = screen.StackHeight+1 - local Ygap = 30 - local Yoff = 26*screen.StackHeight+22 - local n =#screen.TextStack - - if n then - for k=1,math.min(n,Ymul) do - v = screen.TextStack[k] - if v then - local data = v.data - screen.TextStack[k].obj = CreateScreenComponent({ Name = "rectangle01", Group = "PrintStack", X = -1000, Y = -1000}) - local textStack = screen.TextStack[k].obj - components["TextStack_" .. k] = textStack - SetScaleX({Id = textStack.Id, Fraction = 10/6}) - SetScaleY({Id = textStack.Id, Fraction = 0.1}) - SetColor({ Id = textStack.Id, Color = data.Bgcol }) - CreateTextBox({ Id = textStack.Id, Text = data.Text, FontSize = data.FontSize, OffsetX = 0, OffsetY = 0, Color = data.Color, Font = data.Font, Justification = "Center" }) - Attach({ Id = textStack.Id, DestinationId = components.Background.Id, OffsetX = 220, OffsetY = -Yoff }) - Yoff = Yoff - Ygap - end - end - end - -end - -function ModUtil.Hades.PrintStack( text, delay, color, bgcol, fontsize, font, sound ) - if color == nil then color = {1,1,1,1} end - if bgcol == nil then bgcol = {0.590, 0.555, 0.657,0.125} end - if fontsize == nil then fontsize = 13 end - if font == nil then font = "UbuntuMonoBold" end - if sound == nil then sound = "/Leftovers/SFX/AuraOff" end - if delay == nil then delay = 3 end - - if type(text) ~= "string" then - text = tostring(text) - end - text = " "..text.." " - - local first = false - if not ModUtil.Anchors.PrintStack then - first = true - ModUtil.Anchors.PrintStack = { Components = {} } - end - local screen = ModUtil.Anchors.PrintStack - local components = screen.Components - - if first then - - screen.KeepOpen = true - screen.TextStack = {} - screen.CullPrintStack = false - screen.MaxStacks = ModUtil.Hades.PrintStackCapacity - screen.StackHeight = ModUtil.Hades.PrintStackHeight - PlaySound({ Name = "/SFX/Menu Sounds/DialoguePanelOutMenu" }) - components.Background = CreateScreenComponent({ Name = "BlankObstacle", Group = "PrintStack", X = ScreenCenterX, Y = 2*ScreenCenterY}) - components.Backing = CreateScreenComponent({ Name = "TraitTray_Center", Group = "PrintStack"}) - Attach({ Id = components.Backing.Id, DestinationId = components.Background.Id, OffsetX = -180, OffsetY = 0 }) - SetColor({ Id = components.Backing.Id, Color = {0.590, 0.555, 0.657, 0.8} }) - SetScaleX({Id = components.Backing.Id, Fraction = 6.25}) - SetScaleY({Id = components.Backing.Id, Fraction = 6/55*(2+screen.StackHeight)}) - - thread( function() - while screen do - wait(0.5) - if screen.CullEnabled then - if screen.CullPrintStack then - OrderPrintStack(screen,components) - end - end - end - end) - - end - - if #screen.TextStack >= screen.MaxStacks then return end - - screen.CullEnabled = false - - local newText = {} - newText.obj = CreateScreenComponent({ Name = "rectangle01", Group = "PrintStack"}) - newText.data = {Delay = delay, Text = text, Color = color, Bgcol = bgcol, Font = font, FontSize = fontsize} - SetColor({ Id = newText.obj.Id, Color = {0,0,0,0}}) - table.insert(screen.TextStack, newText) - - PlaySound({ Name = sound }) - - OrderPrintStack(screen,components) - - screen.CullEnabled = true - -end - -function ModUtil.Hades.PrintStackChunks( text, linespan, ... ) - if not linespan then linespan = 90 end - for _,s in ipairs(ModUtil.ChunkText(text,linespan,ModUtil.Hades.PrintStackCapacity)) do - ModUtil.Hades.PrintStack(s,...) - end -end - --- Custom Menus - -function ModUtil.Hades.NewMenuYesNo( group, closeFunc, openFunc, yesFunc, noFunc, title, body, yesText, noText, icon, iconScale) - - if not group or group == "" then group = "MenuYesNo" end - if not yesFunc or not noFunc then return end - if not icon then icon = "AmmoPack" end - if not iconScale then iconScale = 1 end - if not yesText then yesText = "Yes" end - if not noText then noText = "No" end - if not body then body = "Make a choice..." end - if not title then title = group end - - local screen = ModUtil.Hades.OpenMenu( group, closeFunc, openFunc ) - local components = screen.Components - - PlaySound({ Name = "/SFX/Menu Sounds/GodBoonInteract" }) - - components.LeftPart = CreateScreenComponent({ Name = "TraitTrayBackground", Group = group, X = 1030, Y = 424}) - components.MiddlePart = CreateScreenComponent({ Name = "TraitTray_Center", Group = group, X = 660, Y = 464 }) - components.RightPart = CreateScreenComponent({ Name = "TraitTray_Right", Group = group, X = 1270, Y = 438 }) - SetScaleY({Id = components.LeftPart.Id, Fraction = 0.8}) - SetScaleY({Id = components.MiddlePart.Id, Fraction = 0.8}) - SetScaleY({Id = components.RightPart.Id, Fraction = 0.8}) - SetScaleX({Id = components.MiddlePart.Id, Fraction = 5}) - - - CreateTextBox({ Id = components.Background.Id, Text = " "..title.." ", FontSize = 34, - OffsetX = 0, OffsetY = -225, Color = Color.White, Font = "SpectralSCLight", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 1}, Justification = "Center" }) - CreateTextBox({ Id = components.Background.Id, Text = " "..body.." ", FontSize = 19, - OffsetX = 0, OffsetY = -175, Width = 840, Color = Color.SubTitle, Font = "CrimsonTextItalic", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 1}, Justification = "Center" }) - - components.Icon = CreateScreenComponent({ Name = "BlankObstacle", Group = group }) - Attach({ Id = components.Icon.Id, DestinationId = components.Background.Id, OffsetX = 0, OffsetY = -50}) - SetAnimation({ Name = icon, DestinationId = components.Icon.Id, Scale = iconScale }) - - ModUtil.NewTable(ModUtil.Anchors.Menu[group], "Funcs") - ModUtil.Anchors.Menu[group].Funcs={ - Yes = function(screen, button) - local ret = yesFunc(screen,button) - ModUtil.Hades.CloseMenuYesNo(screen,button) - return ret - end, - No = function(screen, button) - local ret = noFunc(screen,button) - ModUtil.Hades.CloseMenuYesNo(screen,button) - return ret - end, - } - ModUtil.GlobaliseFuncs( ModUtil.Anchors.Menu[group].Funcs, "ModUtil.Anchors.Menu."..group..".Funcs" ) - - components.CloseButton = CreateScreenComponent({ Name = "ButtonClose", Scale = 0.7, Group = group }) - Attach({ Id = components.CloseButton.Id, DestinationId = components.Background.Id, OffsetX = 0, OffsetY = ScreenCenterY - 315 }) - components.CloseButton.OnPressedFunctionName = "ModUtil.Hades.CloseMenuYesNo" - components.CloseButton.ControlHotkey = "Cancel" - - components.YesButton = CreateScreenComponent({ Name = "BoonSlot1", Group = group, Scale = 0.35, }) - components.YesButton.OnPressedFunctionName = "ModUtil.Anchors.Menu."..group..".Funcs.Yes" - SetScaleX({Id = components.YesButton.Id, Fraction = 0.75}) - SetScaleY({Id = components.YesButton.Id, Fraction = 1.15}) - Attach({ Id = components.YesButton.Id, DestinationId = components.Background.Id, OffsetX = -150, OffsetY = 75 }) - CreateTextBox({ Id = components.YesButton.Id, Text = " "..yesText.." ", - FontSize = 28, OffsetX = 0, OffsetY = 0, Width = 720, Color = Color.LimeGreen, Font = "AlegreyaSansSCLight", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, Justification = "Center" - }) - - components.NoButton = CreateScreenComponent({ Name = "BoonSlot1", Group = group, Scale = 0.35, }) - components.NoButton.OnPressedFunctionName = "ModUtil.Anchors.Menu."..group..".Funcs.No" - SetScaleX({Id = components.NoButton.Id, Fraction = 0.75}) - SetScaleY({Id = components.NoButton.Id, Fraction = 1.15}) - Attach({ Id = components.NoButton.Id, DestinationId = components.Background.Id, OffsetX = 150, OffsetY = 75 }) - CreateTextBox({ Id = components.NoButton.Id, Text = noText, - FontSize = 26, OffsetX = 0, OffsetY = 0, Width = 720, Color = Color.Red, Font = "AlegreyaSansSCLight", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, Justification = "Center" - }) - - return ModUtil.Hades.PostOpenMenu( screen ) -end - -function ModUtil.Hades.CloseMenuYesNo( screen, button ) - PlaySound({ Name = "/SFX/Menu Sounds/GeneralWhooshMENU" }) - _G["ModUtil.Anchors.Menu."..screen.Name..".Funcs.Yes"]=nil - _G["ModUtil.Anchors.Menu."..screen.Name..".Funcs.No"]=nil - ModUtil.Hades.CloseMenu( screen, button ) -end - --- Misc - -function ModUtil.Hades.RandomElement( tableArg, rng ) - local Collapsed = CollapseTable( tableArg ) - return Collapsed[RandomInt( 1, #Collapsed, rng )] -end \ No newline at end of file diff --git a/ModUtil/modfile.txt b/ModUtil/modfile.txt deleted file mode 100644 index 087f24d..0000000 --- a/ModUtil/modfile.txt +++ /dev/null @@ -1,5 +0,0 @@ -:: Mod Utility - -Load Priority 0 -Top Import "ModUtil.lua" -Import "ModUtilHades.lua" \ No newline at end of file diff --git a/RunStartControl b/RunStartControl new file mode 160000 index 0000000..5494e82 --- /dev/null +++ b/RunStartControl @@ -0,0 +1 @@ +Subproject commit 5494e82f7c9f35d430fbabeac07d5fbd4f42b213 diff --git a/sgg-mod-modutil b/sgg-mod-modutil new file mode 160000 index 0000000..6a98494 --- /dev/null +++ b/sgg-mod-modutil @@ -0,0 +1 @@ +Subproject commit 6a98494b82f80fdfbb785395acc7bc7689327279 From 28c6bd0b8884a51f92b6607bf23b982a166155ab Mon Sep 17 00:00:00 2001 From: Caleb Date: Tue, 6 Jul 2021 20:18:31 -0400 Subject: [PATCH 17/77] Updates to RunStartControl, adjusted default Settings Hash --- HadesSpeedrunningModpack/SettingsHashMenu.lua | 2 +- RunStartControl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index 44a32ad..f28ac54 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -44,7 +44,7 @@ HSMConfigMenu.NonRulesetSettings = { } HSMConfigMenu.SettingsDefaults = { - RulesetSettings = 2866471, + RulesetSettings = 769319, NonRulesetSettings = 0 } diff --git a/RunStartControl b/RunStartControl index 5494e82..1765c95 160000 --- a/RunStartControl +++ b/RunStartControl @@ -1 +1 @@ -Subproject commit 5494e82f7c9f35d430fbabeac07d5fbd4f42b213 +Subproject commit 1765c95ce29a1cfafcf163a9ec2511e62c11a70d From cfa15431a21271b4b2ef897c946500734ad12cde Mon Sep 17 00:00:00 2001 From: Caleb Date: Wed, 7 Jul 2021 18:02:46 -0400 Subject: [PATCH 18/77] Updated version# and runstartcontrol issues --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 35eeb98..a5a1827 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.0.0" + Version = "v1.1.1" } HadesSpeedrunningModpack.config = config From 0dcd0dd5fb9ebb5a4715176f10ce8869a23999ea Mon Sep 17 00:00:00 2001 From: Caleb Date: Wed, 7 Jul 2021 18:07:37 -0400 Subject: [PATCH 19/77] actually moving bug fixes --- RunStartControl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RunStartControl b/RunStartControl index 1765c95..9028216 160000 --- a/RunStartControl +++ b/RunStartControl @@ -1 +1 @@ -Subproject commit 1765c95ce29a1cfafcf163a9ec2511e62c11a70d +Subproject commit 90282163b61aa015c497e4cd93e89a488b6f9da3 From 4acbd05a87f6ec2b919bbff72d5eb44bb499b26e Mon Sep 17 00:00:00 2001 From: cgull <48661803+cgu11@users.noreply.github.com> Date: Sun, 18 Jul 2021 13:19:37 -0400 Subject: [PATCH 20/77] Update HadesSpeedrunningModpack.lua --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index a5a1827..7804787 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.1.1" + Version = "v1.1.0-rc1" } HadesSpeedrunningModpack.config = config From 67c95f5ca9cc23501e5e409dfa36b1f84bb5d14c Mon Sep 17 00:00:00 2001 From: Caleb Date: Sun, 18 Jul 2021 14:22:57 -0400 Subject: [PATCH 21/77] Single run ruleset added --- HadesSpeedrunningModpack/SettingsHashMenu.lua | 31 ++++++++++++++++--- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index f28ac54..7c7f9ee 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -29,6 +29,15 @@ HSMConfigMenu.RulesetSettings = { {Key = "ThanatosControl.config.ThanatosSetting", Values = {"Vanilla", "Removed"}, Default = "Removed"}, } +HSMConfigMenu.MultiRunSettings = DeepCopyTable(HSMConfigMenu.RulesetSettings) +HSMConfigMenu.SingleRunSettings = DeepCopyTable(HSMConfigMenu.RulesetSettings) +for i, setting in ipairs(HSMConfigMenu.SingleRunSettings) do + if setting.Key == "ThanatosControl.config.ThanatosSetting" then + setting.Default = "Vanilla" + break + end +end + HSMConfigMenu.NonRulesetSettings = { {Key = "QuickRestart.config.Enabled", Values = {false, true}, Default = false}, @@ -45,6 +54,8 @@ HSMConfigMenu.NonRulesetSettings = { HSMConfigMenu.SettingsDefaults = { RulesetSettings = 769319, + MultiRunSettings = 769319, + SingleRunSettings = 769318, NonRulesetSettings = 0 } @@ -145,9 +156,22 @@ function HSMConfigMenu.CreateSettingsHashMenu( screen ) Font = "AlegrayaSansSCRegular", Items = { ["Default"] = {Text = "Select a Preset", event = function() end}, - {Text = "Multirun Leaderboard Ruleset", event = function() - HSMConfigMenu.LoadSettings("RulesetSettings") - local rulesetHashInt = CalculateHash(HSMConfigMenu.RulesetSettings, _G) + {Text = "Multi-Run Leaderboard Ruleset", event = function() + HSMConfigMenu.LoadSettings("MultiRunSettings") + local rulesetHashInt = CalculateHash(HSMConfigMenu.MultiRunSettings, _G) + local rulesetHash = HSMConfigMenu.ConvertIntToBase25(rulesetHashInt, 5) + HSMConfigMenu.CurrentRulesetHash = rulesetHash + + for i = 1, #rulesetHash do + SetAnimation({ Name = HSMConfigMenu.HashImages[rulesetHash[i]], DestinationId = screen.Components["RulesetHashImage" .. i].Id, OffsetX = 0, OffsetY = 0}) + end + + HSMConfigMenu.updateRulesetHashDisplay() + HSMConfigMenu.SaveSettingsToGlobal() + end}, + {Text = "Single Run Leaderboard Ruleset", event = function() + HSMConfigMenu.LoadSettings("SingleRunSettings") + local rulesetHashInt = CalculateHash(HSMConfigMenu.SingleRunSettings, _G) local rulesetHash = HSMConfigMenu.ConvertIntToBase25(rulesetHashInt, 5) HSMConfigMenu.CurrentRulesetHash = rulesetHash @@ -158,7 +182,6 @@ function HSMConfigMenu.CreateSettingsHashMenu( screen ) HSMConfigMenu.updateRulesetHashDisplay() HSMConfigMenu.SaveSettingsToGlobal() end}, - {Text = "Any Heat Speedrun Ruleset v1.0", IsEnabled = false, event = function() end} }, }) itemLocationY = itemLocationY + 100 From 47b55ae713de5e4e8101de0826e4270324a22fc3 Mon Sep 17 00:00:00 2001 From: cgull <48661803+cgu11@users.noreply.github.com> Date: Tue, 20 Jul 2021 16:41:04 -0400 Subject: [PATCH 22/77] Update HadesSpeedrunningModpack.lua --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 7804787..0a4b3d0 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.1.0-rc1" + Version = "v1.1.0-rc.1" } HadesSpeedrunningModpack.config = config From d6f3a98e1170fb242f00b723d9cca9bd93bd8a83 Mon Sep 17 00:00:00 2001 From: Museus <69139347+Museus@users.noreply.github.com> Date: Thu, 29 Jul 2021 12:01:56 -0400 Subject: [PATCH 23/77] QuickRestart: Disallow in Thanatos Encounters until enemies spawn (#52) * QuickRestart: Disallow in Thanatos Encounters until enemies spawn * !fixup add nil-checking to Thanatos in CanReset --- QuickRestart/QuickRestart.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 83e9806..fca5cef 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -16,7 +16,15 @@ function QuickRestart.CanReset() if not IsEmpty( CurrentRun.Hero.FreezeInputKeys ) then return false end -- Combat UI must be visible - if not (ShowingCombatUI or false) then return false end + if not (ShowingCombatUI or false) then return false end + + -- If we're in a Thanatos Room, enemies must have already spawned + if (CurrentRun ~= nil and CurrentRun.CurrentRoom ~= nil and + CurrentRun.CurrentRoom.Encounter ~= nil and + CurrentRun.CurrentRoom.Encounter.ThanatosId ~= nil + and GetActiveEnemyCount() == 0) then + return false + end return true end From 6f4ce91ee2efa9d0738dbe71011bb5a80d281e43 Mon Sep 17 00:00:00 2001 From: cgull Date: Thu, 29 Jul 2021 15:53:11 -0400 Subject: [PATCH 24/77] Miniboss control: removed asterius from leaderboard --- MinibossControl/MinibossControl.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 48aabfd..4c5904f 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -100,9 +100,9 @@ MinibossControl.Presets = { B_MiniBoss02 = 2, -- Elysium_ButterflyBall - C_MiniBoss02 = 1, + C_MiniBoss02 = 2, -- Elysium_Asterius - C_MiniBoss01 = 1, + C_MiniBoss01 = 0, -- If true, Tiny Vermin will not spawn RemoveTinyVermin = true, From cdd294faae48a8264887b7df94cda3e486df639c Mon Sep 17 00:00:00 2001 From: cgull Date: Thu, 29 Jul 2021 15:53:28 -0400 Subject: [PATCH 25/77] Update version to 1.1.0-rc.2 --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 0a4b3d0..58a5f56 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.1.0-rc.1" + Version = "v1.1.0-rc.2" } HadesSpeedrunningModpack.config = config From 8b4a7686669e50abdcf3d62b68aeb51d59fb1b4b Mon Sep 17 00:00:00 2001 From: Museus <69139347+Museus@users.noreply.github.com> Date: Sun, 1 Aug 2021 20:14:44 -0400 Subject: [PATCH 26/77] Release version v1.1.0 --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 58a5f56..81fb4a0 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.1.0-rc.2" + Version = "v1.1.0" } HadesSpeedrunningModpack.config = config From 79098a874e0fc0e417a6400aa448c00b9f4cd1a7 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 10:43:14 -0400 Subject: [PATCH 27/77] Approval Process high heat bug fixes --- .gitmodules | 3 +++ DontGetVorimed/.gitattributes | 2 ++ DontGetVorimed/DontGetVorimed.lua | 13 +++++++++++-- DontGetVorimed/LICENSE | 21 +++++++++++++++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 DontGetVorimed/.gitattributes create mode 100644 DontGetVorimed/LICENSE diff --git a/.gitmodules b/.gitmodules index 2911fe7..1d86fbd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,6 @@ [submodule "sgg-mod-modutil"] path = sgg-mod-modutil url = https://github.com/SGG-Modding/sgg-mod-modutil.git +[submodule "DontGetVorimed"] + path = DontGetVorimed + url = https://github.com/cgullsHadesMods/DontGetVorimed diff --git a/DontGetVorimed/.gitattributes b/DontGetVorimed/.gitattributes new file mode 100644 index 0000000..dfe0770 --- /dev/null +++ b/DontGetVorimed/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/DontGetVorimed/DontGetVorimed.lua b/DontGetVorimed/DontGetVorimed.lua index dc1bbd6..d9f1028 100644 --- a/DontGetVorimed/DontGetVorimed.lua +++ b/DontGetVorimed/DontGetVorimed.lua @@ -36,7 +36,7 @@ end, DontGetVorimed) -- After first boon reward has been selected, return to normal number of choices ModUtil.WrapBaseFunction("HandleUpgradeChoiceSelection", function ( baseFunc, screen, button ) - if config.Enabled and not DontGetVorimed.BoonTakenFlag then + if config.Enabled and not DontGetVorimed.BoonTakenFlag and #GetAllUpgradeableGodTraits() == 0 then if button.Data.God ~= nil then LootChoiceExt.Choices = 3 LootChoiceExt.LastLootChoices = 3 @@ -51,6 +51,15 @@ ModUtil.WrapBaseFunction("HandleUpgradeChoiceSelection", function ( baseFunc, sc baseFunc(screen, button) end, DontGetVorimed) +-- Removing Approval Process blockers on the 4-core boon +ModUtil.WrapBaseFunction("CalcNumLootChoices", function( baseFunc ) + if LootChoiceExt.Choices == 4 then + return baseFunc() + GetNumMetaUpgrades("ReducedLootChoicesShrineUpgrade") + else + return baseFunc() + end +end, DontGetVorimed) + -- If the player ever rerolls, reduce to 3 options ModUtil.WrapBaseFunction("DestroyBoonLootButtons", function ( baseFunc, lootData ) baseFunc(lootData) @@ -59,4 +68,4 @@ ModUtil.WrapBaseFunction("DestroyBoonLootButtons", function ( baseFunc, lootData LootChoiceExt.LastLootChoices = 3 DontGetVorimed.BoonTakenFlag = true end -end, DontGetVorimed) +end, DontGetVorimed) \ No newline at end of file diff --git a/DontGetVorimed/LICENSE b/DontGetVorimed/LICENSE new file mode 100644 index 0000000..909815a --- /dev/null +++ b/DontGetVorimed/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 ellomenop + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From 4bb8b65e9053fa0ded45a76489e2be309811c30b Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 10:53:19 -0400 Subject: [PATCH 28/77] QuickRestart: add 0.1 second delay before check Many weird bugs are caused by the button combo interacting with something, or frame-perfect deaths. Adding the delay helps to prevent that. --- QuickRestart/QuickRestart.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index fca5cef..faf0d59 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -9,6 +9,9 @@ function QuickRestart.CanReset() -- QuickRestart must be Enabled if not config.Enabled then return false end + -- Short delay to handle edge cases + wait(0.1) + -- Zag must not be in the House if ModUtil.PathGet("CurrentDeathAreaRoom") then return false end From 004bfec5b6ec02b15d86b51acdf54bcb94fd54db Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 11:48:40 -0400 Subject: [PATCH 29/77] Added HellModeToggle feature and corresponding menu button --- .gitmodules | 3 +++ .../QualityOfLifeMenu.lua | 26 +++++++++++++++++++ HellModeToggle | 1 + 3 files changed, 30 insertions(+) create mode 160000 HellModeToggle diff --git a/.gitmodules b/.gitmodules index 1d86fbd..c5a47b5 100644 --- a/.gitmodules +++ b/.gitmodules @@ -7,3 +7,6 @@ [submodule "DontGetVorimed"] path = DontGetVorimed url = https://github.com/cgullsHadesMods/DontGetVorimed +[submodule "HellModeToggle"] + path = HellModeToggle + url = https://github.com/cgullsHadesMods/HellModeToggle diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index b2da304..1967efa 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -224,8 +224,34 @@ function HSMConfigMenu.CreateQolMenu(screen) }) screen.Components["RtaTimerResetButton"].OnPressedFunctionName = "RtaTimer__ResetRtaTimer" itemLocationY = itemLocationY + itemSpacingY + + ----------------------------- + -- Hell Mode Toggle + ----------------------------- + screen.Components["HellModeToggleButton"] = CreateScreenComponent({ + Name = "MarketSlot", + Scale = 1, + X = itemLocationX + itemSpacingX / 2 - 50, + Y = itemLocationY + itemSpacingY, + Group = "Combat_Menu" }) + SetScaleX({ Id = screen.Components["HellModeToggleButton"].Id, Fraction = .375}) + SetScaleY({ Id = screen.Components["HellModeToggleButton"].Id, Fraction = .5 }) + + CreateTextBox({ + Id = screen.Components["HellModeToggleButton"].Id, + Text = "Toggle Hell Mode", + Color = Color.BoonPatchCommon, + FontSize = 12, + OffsetX = 0, OffsetY = 0, + Font = "AlegrayaSansSCRegular", + ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, + Justification = "Center" + }) + screen.Components["HellModeToggleButton"].OnPressedFunctionName = "HellModeToggle.ToggleHellMode" + itemLocationY = itemLocationY + itemSpacingY end + ModUtil.LoadOnce(function() ModConfigMenu.RegisterMenuOverride({ModName = "QoL"}, HSMConfigMenu.CreateQolMenu) end) diff --git a/HellModeToggle b/HellModeToggle new file mode 160000 index 0000000..359bf7c --- /dev/null +++ b/HellModeToggle @@ -0,0 +1 @@ +Subproject commit 359bf7c8d2682f978aa2026a17eb0b575f4135b2 From 6363641e4eee6a54f6ac9c134d4e0ac87b7ccde6 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 12:39:23 -0400 Subject: [PATCH 30/77] Updating credits menu screen --- HadesSpeedrunningModpack/AttributionAndNotes.lua | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/HadesSpeedrunningModpack/AttributionAndNotes.lua b/HadesSpeedrunningModpack/AttributionAndNotes.lua index 7abcd11..d4f486a 100644 --- a/HadesSpeedrunningModpack/AttributionAndNotes.lua +++ b/HadesSpeedrunningModpack/AttributionAndNotes.lua @@ -17,15 +17,10 @@ function HSMConfigMenu.CreateAttributionMenu(screen) }) CreateTextBox({ Id = screen.Components["ThanksForAllTheFish"].Id, - Text = "Hey Everyone,\\n" - .. " Business first. The Hades Speedrunning ModPack (v0.1) will be ever evolving." - .. " There WILL be bugs. \\n It took over 8000 lines of code and 100s of hours of nerd sweat to get this far." - .. " So I know its difficult after 4 hours of Flurry Jab resets, but please be patient, it takes time and toil to make your ideas into reality." - - .. " \\n Also please lend your voice and opinions to making it better as we move forwards." - .. " Wriste is organizing a document and survey to get us started, but feel free to reach out directly or via #modification-station too." - - .. " \\n \\n Secondly, I just wanted to say thank you.\\n \\n Thank you to the Speedrunning Community:" + Text = "Modders: \\n" + .. "Ellomenop, Museus, cgull, paradigmsort, erumi321, Magic_Gonads, PonyWarrior" + .. "\\n Testers: The Entire Hades Speedrunning Commumity <3" + .. " \\n\\n\\n Note from Ello: I just wanted to say thank you.\\n \\n Thank you to the Speedrunning Community:" .. " \\n This community is an immensely positive place to be and thats no accident." .. " \\n Its due to our shared love of this game (or perhaps our shared hatred of 5 sacks)." .. " \\n Its due to the hard work of everyone building each other up (even if its just so the Fish Flex hits that much harder)." From eaeb3f84ace66042bdabd697efeff8e19653f6a0 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 14:16:57 -0400 Subject: [PATCH 31/77] Hell Mode Toggle final fixes, QoL Menu updated, DontGetVorimed bugfixes --- .../HadesSpeedrunningModpack.lua | 2 +- .../QualityOfLifeMenu.lua | 22 +++++++++++++++++-- HellModeToggle | 2 +- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 81fb4a0..a5a1827 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.1.0" + Version = "v1.1.1" } HadesSpeedrunningModpack.config = config diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index 1967efa..9f67c07 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -228,11 +228,29 @@ function HSMConfigMenu.CreateQolMenu(screen) ----------------------------- -- Hell Mode Toggle ----------------------------- + screen.Components["HellModeToggleTextBox"] = CreateScreenComponent({ + Name = "BlankObstacle", + Scale = 1, + X = itemLocationX, + Y = itemLocationY, + Group = "Combat_Menu" }) + CreateTextBox({ + Id = screen.Components["HellModeToggleTextBox"].Id, + Text = "Turn Hell Mode On/Off:", + Color = Color.BoonPatchCommon, + FontSize = 16, + OffsetX = 0, OffsetY = 0, + Font = "AlegrayaSansSCRegular", + ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, + Justification = "Left" + }) + itemLocationY = itemLocationY + itemSpacingY + screen.Components["HellModeToggleButton"] = CreateScreenComponent({ Name = "MarketSlot", Scale = 1, X = itemLocationX + itemSpacingX / 2 - 50, - Y = itemLocationY + itemSpacingY, + Y = itemLocationY, Group = "Combat_Menu" }) SetScaleX({ Id = screen.Components["HellModeToggleButton"].Id, Fraction = .375}) SetScaleY({ Id = screen.Components["HellModeToggleButton"].Id, Fraction = .5 }) @@ -241,7 +259,7 @@ function HSMConfigMenu.CreateQolMenu(screen) Id = screen.Components["HellModeToggleButton"].Id, Text = "Toggle Hell Mode", Color = Color.BoonPatchCommon, - FontSize = 12, + FontSize = 16, OffsetX = 0, OffsetY = 0, Font = "AlegrayaSansSCRegular", ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, diff --git a/HellModeToggle b/HellModeToggle index 359bf7c..8942977 160000 --- a/HellModeToggle +++ b/HellModeToggle @@ -1 +1 @@ -Subproject commit 359bf7c8d2682f978aa2026a17eb0b575f4135b2 +Subproject commit 89429779afa365031b74cc9f79090c5ada2324a5 From e77cf7f531c6f41002e401eadc2df9fe7ccb0da3 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 14:24:40 -0400 Subject: [PATCH 32/77] Removing Starting Boon Selector (not legal for any category) and fixing AntiVorime submodule weirdness step 1 --- DontGetVorimed/.gitattributes | 2 - DontGetVorimed/DontGetVorimed.lua | 71 - DontGetVorimed/LICENSE | 21 - DontGetVorimed/modfile.txt | 6 - .../Scripts/EllosBoonSelectorMod.lua | 1163 ----------------- EllosStartingBoonSelectorMod/modfile.txt | 2 - 6 files changed, 1265 deletions(-) delete mode 100644 DontGetVorimed/.gitattributes delete mode 100644 DontGetVorimed/DontGetVorimed.lua delete mode 100644 DontGetVorimed/LICENSE delete mode 100644 DontGetVorimed/modfile.txt delete mode 100644 EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua delete mode 100644 EllosStartingBoonSelectorMod/modfile.txt diff --git a/DontGetVorimed/.gitattributes b/DontGetVorimed/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/DontGetVorimed/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/DontGetVorimed/DontGetVorimed.lua b/DontGetVorimed/DontGetVorimed.lua deleted file mode 100644 index d9f1028..0000000 --- a/DontGetVorimed/DontGetVorimed.lua +++ /dev/null @@ -1,71 +0,0 @@ ---[[ - DontGetVorimed v1.0 - Authors: - ellomenop (Discord: ellomenop#2254) - cgull (Discord: cgull#4469) - Makes the first boon reward offer all 4 core boons -]] -ModUtil.RegisterMod("DontGetVorimed") - -local config = { - ModName = "Dont Get Vorimed", - Enabled = true -} -DontGetVorimed.config = config -DontGetVorimed.BoonTakenFlag = nil - --- When the first room is created, set the number of loot choices to 4 --- This will persist until the first boon reward is received or rolled over -ModUtil.WrapBaseFunction("ChooseStartingRoom", function ( baseFunc, ... ) - if config.Enabled then - LootChoiceExt.Choices = 4 - DontGetVorimed.BoonTakenFlag = false - end - return baseFunc(...) -end, DontGetVorimed) - -ModUtil.WrapBaseFunction("CreateLoot", function( baseFunc, args ) - local lootData = args.LootData or LootData[args.Name] - if config.Enabled and not lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then - LootChoiceExt.Choices = 3 - LootChoiceExt.LastLootChoices = 3 - end - - return baseFunc(args) -end, DontGetVorimed) - --- After first boon reward has been selected, return to normal number of choices -ModUtil.WrapBaseFunction("HandleUpgradeChoiceSelection", function ( baseFunc, screen, button ) - if config.Enabled and not DontGetVorimed.BoonTakenFlag and #GetAllUpgradeableGodTraits() == 0 then - if button.Data.God ~= nil then - LootChoiceExt.Choices = 3 - LootChoiceExt.LastLootChoices = 3 - DontGetVorimed.BoonTakenFlag = true - else - -- reset to 4 options after selecting a hammer/chaos before first boon has been taken - LootChoiceExt.Choices = 4 - LootChoiceExt.LastLootChoices = 4 - end - end - - baseFunc(screen, button) -end, DontGetVorimed) - --- Removing Approval Process blockers on the 4-core boon -ModUtil.WrapBaseFunction("CalcNumLootChoices", function( baseFunc ) - if LootChoiceExt.Choices == 4 then - return baseFunc() + GetNumMetaUpgrades("ReducedLootChoicesShrineUpgrade") - else - return baseFunc() - end -end, DontGetVorimed) - --- If the player ever rerolls, reduce to 3 options -ModUtil.WrapBaseFunction("DestroyBoonLootButtons", function ( baseFunc, lootData ) - baseFunc(lootData) - if config.Enabled and lootData.GodLoot and not DontGetVorimed.BoonTakenFlag then - LootChoiceExt.Choices = 3 - LootChoiceExt.LastLootChoices = 3 - DontGetVorimed.BoonTakenFlag = true - end -end, DontGetVorimed) \ No newline at end of file diff --git a/DontGetVorimed/LICENSE b/DontGetVorimed/LICENSE deleted file mode 100644 index 909815a..0000000 --- a/DontGetVorimed/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 ellomenop - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/DontGetVorimed/modfile.txt b/DontGetVorimed/modfile.txt deleted file mode 100644 index dc14973..0000000 --- a/DontGetVorimed/modfile.txt +++ /dev/null @@ -1,6 +0,0 @@ --: - DontGetVorimed v1.0 - Authors: - ellomenop (Discord: ellomenop#2254) -:- -Import DontGetVorimed.lua diff --git a/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua b/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua deleted file mode 100644 index 5a95e42..0000000 --- a/EllosStartingBoonSelectorMod/Scripts/EllosBoonSelectorMod.lua +++ /dev/null @@ -1,1163 +0,0 @@ -ModUtil.RegisterMod("EllosBoonSelectorMod") - -local config = { - showPreview = true, - showChaosPreview = true, - showNextRoomPreview = true, - chaosFiltersActive = true, - boonRarityFiltersActive = true, -} -EllosBoonSelectorMod.config = config - -EllosBoonSelectorMod.RarityFilter = {} -EllosBoonSelectorMod.GodFilter = "" -EllosBoonSelectorMod.ChaosFilter = "" -EllosBoonSelectorMod.CurrentHammerIndex = 1 - -EllosBoonSelectorMod.RarityColors = {Color.BoonPatchCommon, Color.BoonPatchRare, Color.BoonPatchEpic, Color.BoonPatchLegendary} - -EllosBoonSelectorMod.BoonGods = {"Aphrodite", "Ares", "Artemis", "Athena", "Demeter", "Dionysus", "Poseidon", "Zeus"} -EllosBoonSelectorMod.ChaosLocations = { "No Chaos", "Room 1 Chaos", "Room 2 Chaos" } -EllosBoonSelectorMod.PriorityBoons = {"WeaponTrait","SecondaryTrait","RangedTrait","RushTrait"} -EllosBoonSelectorMod.PriorityBoonsOrder = {WeaponTrait = 1, SecondaryTrait = 2, RangedTrait = 3, RushTrait = 4} -EllosBoonSelectorMod.PriorityBoonCannonicalNameToCodeName = { - Attack = "WeaponTrait", - Special = "SecondaryTrait", - Dash = "RushTrait", - Cast = "RangedTrait" -} - -EllosBoonSelectorMod.HammerOptions = { - SwordWeapon1 = {"SwordTwoComboTrait","SwordSecondaryAreaDamageTrait","SwordGoldDamageTrait","SwordBlinkTrait","SwordThrustWaveTrait","SwordHealthBufferDamageTrait","SwordSecondaryDoubleAttackTrait","SwordCriticalTrait","SwordBackstabTrait","SwordDoubleDashAttackTrait","SwordHeavySecondStrikeTrait","SwordCursedLifeStealTrait"}, - SwordWeapon2 = {"SwordTwoComboTrait","SwordSecondaryAreaDamageTrait","SwordGoldDamageTrait","SwordBlinkTrait","SwordThrustWaveTrait","SwordHealthBufferDamageTrait","SwordSecondaryDoubleAttackTrait","SwordCriticalTrait","SwordBackstabTrait","SwordDoubleDashAttackTrait","SwordHeavySecondStrikeTrait","SwordCursedLifeStealTrait"}, - SwordWeapon3 = {"SwordTwoComboTrait","SwordSecondaryAreaDamageTrait","SwordGoldDamageTrait","SwordBlinkTrait","SwordThrustWaveTrait","SwordHealthBufferDamageTrait","SwordSecondaryDoubleAttackTrait","SwordCriticalTrait","SwordBackstabTrait","SwordDoubleDashAttackTrait","SwordHeavySecondStrikeTrait","SwordCursedLifeStealTrait"}, - SwordWeapon4 = {"SwordConsecrationBoostTrait","SwordSecondaryAreaDamageTrait","SwordGoldDamageTrait","SwordBlinkTrait","SwordThrustWaveTrait","SwordHealthBufferDamageTrait","SwordSecondaryDoubleAttackTrait","SwordBackstabTrait","SwordDoubleDashAttackTrait","SwordCursedLifeStealTrait"}, - SpearWeapon1 = {"SpearReachAttack", "SpearAutoAttack", "SpearThrowExplode", "SpearThrowBounce", "SpearThrowPenetrate", "SpearThrowCritical", "SpearSpinDamageRadius", "SpearSpinChargeLevelTime", "SpearDashMultiStrike", "SpearThrowElectiveCharge", "SpearSpinChargeAreaDamageTrait", "SpearAttackPhalanxTrait"}, - SpearWeapon2 = {"SpearReachAttack", "SpearAutoAttack", "SpearThrowPenetrate", "SpearThrowCritical", "SpearSpinDamageRadius", "SpearSpinChargeLevelTime", "SpearDashMultiStrike", "SpearSpinChargeAreaDamageTrait", "SpearAttackPhalanxTrait"}, - SpearWeapon3 = {"SpearReachAttack", "SpearThrowExplode", "SpearThrowBounce", "SpearThrowPenetrate", "SpearThrowCritical", "SpearSpinDamageRadius", "SpearSpinChargeLevelTime", "SpearDashMultiStrike", "SpearThrowElectiveCharge", "SpearSpinChargeAreaDamageTrait", "SpearAttackPhalanxTrait"}, - SpearWeapon4 = {"SpearSpinTravelDurationTrait","SpearReachAttack", "SpearThrowPenetrate", "SpearSpinDamageRadius", "SpearSpinChargeLevelTime", "SpearDashMultiStrike", "SpearThrowElectiveCharge", "SpearSpinChargeAreaDamageTrait", "SpearAttackPhalanxTrait"}, - ShieldWeapon1 = {"ShieldDashAOETrait", "ShieldRushProjectileTrait", "ShieldThrowFastTrait", "ShieldThrowCatchExplode", "ShieldChargeHealthBufferTrait", "ShieldChargeSpeedTrait", "ShieldBashDamageTrait", "ShieldPerfectRushTrait", "ShieldThrowElectiveCharge", "ShieldThrowEmpowerTrait", "ShieldBlockEmpowerTrait", "ShieldThrowRushTrait"}, - ShieldWeapon2 = {"ShieldDashAOETrait", "ShieldRushProjectileTrait", "ShieldThrowFastTrait", "ShieldThrowCatchExplode", "ShieldChargeHealthBufferTrait", "ShieldChargeSpeedTrait", "ShieldBashDamageTrait", "ShieldPerfectRushTrait", "ShieldThrowEmpowerTrait", "ShieldBlockEmpowerTrait", "ShieldThrowRushTrait"}, - ShieldWeapon3 = {"ShieldDashAOETrait", "ShieldRushProjectileTrait", "ShieldThrowCatchExplode", "ShieldChargeHealthBufferTrait", "ShieldChargeSpeedTrait", "ShieldBashDamageTrait", "ShieldPerfectRushTrait", "ShieldThrowEmpowerTrait", "ShieldBlockEmpowerTrait"}, - ShieldWeapon4 = {"ShieldLoadAmmoBoostTrait", "ShieldDashAOETrait", "ShieldRushProjectileTrait", "ShieldThrowFastTrait", "ShieldThrowCatchExplode", "ShieldChargeHealthBufferTrait", "ShieldChargeSpeedTrait", "ShieldBashDamageTrait", "ShieldPerfectRushTrait", "ShieldThrowElectiveCharge", "ShieldThrowEmpowerTrait", "ShieldBlockEmpowerTrait", "ShieldThrowRushTrait"}, - BowWeapon1 = {"BowDoubleShotTrait", "BowLongRangeDamageTrait", "BowSlowChargeDamageTrait", "BowTapFireTrait", "BowPenetrationTrait", "BowPowerShotTrait", "BowSecondaryBarrageTrait", "BowTripleShotTrait", "BowSecondaryFocusedFireTrait", "BowChainShotTrait", "BowCloseAttackTrait", "BowConsecutiveBarrageTrait"}, - BowWeapon2 = {"BowDoubleShotTrait", "BowLongRangeDamageTrait", "BowSlowChargeDamageTrait", "BowTapFireTrait", "BowPenetrationTrait", "BowPowerShotTrait", "BowSecondaryBarrageTrait", "BowTripleShotTrait", "BowChainShotTrait", "BowCloseAttackTrait", "BowConsecutiveBarrageTrait"}, - BowWeapon3 = {"BowDoubleShotTrait", "BowLongRangeDamageTrait", "BowSlowChargeDamageTrait", "BowTapFireTrait", "BowPenetrationTrait", "BowPowerShotTrait", "BowSecondaryBarrageTrait", "BowTripleShotTrait", "BowSecondaryFocusedFireTrait", "BowChainShotTrait", "BowCloseAttackTrait", "BowConsecutiveBarrageTrait"}, - BowWeapon4 = {"BowBondBoostTrait", "BowDoubleShotTrait", "BowLongRangeDamageTrait", "BowSlowChargeDamageTrait", "BowPowerShotTrait", "BowSecondaryBarrageTrait", "BowTripleShotTrait", "BowChainShotTrait", "BowCloseAttackTrait"}, - FistWeapon1 = {"FistReachAttackTrait", "FistDashAttackHealthBufferTrait", "FistTeleportSpecialTrait", "FistDoubleDashSpecialTrait", "FistChargeSpecialTrait", "FistKillTrait", "FistSpecialLandTrait", "FistAttackFinisherTrait", "FistConsecutiveAttackTrait", "FistSpecialFireballTrait", "FistAttackDefenseTrait", "FistHeavyAttackTrait"}, - FistWeapon2 = {"FistReachAttackTrait", "FistDashAttackHealthBufferTrait", "FistKillTrait", "FistSpecialLandTrait", "FistAttackFinisherTrait", "FistConsecutiveAttackTrait", "FistAttackDefenseTrait", "FistHeavyAttackTrait", "FistDoubleDashSpecialTrait"}, - FistWeapon3 = {"FistReachAttackTrait", "FistDashAttackHealthBufferTrait", "FistTeleportSpecialTrait", "FistDoubleDashSpecialTrait", "FistChargeSpecialTrait", "FistKillTrait", "FistAttackFinisherTrait", "FistConsecutiveAttackTrait", "FistSpecialFireballTrait", "FistAttackDefenseTrait", "FistHeavyAttackTrait"}, - FistWeapon4 = {"FistDetonateBoostTrait", "FistSpecialLandTrait", "FistChargeSpecialTrait", "FistConsecutiveAttackTrait", "FistDashAttackHealthBufferTrait", "FistAttackDefenseTrait", "FistTeleportSpecialTrait", "FistDoubleDashSpecialTrait", "FistKillTrait"}, - GunWeapon1 = {"GunSlowGrenade", "GunMinigunTrait", "GunShotgunTrait", "GunExplodingSecondaryTrait", "GunGrenadeFastTrait", "GunArmorPenerationTrait", "GunInfiniteAmmoTrait", "GunGrenadeClusterTrait", "GunGrenadeDropTrait", "GunHeavyBulletTrait", "GunChainShotTrait", "GunHomingBulletTrait"}, - GunWeapon2 = {"GunSlowGrenade", "GunMinigunTrait", "GunShotgunTrait", "GunExplodingSecondaryTrait", "GunGrenadeFastTrait", "GunArmorPenerationTrait", "GunInfiniteAmmoTrait", "GunGrenadeClusterTrait", "GunGrenadeDropTrait", "GunHeavyBulletTrait", "GunChainShotTrait", "GunHomingBulletTrait"}, - GunWeapon3 = {"GunSlowGrenade", "GunMinigunTrait", "GunShotgunTrait", "GunExplodingSecondaryTrait", "GunGrenadeFastTrait", "GunArmorPenerationTrait", "GunInfiniteAmmoTrait", "GunGrenadeClusterTrait", "GunGrenadeDropTrait", "GunHeavyBulletTrait", "GunChainShotTrait", "GunHomingBulletTrait"}, - GunWeapon4 = {"GunLoadedGrenadeBoostTrait", "GunLoadedGrenadeLaserTrait", "GunLoadedGrenadeSpeedTrait", "GunLoadedGrenadeWideTrait", "GunLoadedGrenadeInfiniteAmmoTrait", "GunSlowGrenade", "GunGrenadeFastTrait", "GunArmorPenerationTrait"}, -} - ---[[ -Modified version of SeedControlScreen.lua -]] -ScreenData.SeedControl = -{ - ItemStartX = 1400, - ItemStartY = 560, - ItemSpacing = 100, - EntryYSpacer = 50, - ItemsPerPage = 12, - ScrollOffset = 0, - Digits = { 1, 2, 3, 4, 5, 6 }, -} - -function OpenRngSeedSelectorScreen(screen, button) - CloseAdvancedTooltipScreen() - UseSeedController(CurrentRun.Hero) -end - -function UseSeedController( usee, args ) - PlayInteractAnimation( usee.ObjectId ) - UseableOff({ Id = usee.ObjectId }) - StopStatusAnimation( usee ) - local screen = OpenSeedControlScreen() - UseableOn({ Id = usee.ObjectId }) -end - -function OpenSeedControlScreen( args ) - - if DontGetVorimed then - EllosBoonSelectorMod.boonRarityFiltersActive = (config.boonRarityFiltersActive and not DontGetVorimed.config.Enabled) - end - - local screen = DeepCopyTable( ScreenData.SeedControl ) - screen.Components = {} - local components = screen.Components - screen.CloseAnimation = "QuestLogBackground_Out" - - OnScreenOpened({ Flag = screen.Name, PersistCombatUI = true }) - FreezePlayerUnit() - EnableShopGamepadCursor() - SetConfigOption({ Name = "FreeFormSelectWrapY", Value = false }) - SetConfigOption({ Name = "FreeFormSelectStepDistance", Value = 8 }) - SetConfigOption({ Name = "FreeFormSelectSuccessDistanceStep", Value = 8 }) - - components.ShopBackgroundDim = CreateScreenComponent({ Name = "rectangle01", Group = "Combat_Menu" }) - components.ShopBackgroundSplatter = CreateScreenComponent({ Name = "LevelUpBackground", Group = "Combat_Menu" }) - components.ShopBackground = CreateScreenComponent({ Name = "rectangle01", Group = "Combat_Menu" }) - - SetAnimation({ DestinationId = components.ShopBackground.Id, Name = "QuestLogBackground_In", OffsetY = 30 }) - - SetScale({ Id = components.ShopBackgroundDim.Id, Fraction = 4 }) - SetColor({ Id = components.ShopBackgroundDim.Id, Color = {0.090, 0.055, 0.157, 0.8} }) - - PlaySound({ Name = "/SFX/Menu Sounds/FatedListOpen" }) - - wait(0.2) - - -- Title - CreateTextBox({ Id = components.ShopBackground.Id, Text = "Weave a New Thread?", FontSize = 34, OffsetX = 0, OffsetY = -460, Color = Color.White, Font = "SpectralSCLightTitling", ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, Justification = "Center" }) - CreateTextBox({ Id = components.ShopBackground.Id, Text = "Beware, the Fates Watch On with a Single Judging Eye, your Thread in Hand", FontSize = 15, OffsetX = 0, OffsetY = -410, Width = 840, Color = {120, 120, 120, 255}, Font = "CrimsonTextItalic", ShadowBlur = 0, ShadowColor = {0,0,0,0}, ShadowOffset={0, 2}, Justification = "Center" }) - - -- Description Box - --components.DescriptionBox = CreateScreenComponent({ Name = "BlankObstacle", X = 815, Y = 290, Group = "Combat_Menu" }) - - local itemLocationX = screen.ItemStartX - local itemLocationY = screen.ItemStartY - - SeedControlScreenSyncDigits( screen ) - - for digit, digitValue in ipairs( screen.Digits ) do - components["DigitUp"..digit] = CreateScreenComponent({ Name = "ButtonCodexUp", X = itemLocationX, Y = itemLocationY - 100, Scale = 1.0, Sound = "/SFX/Menu Sounds/GeneralWhooshMENU", Group = "Combat_Menu" }) - components["DigitUp"..digit].OnPressedFunctionName = "SeedDigitDown" - components["DigitUp"..digit].ControlHotkey = "MenuLeft" - components["DigitUp"..digit].Digit = digit - - local digitKey = "DigitButton"..digit - components[digitKey] = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1, X = itemLocationX, Y = itemLocationY, Group = "Combat_Menu" }) - components[digitKey].Digit = digit - AttachLua({ Id = components[digitKey].Id, Table = components[digitKey] }) - - CreateTextBox({ Id = components[digitKey].Id, - Text = digitValue, - Color = {245, 200, 47, 255}, - FontSize = 48, - OffsetX = 0, OffsetY = 0, - Font = "AlegreyaSansSCBold", - OutlineThickness = 0, - OutlineColor = {255, 205, 52, 255}, - ShadowBlur = 0, ShadowColor = {0,0,0,0}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - components["DigitDown"..digit] = CreateScreenComponent({ Name = "ButtonCodexDown", X = itemLocationX, Y = itemLocationY + 100, Scale = 1.0, Sound = "/SFX/Menu Sounds/GeneralWhooshMENU", Group = "Combat_Menu" }) - components["DigitDown"..digit].OnPressedFunctionName = "SeedDigitUp" - components["DigitDown"..digit].ControlHotkey = "MenuRight" - components["DigitDown"..digit].Digit = digit - - itemLocationX = itemLocationX - screen.ItemSpacing - end - - UpdateDigitDisplay( screen ) - - -- Randomize button - components.RandomizeButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 1150, Y = 760 }) - components.RandomizeButton.OnPressedFunctionName = "SeedControlScreenRandomize" - CreateTextBox({ Id = components.RandomizeButton.Id, - Text = "Randomize Seed", - OffsetX = 0, OffsetY = 0, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - -- Roll seed for filters button - components.ClearFiltersButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 320, Y = 810 }) - components.ClearFiltersButton.OnPressedFunctionName = "ClearFilters" - CreateTextBox({ Id = components.ClearFiltersButton.Id, - Text = "Clear Filters", - OffsetX = 0, OffsetY = 0, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - -- Roll seed for filters button - components.RollSeedForFiltersButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu", X = 625, Y = 810 }) - components.RollSeedForFiltersButton.OnPressedFunctionName = "RollSeedForFilters" - CreateTextBox({ Id = components.RollSeedForFiltersButton.Id, - Text = "Roll Seed For Filters", - OffsetX = 0, OffsetY = 0, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - components.FiltersTitle = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 475, Y = 520 }) - local displayText = "Filter for a Specific God" - if EllosBoonSelectorMod.boonRarityFiltersActive then - displayText = displayText.." and Boon" - end - CreateTextBox({ Id = components.FiltersTitle.Id, - Text = displayText, - OffsetX = 0, OffsetY = -100, - FontSize = 28, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - -- God filter buttons - local x = -250 - local y = 30 - for _, god in pairs(EllosBoonSelectorMod.BoonGods) do - components[god .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.5, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) - components[god .. "Filter"].OnPressedFunctionName = "ToggleGodFilter" - components[god .. "Filter"].GodName = god - CreateTextBox({ Id = components[god .. "Filter"].Id, - Text = god, - OffsetX = 0, OffsetY = 0, - FontSize = 16, - Color = Color.BoonPatchCommon, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - x = x + 150 - if x > 250 then - x = -250 - y = y + 50 - end - end - - if EllosBoonSelectorMod.config.chaosFiltersActive then - -- Chaos filter buttons - x = -225 - y = 290 - for _, location in pairs(EllosBoonSelectorMod.ChaosLocations) do - components[location .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.66, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) - components[location .. "Filter"].OnPressedFunctionName = "ToggleChaosFilter" - components[location .. "Filter"].ChaosLocation = location - CreateTextBox({ Id = components[location .. "Filter"].Id, - Text = location, - OffsetX = 0, OffsetY = 0, - FontSize = 16, - Color = Color.BoonPatchCommon, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - x = x + 200 - end - end - - -- Rarity filter buttons - x = -250 - y = 150 - if EllosBoonSelectorMod.boonRarityFiltersActive then - for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do - components[priorityBoon .. "Filter"] = CreateScreenComponent({ Name = "ButtonDefault", Scale = 0.5, Group = "Combat_Menu", X = 500 + x, Y = 450 + y }) - components[priorityBoon .. "Filter"].OnPressedFunctionName = "CycleRarityFilter" - components[priorityBoon .. "Filter"].PriorityBoon = EllosBoonSelectorMod.PriorityBoonCannonicalNameToCodeName[priorityBoon] - CreateTextBox({ Id = components[priorityBoon .. "Filter"].Id, - Text = priorityBoon, - OffsetX = 0, OffsetY = 0, - FontSize = 16, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - x = x + 150 - if x > 250 then - x = -250 - y = y + 60 - end - end - end - - -- Hammer filter button - x = 480 - y = 675 - - components["HammerFilterLeft"] = CreateScreenComponent({ Name = "ButtonCodexDown", X = x - 200, Y = y, Scale = 1.0, Group = "Combat_Menu" }) - SetAngle({ Id = components["HammerFilterLeft"].Id, Angle = -90 }) - SetScale({ Id = components["HammerFilterLeft"].Id, Fraction = .8 }) - components["HammerFilterLeft"].OnPressedFunctionName = "HammerFilterLeft" - components["HammerFilterRight"] = CreateScreenComponent({ Name = "ButtonCodexDown", X = x + 200, Y = y, Scale = 1.0, Group = "Combat_Menu" }) - SetAngle({ Id = components["HammerFilterRight"].Id, Angle = 90 }) - SetScale({ Id = components["HammerFilterRight"].Id, Fraction = .8 }) - components["HammerFilterRight"].OnPressedFunctionName = "HammerFilterRight" - - components["HammerFilter"] = CreateScreenComponent({ Name = "BlankObstacle", Scale = 0.5, Group = "Combat_Menu", X = x, Y = y }) - EllosBoonSelectorMod.CurrentHammerIndex = 1 - - local weapon = GetEquippedWeapon() - local aspectIndex = GetEquippedWeaponTraitIndex( weapon ) - local hammerIndex = EllosBoonSelectorMod.CurrentHammerIndex - CreateTextBox({ Id = components["HammerFilter"].Id, - Text = EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex][hammerIndex], - OffsetX = 0, OffsetY = 0, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - - -- Icon Display - components.GodIcon = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 550, Y = 665 }) - Attach({ Id = components.GodIcon.Id, DestinationId = components.ShopBackground.Id, OffsetX = 0, OffsetY = 0}) - components.Reward1 = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.Reward1.Id, - Text = "", - OffsetX = 400, OffsetY = -50, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - components.Reward2 = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.Reward2.Id, - Text = "", - OffsetX = 400, OffsetY = -20, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - components.Reward3 = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.Reward3.Id, - Text = "", - OffsetX = 400, OffsetY = 10, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - if EllosBoonSelectorMod.config.showChaosPreview then - components.ChaosRoomIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.ChaosRoomIndicator.Id, - Text = "", - OffsetX = 400, OffsetY = 40, - FontSize = 22, - Color = Color.Purple, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - end - if EllosBoonSelectorMod.config.showNextRoomPreview then - components.NextRoomNameIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.NextRoomNameIndicator.Id, - Text = "", - OffsetX = 400, OffsetY = 70, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - components.NextRoomRewardIndicator = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1.0, Group = "Combat_Menu", X = 1200, Y = 650 }) - CreateTextBox({ Id = components.NextRoomRewardIndicator.Id, - Text = "", - OffsetX = 400, OffsetY = 100, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - end - - local roomReward = PredictStartingRoomReward((NextSeeds[1] or 000000)) - UpdateRewardPreview( screen, roomReward ) - ClearFilters(screen) - - -- Close button - components.CloseButton = CreateScreenComponent({ Name = "ButtonClose", Scale = 0.7, Group = "Combat_Menu" }) - Attach({ Id = components.CloseButton.Id, DestinationId = components.ShopBackground.Id, OffsetX = -6, OffsetY = 456 }) - components.CloseButton.OnPressedFunctionName = "CloseSeedControlScreen" - components.CloseButton.ControlHotkey = "Cancel" - - wait(0.1) - --TeleportCursor({ OffsetX = screen.ItemStartX - 30, OffsetY = screen.ItemStartY, ForceUseCheck = true }) - - screen.KeepOpen = true - thread( HandleWASDInput, screen ) - HandleScreenInput( screen ) -end - -function DoesRewardMatchFilters(roomReward) - if EllosBoonSelectorMod.ChaosFilter == "No Chaos" and (roomReward.FirstRoomChaos or roomReward.SecondRoomChaos) then - return false - elseif EllosBoonSelectorMod.ChaosFilter == "Room 1 Chaos" and not roomReward.FirstRoomChaos then - return false - elseif EllosBoonSelectorMod.ChaosFilter == "Room 2 Chaos" and not roomReward.SecondRoomChaos then - return false - end - - local targetReward = "Boon" - - if EllosBoonSelectorMod.GodFilter == "" and next(EllosBoonSelectorMod.RarityFilter) == nil then - targetReward = "Hammer" - end - - if roomReward.Type == "Boon" then - -- If looking for a hammer, return false - if targetReward == "Hammer" then - return false - end - - -- God must match filtered god if specified - if EllosBoonSelectorMod.GodFilter ~= "" and EllosBoonSelectorMod.GodFilter ~= roomReward.BoonData.God then - return false - end - - -- Rarities much match all active rarity filters - for priorityBoon, rarityFilter in pairs(EllosBoonSelectorMod.RarityFilter) do - local thisFilterPassed = false; - for index, boonOption in ipairs(roomReward.BoonData.Options) do - if boonOption.Blocked == nil then - if rarityFilter == nil or rarityFilter == 0 then - thisFilterPassed = true - elseif rarityFilter > 0 and priorityBoon == PriorityBoonType(boonOption.Boon) and rarityFilter <= boonOption.Rarity then - thisFilterPassed = true - end - end - end - -- If any rarity filter fails, return false - if not thisFilterPassed then - return false - end - end - - -- All filters passed - return true - elseif roomReward.Type == "Hammer" then - -- If looking for a boon, return false - if targetReward == "Boon" then - return false - end - - local weapon = GetEquippedWeapon() - local aspectIndex = GetEquippedWeaponTraitIndex( weapon ) - local hammerIndex = EllosBoonSelectorMod.CurrentHammerIndex - - for index, hammerOption in ipairs(roomReward.HammerData.Options) do - if hammerOption.Blocked == nil then - if hammerOption.Name == EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex][hammerIndex] then - return true - end - end - end - - return false - end -end - -function RollSeedForFilters( screen, button ) - local roomReward = nil - local counter = 0 - local seed = (NextSeeds[1] or 000000) - repeat - roomReward = PredictStartingRoomReward(seed + counter) - counter = counter + 1 - - -- Cool spinner visual, but searches much slower - --wait(.05) - --UpdateRewardPreview( screen, roomReward ) - --SeedControlScreenSyncDigits( screen ) - --UpdateDigitDisplay( screen ) - until DoesRewardMatchFilters(roomReward) or counter > 1000 - - if counter > 1000 then - -- TODO: tell the user we failed to find a seed - end - - UpdateRewardPreview( screen, roomReward ) - SeedControlScreenSyncDigits( screen ) - UpdateDigitDisplay( screen ) -end - -function ClearFilters ( screen, button ) - EllosBoonSelectorMod.GodFilter = "" - EllosBoonSelectorMod.ChaosFilter = "" - EllosBoonSelectorMod.RarityFilter = {} - - -- TODO: Use a constant for the priority boons so we don't hardcode them in multiple places - for _, god in pairs(EllosBoonSelectorMod.BoonGods) do - ModifyTextBox({ Id = screen.Components[god .. "Filter"].Id, Color = Color.BoonPatchCommon }) - end - if EllosBoonSelectorMod.config.chaosFiltersActive then - for _, location in pairs(EllosBoonSelectorMod.ChaosLocations) do - ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.BoonPatchCommon }) - end - end - if EllosBoonSelectorMod.boonRarityFiltersActive then - for _, priorityBoon in pairs({"Attack", "Special", "Dash", "Cast"}) do - ModifyTextBox({ Id = screen.Components[priorityBoon .. "Filter"].Id, Color = Color.BoonPatchCommon }) - end - end -end - -function ToggleGodFilter ( screen, button ) - local godName = button.GodName - - EllosBoonSelectorMod.GodFilter = godName - - for _, god in pairs(EllosBoonSelectorMod.BoonGods) do - if god == godName and EllosBoonSelectorMod.GodFilter then - ModifyTextBox({ Id = screen.Components[god .. "Filter"].Id, Color = Color.BoonPatchCommon }) - else - ModifyTextBox({ Id = screen.Components[god .. "Filter"].Id, Color = Color.Gray }) - end - end -end - -function ToggleChaosFilter( screen, button ) - local chaosLocation = button.ChaosLocation - EllosBoonSelectorMod.ChaosFilter = chaosLocation - - for _, location in pairs(EllosBoonSelectorMod.ChaosLocations) do - if location == chaosLocation then - ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.BoonPatchCommon }) - else - ModifyTextBox({ Id = screen.Components[location .. "Filter"].Id, Color = Color.Gray }) - end - end -end - -function CycleRarityFilter ( screen, button ) - local priorityBoon = button.PriorityBoon - - local currentFilterLevel = (EllosBoonSelectorMod.RarityFilter[priorityBoon] or 0) - currentFilterLevel = (currentFilterLevel + 1) % 3 - EllosBoonSelectorMod.RarityFilter[priorityBoon] = currentFilterLevel - - -- CurrentFilterLevel goes from 0 to 2, add 1 to map to rarity values - ModifyTextBox({ Id = button.Id, Color = EllosBoonSelectorMod.RarityColors[currentFilterLevel + 1] }) -end - -function HammerFilterLeft ( screen, button ) - local weapon = GetEquippedWeapon() - local aspectIndex = GetEquippedWeaponTraitIndex( weapon ) - local hammerIndex = EllosBoonSelectorMod.CurrentHammerIndex - - hammerIndex = hammerIndex - 1 - if hammerIndex == 0 then - hammerIndex = TableLength(EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex]) - end - EllosBoonSelectorMod.CurrentHammerIndex = hammerIndex - ModifyTextBox({Id = screen.Components["HammerFilter"].Id, Text = EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex][hammerIndex]}) -end - -function HammerFilterRight ( screen, button ) - local weapon = GetEquippedWeapon() - local aspectIndex = GetEquippedWeaponTraitIndex( weapon ) - local hammerIndex = EllosBoonSelectorMod.CurrentHammerIndex - - hammerIndex = hammerIndex + 1 - if hammerIndex == TableLength(EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex]) + 1 then - hammerIndex = 1 - end - EllosBoonSelectorMod.CurrentHammerIndex = hammerIndex - ModifyTextBox({Id = screen.Components["HammerFilter"].Id, Text = EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex][hammerIndex]}) -end - -function SeedDigitUp( screen, button ) - local newDigitValue = screen.Digits[button.Digit] - newDigitValue = newDigitValue - 1 - if newDigitValue < 0 then - newDigitValue = 9 - end - screen.Digits[button.Digit] = newDigitValue - local newSeed = 0 - for digit, digitValue in ipairs( screen.Digits ) do - newSeed = newSeed + (digitValue * math.pow(10, digit - 1)) - end - local roomReward = PredictStartingRoomReward(newSeed) - UpdateRewardPreview( screen, roomReward ) - UpdateDigitDisplay( screen ) -end - -function SeedDigitDown( screen, button ) - local newDigitValue = screen.Digits[button.Digit] - newDigitValue = newDigitValue + 1 - if newDigitValue > 9 then - newDigitValue = 0 - end - screen.Digits[button.Digit] = newDigitValue - local newSeed = 0 - for digit, digitValue in ipairs( screen.Digits ) do - newSeed = newSeed + (digitValue * math.pow(10, digit - 1)) - end - local roomReward = PredictStartingRoomReward(newSeed) - UpdateRewardPreview( screen, roomReward ) - UpdateDigitDisplay( screen ) -end - -function SeedControlScreenSyncDigits( screen ) - local displayNumber = (NextSeeds[1] or 000000) - if displayNumber ~= nil then - for digit = 1, #screen.Digits do - local digitValue = displayNumber % 10 - screen.Digits[digit] = digitValue - displayNumber = math.floor( displayNumber / 10 ) - end - end -end - -function UpdateDigitDisplay( screen ) - for digit, digitValue in ipairs( screen.Digits ) do - local digitKey = "DigitButton"..digit - ModifyTextBox({ Id = screen.Components["DigitButton"..digit].Id, Text = digitValue }) - end -end - -function SeedControlScreenRandomize( screen, button ) - local newSeed = RandomInt(0, 999999) - local roomReward = PredictStartingRoomReward(newSeed) - UpdateRewardPreview( screen, roomReward ) - SeedControlScreenSyncDigits( screen ) - UpdateDigitDisplay( screen ) -end - -function CloseSeedControlScreen( screen, button ) - - local newSeed = 0 - local place = 1 - for digit, digitValue in ipairs( screen.Digits ) do - newSeed = newSeed + (digitValue * math.pow(10, digit - 1)) - end - - DisableShopGamepadCursor() - SetConfigOption({ Name = "FreeFormSelectWrapY", Value = false }) - SetConfigOption({ Name = "FreeFormSelectStepDistance", Value = 16 }) - SetConfigOption({ Name = "FreeFormSelectSuccessDistanceStep", Value = 8 }) - SetAnimation({ DestinationId = screen.Components.ShopBackground.Id, Name = screen.CloseAnimation }) - PlaySound({ Name = "/SFX/Menu Sounds/FatedListClose" }) - CloseScreen( GetAllIds( screen.Components ), 0.1 ) - UnfreezePlayerUnit() - screen.KeepOpen = false - OnScreenClosed({ Flag = screen.Name }) -end - -function IsSecondRoomRewardEligible(requirements, firstRoomReward) - if requirements.RequiredUpgradeableGodTraits ~= nil and requirements.RequiredUpgradeableGodTraits >= 1 and firstRoomReward ~= "Boon" then - return false - end - if requirements.RequiredMaxWeaponUpgrades ~= nil and requirements.RequiredMaxWeaponUpgrades < 1 and firstRoomReward == "Hammer" then - return false - end - if requirements.RequiredMinDepth ~= nil and requirements.RequiredMinDepth >= 2 then - return false - end - if requirements.RequiredMinBiomeDepth ~= nil and requirements.RequiredMinBiomeDepth >= 2 then - return false - end - if requirements.RequiredFalseCosmetics ~= nil then - return false - end - return true -end - -function PredictSecondRoomReward(seedForPrediction, firstRoomReward, firstRoomShrine, secondRoomName) - RandomSetNextInitSeed( {Seed = seedForPrediction} ) - local rewardStore = "MetaProgress" - if firstRoomShrine or secondRoomName == "RoomSimple01" then - rewardStore = "RunProgress" - end - local eligibleRewards = {} - for key, reward in pairs(RewardStoreData[rewardStore]) do - if IsSecondRoomRewardEligible(reward.GameStateRequirements, firstRoomReward) then - table.insert(eligibleRewards, key) - end - end - RandomSynchronize(4) - local selectedKey = GetRandomValue( eligibleRewards ) - local reward = RewardStoreData[rewardStore][selectedKey].Name - RemoveValueAndCollapse( eligibleRewards, selectedKey ) - local eligibleGods = DeepCopyTable(EllosBoonSelectorMod.BoonGods) - if reward == "Boon" then - reward = GetRandomValue( eligibleGods ) - RemoveValueAndCollapse( eligibleGods, reward ) - end - if firstRoomShrine then - -- roll for the actual exit second - RandomSynchronize(4) - selectedKey = GetRandomValue( eligibleRewards ) - reward = RewardStoreData.RunProgress[selectedKey].Name - if reward == "Boon" then - reward = GetRandomValue( eligibleGods ) - end - end - return reward -end - ---- Takes in a seed and returns the predicted RoomReward object for the starting room. --- TODO: Update this to use a separate RNG entirely rather than using the current RNG and putting it back again --- --- @param int seedForPrediction RNG seed to use when predicting the rng calls --- @param int currentSeed RNG seed to reset to after making the predictions (Optional) --- @return RoomReward where RoomReward has a {Type} ("Hammer" or "Boon") and the corresponding Data e.g. {BoonData} or {HammerData} -function PredictStartingRoomReward( seedForPrediction, currentSeed ) - local roomReward = {} - - local roomRewardType = PredictRoomRewardType(seedForPrediction, currentSeed) - roomReward.Type = roomRewardType.Name - - if roomReward.Type == "Boon" then - roomReward.BoonData = {} - roomReward.BoonData.God = roomRewardType.God - - -- Get exact boon rewards and rarity and update the menu - roomReward.BoonData.Options = PredictStartingGodBoonOptions( roomReward.BoonData.God, seedForPrediction, currentSeed ) - roomReward.FirstRoomChaos = PredictChaos(6, seedForPrediction) - if roomReward.FirstRoomChaos then - roomReward.FirstRoomShrine = false - else - roomReward.FirstRoomShrine = PredictChaos(7, seedForPrediction) - end - elseif roomReward.Type == "Hammer" then - roomReward.HammerData = {} - local currentWeapon = GetEquippedWeapon() - roomReward.HammerData.Options = PredictHammerOptionsForWeapon(currentWeapon, GetEquippedWeaponTraitIndex( currentWeapon ), seedForPrediction, currentSeed) - roomReward.FirstRoomChaos = PredictChaos(5, seedForPrediction) - - if roomReward.FirstRoomChaos then - roomReward.FirstRoomShrine = false - else - roomReward.FirstRoomShrine = PredictChaos(6, seedForPrediction) - end - end - - if roomReward.FirstRoomChaos then - roomReward.SecondRoomChaos = false - else - roomReward.SecondRoomChaos = PredictChaos(2, seedForPrediction) - end - - RandomSynchronize(1) -- Known offset at which the RNG rolls the room - roomReward.SecondRoomName = GetRandomValue({ - "A_Combat01", - "A_Combat02", - "A_Combat03", - "A_Combat04", - "A_Combat05", - "A_Combat06", - "A_Combat07", - "A_Combat08A", - "A_Combat09", - "A_Combat10", - "A_Combat12", - "A_Combat13", - "A_Combat14", - "A_Combat15", - "A_Combat16", - "A_Combat19", - "A_Combat21", - "A_Combat24", - "RoomSimple01" - }) - roomReward.SecondRoomReward = PredictSecondRoomReward( - seedForPrediction, - roomReward.Type, - roomReward.FirstRoomShrine, - roomReward.SecondRoomName) - return roomReward -end - -function GetBlockedIndicesForAP() - -- Resync and calculate blocked indices from AP - RandomSynchronize(2) - local blockedIndices = {} - for i = 1, 3 do - table.insert( blockedIndices, i ) - end - for i = 1, CalcNumLootChoices() do - RemoveRandomValue( blockedIndices ) - end - return blockedIndices -end - -function GetChaosChance() - -- TODO: How to retrieve room date for a future room? Just need tile name? - --local secretChance = room.SecretSpawnChance or RoomData.BaseRoom.SecretSpawnChance - local secretChance = RoomData.BaseRoom.SecretSpawnChance - for k, mutator in pairs( GameState.ActiveMutators ) do - if mutator.SecretSpawnChanceMultiplier ~= nil then - secretChance = secretChance * mutator.SecretSpawnChanceMultiplier - end - end - return 0.15 -end - -function PredictFirstRoomChaos(seedForPrediction, currentSeed) - RandomSetNextInitSeed( {Seed = seedForPrediction} ) - RandomSynchronize(5) -- Known offset at which the RNG rolls reward type - local chaosPresent = RandomChance(GetChaosChance()) - - -- Reset RNG to the pre-call state - if currentSeed ~= nil then - RandomSetNextInitSeed( {Seed = currentSeed} ) - end - - return chaosPresent -end - -function PredictChaos(offset, seedForPrediction, currentSeed) - RandomSetNextInitSeed( {Seed = seedForPrediction} ) - RandomSynchronize(offset) -- Known offset at which the RNG rolls reward type - local chaosPresent = RandomChance(GetChaosChance()) - - -- Reset RNG to the pre-call state - if currentSeed ~= nil then - RandomSetNextInitSeed( {Seed = currentSeed} ) - end - - return chaosPresent -end - ---- Takes in a seed and returns the predicted RoomRewardType object. --- TODO: Update this to use a separate RNG entirely rather than using the current RNG and putting it back again --- --- @param int seedForPrediction RNG seed to use when predicting the rng calls --- @param int currentSeed RNG seed to reset to after making the predictions (Optional) --- @return RoomRewardType where RoomRewardType has a {Name} ("Hammer" or "Boon") and conditionally a {God} -function PredictRoomRewardType( seedForPrediction, currentSeed ) - local roomRewardType = {} - - -- Save off the current RNG the load the provided seed and offset - RandomSetNextInitSeed( {Seed = seedForPrediction} ) - RandomSynchronize(4) -- Known offset at which the RNG rolls reward type - roomRewardType.Name = GetRandomValue({"Boon", "Boon", "Boon", "Hammer"}) - - if roomRewardType.Name == "Boon" then - local godOptions = DeepCopyTable(EllosBoonSelectorMod.BoonGods) - local rarityTraits = GetHeroTraitValues("RarityBonus", { UnlimitedOnly = false }) - local god = "" - for i, rarityTraitData in pairs(rarityTraits) do - if rarityTraitData.RequiredGod ~= nil and rarityTraitData.RequiredGod ~= "TrialUpgrade" then - god = string.sub(rarityTraitData.RequiredGod, 1, -8) - end - end - if god == "" then - god = RemoveRandomValue(godOptions) - end - roomRewardType.God = god -- God RNG roll will happen at the next offset - end - - -- Reset RNG to the pre-call state - if currentSeed ~= nil then - RandomSetNextInitSeed( {Seed = currentSeed} ) - end - - return roomRewardType -end - ---- Takes in a weapon name and seed and returns a list of HammerOption objects. --- TODO: Update this to use a separate RNG entirely rather than using the current RNG and putting it back again --- --- @param string weapon The name of the weapon used to find the eligible hammers --- @param int aspectIndex Index corrseponding to the aspect of the weapon (e.g. 1-4 in order of unlock) --- @param int seedForPrediction RNG seed to use when predicting the rng calls --- @param int currentSeed RNG seed to reset to after making the predictions (Optional) --- @return list where HammerOption has a {Name} -function PredictHammerOptionsForWeapon( weapon, aspectIndex, seedForPrediction, currentSeed ) - local hammerOptions = {} - - -- Save off the current RNG the load the provided seed and offset - RandomSetNextInitSeed( {Seed = seedForPrediction} ) - local blockedIndices = GetBlockedIndicesForAP() - RandomSynchronize(1) -- Offset that when the hammer options start being rolled at - - local eligibleHammers = DeepCopyTable(EllosBoonSelectorMod.HammerOptions[weapon .. aspectIndex]) - - local selectedIndexes = TableLength( eligibleHammers ) - for index = 1, 3 do - hammerOptions[index] = {} - local selectedHammer = GetRandomValue(eligibleHammers) - hammerOptions[index].Name = selectedHammer - - -- Remove the picked hammer from the pool of eligible Hammers - for i, hammer in ipairs(eligibleHammers) do - if hammer == selectedHammer then - table.remove(eligibleHammers, i) - break - end - end - - end - - -- Reset RNG to the pre-call state - if currentSeed ~= nil then - RandomSetNextInitSeed( {Seed = currentSeed} ) - end - - for _ , value in pairs(blockedIndices) do - hammerOptions[value].Blocked = true - end - - return hammerOptions -end - -function PriorityBoonType(boon) - for _, boonType in pairs(EllosBoonSelectorMod.PriorityBoons) do - if string.find(boon, boonType) then - return boonType - end - end - return "No boon type found." -end - ---- Takes in a gods name and seed and returns a list of BoonOption objects for the starting room only. --- TODO: Update this to use a separate RNG entirely rather than using the current RNG and putting it back again --- --- @param string god The god for which the boon options should be predicted --- @param int seedForPrediction RNG seed to use when predicting the rng calls --- @param int currentSeed RNG seed to reset to after making the predictions (Optional) --- @return list where BoonOption has a {God}, {Name} and {Rarity} (numeric) -function PredictStartingGodBoonOptions( god, seedForPrediction, currentSeed ) - local startingBoons = {} - - -- Save off the current RNG the load the provided seed and offset - RandomSetNextInitSeed( {Seed = seedForPrediction} ) - local blockedIndices = GetBlockedIndicesForAP() - RandomSynchronize(1) -- Offset that when the boons start being rolled at - - -- Checking for Aspect of Beowulf for cast changes, ignoring poseidon/dionysus - local beowulfFlag = HeroHasTrait("ShieldLoadAmmoTrait") and god ~= "Poseidon" and god ~= "Dionysus" - - -- First room always offers 3 priority boons (selected by excluding one of the 4 options) - local boonRewards = DeepCopyTable(EllosBoonSelectorMod.PriorityBoons) - - for i, boonReward in ipairs(boonRewards) do - if boonReward == "RangedTrait" and beowulfFlag then - boonRewards[3] = "ShieldLoadAmmo_"..god..boonReward - else - boonRewards[i] = god..boonReward - end - end - - - local excluded = RemoveRandomValue(boonRewards) - boonRewards = CollapseTableOrdered(boonRewards) - - local rarities = ElloGetBoonRarityChances(god) - for index = 1, 3 do - startingBoons[index] = {} - startingBoons[index].Boon = boonRewards[index] - RandomChance(0) -- Legendary isn't possible for starting boons - if RandomChance(rarities.Epic) then -- Skip Heroic because starting boons can't naturally roll Heroic - startingBoons[index].Rarity = 2 - elseif RandomChance(rarities.Rare) then - startingBoons[index].Rarity = 1 - else - startingBoons[index].Rarity = 0 - end - end - - -- Reset RNG to the pre-call state - if currentSeed ~= nil then - RandomSetNextInitSeed( {Seed = currentSeed} ) - end - - table.sort( startingBoons, function(boon1, boon2) return EllosBoonSelectorMod.PriorityBoonsOrder[PriorityBoonType(boon1.Boon)] < EllosBoonSelectorMod.PriorityBoonsOrder[PriorityBoonType(boon2.Boon)] end) - - for key, value in pairs(blockedIndices) do - startingBoons[value].Blocked = true - end - return startingBoons -end - -function ElloGetBoonRarityChances( godName, roomRarityOverride ) - local name = godName - --local ignoreTempRarityBonus = args.IgnoreTempRarityBonus - local referencedTable = "BoonData" - -- "HermesUpgrade" then referencedTable = "HermesData" - - local legendaryRoll = CurrentRun.Hero[referencedTable].LegendaryChance or 0 - local heroicRoll = CurrentRun.Hero[referencedTable].HeroicChance or 0 - local epicRoll = CurrentRun.Hero[referencedTable].EpicChance or 0 - local rareRoll = CurrentRun.Hero[referencedTable].RareChance or 0 - - if roomRarityOverride ~= nil then - legendaryRoll = roomRarityOverride.Legendary or legendaryRoll - heroicRoll = roomRarityOverride.Heroic or heroicRoll - epicRoll = roomRarityOverride.EpicChance or epicRoll - rareRoll = roomRarityOverride.RareChance or rareRoll - end - - local metaupgradeRareBoost = GetNumMetaUpgrades( "RareBoonDropMetaUpgrade" ) * ( MetaUpgradeData.RareBoonDropMetaUpgrade.ChangeValue - 1 ) - local metaupgradeEpicBoost = GetNumMetaUpgrades( "EpicBoonDropMetaUpgrade" ) * ( MetaUpgradeData.EpicBoonDropMetaUpgrade.ChangeValue - 1 ) + GetNumMetaUpgrades( "EpicHeroicBoonMetaUpgrade" ) * ( MetaUpgradeData.EpicBoonDropMetaUpgrade.ChangeValue - 1 ) - local metaupgradeLegendaryBoost = GetNumMetaUpgrades( "DuoRarityBoonDropMetaUpgrade" ) * ( MetaUpgradeData.EpicBoonDropMetaUpgrade.ChangeValue - 1 ) - local metaupgradeHeroicBoost = GetNumMetaUpgrades( "EpicHeroicBoonMetaUpgrade" ) * ( MetaUpgradeData.EpicBoonDropMetaUpgrade.ChangeValue - 1 ) - legendaryRoll = legendaryRoll + metaupgradeLegendaryBoost - heroicRoll = heroicRoll + metaupgradeHeroicBoost - rareRoll = rareRoll + metaupgradeRareBoost - epicRoll = epicRoll + metaupgradeEpicBoost - - local rarityTraits = GetHeroTraitValues("RarityBonus", { UnlimitedOnly = false }) - for i, rarityTraitData in pairs(rarityTraits) do - if rarityTraitData.RequiredGod == nil or rarityTraitData.RequiredGod == name .. "Upgrade" then - if rarityTraitData.RareBonus then - rareRoll = rareRoll + rarityTraitData.RareBonus - end - if rarityTraitData.EpicBonus then - epicRoll = epicRoll + rarityTraitData.EpicBonus - end - if rarityTraitData.HeroicBonus then - heroicRoll = heroicRoll + rarityTraitData.HeroicBonus - end - if rarityTraitData.LegendaryBonus then - legendaryRoll = legendaryRoll + rarityTraitData.LegendaryBonus - end - end - end - return - { - Rare = rareRoll, - Epic = epicRoll, - Heroic = heroicRoll, - Legendary = legendaryRoll, - } -end - -function UpdateRewardPreview( screen, roomReward ) - if config.showPreview == false then - return - end - if roomReward.Type == "Boon" then - SetAnimation({ Name = "BoonSymbol" .. roomReward.BoonData.God .. "Isometric", DestinationId = screen.Components.GodIcon.Id, OffsetX = 640, OffsetY = -45}) - - local roomRewardOptions = roomReward.BoonData.Options - for index, boonOption in ipairs(roomRewardOptions) do - local color = EllosBoonSelectorMod.RarityColors[boonOption.Rarity + 1] - if boonOption.Blocked == true then - color = Color.Red - end - ModifyTextBox({ Id = screen.Components["Reward" .. index].Id, Text = boonOption.Boon, Color = color}) - end - elseif roomReward.Type == "Hammer" then - SetAnimation({ Name = "WeaponUpgradePreview", DestinationId = screen.Components.GodIcon.Id, OffsetX = 640, OffsetY = -45}) - local hammerOptions = roomReward.HammerData.Options - - for index = 1, 3 do - local color = Color.BoonPatchCommon - if hammerOptions[index].Blocked == true then - color = Color.Red - end - ModifyTextBox({ Id = screen.Components["Reward" .. index].Id, Text = hammerOptions[index].Name, Color = color }) - end - end - - if EllosBoonSelectorMod.config.showChaosPreview then - if roomReward.FirstRoomChaos then - ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "Room 1 Chaos" }) - elseif roomReward.SecondRoomChaos then - ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "Room 2 Chaos" }) - else - ModifyTextBox({ Id = screen.Components.ChaosRoomIndicator.Id, Text = "No Chaos" }) - end - end - if EllosBoonSelectorMod.config.showNextRoomPreview then - ModifyTextBox({ Id = screen.Components.NextRoomNameIndicator.Id, Text = roomReward.SecondRoomName}) - ModifyTextBox({ Id = screen.Components.NextRoomRewardIndicator.Id, Text = roomReward.SecondRoomReward}) - end -end - --- Convenient place to add a button to the AdvancedTooltipScreen -ModUtil.WrapBaseFunction("CreatePrimaryBacking", function ( baseFunc ) - local components = ScreenAnchors.TraitTrayScreen.Components - - -- Add button for RNG seed select menu but only between runs - if ModUtil.PathGet("CurrentDeathAreaRoom") then - components.RngSeedButton = CreateScreenComponent({ Name = "ButtonDefault", Scale = 1.0, Group = "Combat_Menu_TraitTray", X = CombatUI.TraitUIStart + 105, Y = 930 }) - components.RngSeedButton.OnPressedFunctionName = "OpenRngSeedSelectorScreen" - CreateTextBox({ Id = components.RngSeedButton.Id, - Text = "Set Starting Boon", - OffsetX = 0, OffsetY = 0, - FontSize = 22, - Color = Color.White, - Font = "AlegreyaSansSCRegular", - ShadowBlur = 0, ShadowColor = {0,0,0,1}, ShadowOffset={0, 2}, - Justification = "Center", - DataProperties = - { - OpacityWithOwner = true, - }, - }) - Attach({ Id = components.RngSeedButton.Id, DestinationId = components.RngSeedButton, OffsetX = 500, OffsetY = 500 }) - end - baseFunc() -end, EllosBoonSelectorMod) \ No newline at end of file diff --git a/EllosStartingBoonSelectorMod/modfile.txt b/EllosStartingBoonSelectorMod/modfile.txt deleted file mode 100644 index 1c61242..0000000 --- a/EllosStartingBoonSelectorMod/modfile.txt +++ /dev/null @@ -1,2 +0,0 @@ -:: Ello's Boon Selector Mod v1.1 -Import "Scripts" From c0c97ccf99ddb83322a596b1e1549d78506dbf05 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 14:30:11 -0400 Subject: [PATCH 33/77] DontGetVorimed: Update submodule reference --- .gitmodules | 6 +++--- DontGetVorimed | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) create mode 160000 DontGetVorimed diff --git a/.gitmodules b/.gitmodules index c5a47b5..bd633ee 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,9 +4,9 @@ [submodule "sgg-mod-modutil"] path = sgg-mod-modutil url = https://github.com/SGG-Modding/sgg-mod-modutil.git -[submodule "DontGetVorimed"] - path = DontGetVorimed - url = https://github.com/cgullsHadesMods/DontGetVorimed [submodule "HellModeToggle"] path = HellModeToggle url = https://github.com/cgullsHadesMods/HellModeToggle +[submodule "DontGetVorimed"] + path = DontGetVorimed + url = https://github.com/cgullsHadesMods/DontGetVorimed diff --git a/DontGetVorimed b/DontGetVorimed new file mode 160000 index 0000000..a3e9d1c --- /dev/null +++ b/DontGetVorimed @@ -0,0 +1 @@ +Subproject commit a3e9d1c5fafbaf1f681809321baa2deec190457b From 0b560dc436a4094398bc090a80180ce53a627844 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 14:33:12 -0400 Subject: [PATCH 34/77] Changing ruleset text to be clearer --- HadesSpeedrunningModpack/SettingsHashMenu.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index 7c7f9ee..fbf27ca 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -156,7 +156,7 @@ function HSMConfigMenu.CreateSettingsHashMenu( screen ) Font = "AlegrayaSansSCRegular", Items = { ["Default"] = {Text = "Select a Preset", event = function() end}, - {Text = "Multi-Run Leaderboard Ruleset", event = function() + {Text = "Real Time (RTA) Ruleset", event = function() HSMConfigMenu.LoadSettings("MultiRunSettings") local rulesetHashInt = CalculateHash(HSMConfigMenu.MultiRunSettings, _G) local rulesetHash = HSMConfigMenu.ConvertIntToBase25(rulesetHashInt, 5) @@ -169,7 +169,7 @@ function HSMConfigMenu.CreateSettingsHashMenu( screen ) HSMConfigMenu.updateRulesetHashDisplay() HSMConfigMenu.SaveSettingsToGlobal() end}, - {Text = "Single Run Leaderboard Ruleset", event = function() + {Text = "Ingame Time (IGT) Ruleset", event = function() HSMConfigMenu.LoadSettings("SingleRunSettings") local rulesetHashInt = CalculateHash(HSMConfigMenu.SingleRunSettings, _G) local rulesetHash = HSMConfigMenu.ConvertIntToBase25(rulesetHashInt, 5) From 0ff8c91af3101bc1afdb72f4ee5dfc08dda9a159 Mon Sep 17 00:00:00 2001 From: cgull Date: Tue, 14 Sep 2021 14:38:22 -0400 Subject: [PATCH 35/77] fixup credits menu and ruleset text --- HadesSpeedrunningModpack/AttributionAndNotes.lua | 4 ++-- HadesSpeedrunningModpack/SettingsHashMenu.lua | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/HadesSpeedrunningModpack/AttributionAndNotes.lua b/HadesSpeedrunningModpack/AttributionAndNotes.lua index d4f486a..72ca843 100644 --- a/HadesSpeedrunningModpack/AttributionAndNotes.lua +++ b/HadesSpeedrunningModpack/AttributionAndNotes.lua @@ -18,9 +18,9 @@ function HSMConfigMenu.CreateAttributionMenu(screen) CreateTextBox({ Id = screen.Components["ThanksForAllTheFish"].Id, Text = "Modders: \\n" - .. "Ellomenop, Museus, cgull, paradigmsort, erumi321, Magic_Gonads, PonyWarrior" + .. " Ellomenop, Museus, cgull, paradigmsort, erumi321, Magic_Gonads, PonyWarrior" .. "\\n Testers: The Entire Hades Speedrunning Commumity <3" - .. " \\n\\n\\n Note from Ello: I just wanted to say thank you.\\n \\n Thank you to the Speedrunning Community:" + .. " \\n\\n\\n Note from Ello: \\nI just wanted to say thank you.\\n \\n Thank you to the Speedrunning Community:" .. " \\n This community is an immensely positive place to be and thats no accident." .. " \\n Its due to our shared love of this game (or perhaps our shared hatred of 5 sacks)." .. " \\n Its due to the hard work of everyone building each other up (even if its just so the Fish Flex hits that much harder)." diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index fbf27ca..22a0ddf 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -169,7 +169,7 @@ function HSMConfigMenu.CreateSettingsHashMenu( screen ) HSMConfigMenu.updateRulesetHashDisplay() HSMConfigMenu.SaveSettingsToGlobal() end}, - {Text = "Ingame Time (IGT) Ruleset", event = function() + {Text = "In-Game Time (IGT) Ruleset", event = function() HSMConfigMenu.LoadSettings("SingleRunSettings") local rulesetHashInt = CalculateHash(HSMConfigMenu.SingleRunSettings, _G) local rulesetHash = HSMConfigMenu.ConvertIntToBase25(rulesetHashInt, 5) From adce15ae7370105c433a70d22d048e289486e34c Mon Sep 17 00:00:00 2001 From: cgull Date: Wed, 29 Dec 2021 12:34:13 -0600 Subject: [PATCH 36/77] Added charon sack control functionality, no control toggle yet. --- .gitmodules | 4 ++++ hades-CharonSackControl | 1 + 2 files changed, 5 insertions(+) create mode 160000 hades-CharonSackControl diff --git a/.gitmodules b/.gitmodules index bd633ee..24f94f2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,7 @@ [submodule "DontGetVorimed"] path = DontGetVorimed url = https://github.com/cgullsHadesMods/DontGetVorimed +[submodule "hades-CharonSackControl"] + path = hades-CharonSackControl + url = https://github.com/cgullsHadesMods/hades-CharonSackControl.git + diff --git a/hades-CharonSackControl b/hades-CharonSackControl new file mode 160000 index 0000000..ce1b1dc --- /dev/null +++ b/hades-CharonSackControl @@ -0,0 +1 @@ +Subproject commit ce1b1dc23c351b7deb3cb8fd700d394622fdf601 From ce90a2bf42b060df9f93e5ea0dac0f7222bb2d01 Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 11 Feb 2022 15:54:07 -0500 Subject: [PATCH 37/77] RtaTimer: Add Low Performance Mode Making 60 GetTime({ }) calls per second is bad - go figure. Most PCs don't have an issue with it, but sometimes it causes pretty bad lag. Enabling Low Performance mode will cut system calls down once every 15 frames, and track a running offset to adjust based on _worldTime. Much more efficient, but can take up to 0.24s to catch back up after losing sync. --- .../QualityOfLifeMenu.lua | 27 +++++++++++++++++++ RtaTimer/RtaTimer.lua | 21 ++++++++++++++- 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index 9f67c07..02dbe76 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -202,6 +202,33 @@ function HSMConfigMenu.CreateQolMenu(screen) HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["RtaTimerMultiRunCheckBox"]) itemLocationY = itemLocationY + itemSpacingY + screen.Components["RtaTimerLowPerformanceTextBox"] = CreateScreenComponent({ + Name = "BlankObstacle", + Scale = 1, + X = itemLocationX, + Y = itemLocationY, + Group = "Combat_Menu" }) + CreateTextBox({ + Id = screen.Components["RtaTimerLowPerformanceTextBox"].Id, + Text = " - Use Low-Performance Mode: ", + Color = Color.BoonPatchCommon, + FontSize = 16, + OffsetX = 0, OffsetY = 0, + Font = "AlegrayaSansSCRegular", + ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, + Justification = "Left" + }) + screen.Components["RtaTimerLowPerformanceCheckBox"] = CreateScreenComponent({ + Name = "RadioButton", + Scale = 1, + X = itemLocationX + itemSpacingX, + Y = itemLocationY, + Group = "CombatMenu" + }) + screen.Components["RtaTimerLowPerformanceCheckBox"].Config = "RtaTimer.config.LowPerformance" + screen.Components["RtaTimerLowPerformanceCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" + HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["RtaTimerLowPerformanceCheckBox"]) + itemLocationY = itemLocationY + itemSpacingY screen.Components["RtaTimerResetButton"] = CreateScreenComponent({ Name = "MarketSlot", diff --git a/RtaTimer/RtaTimer.lua b/RtaTimer/RtaTimer.lua index 293c22b..2631674 100644 --- a/RtaTimer/RtaTimer.lua +++ b/RtaTimer/RtaTimer.lua @@ -11,6 +11,7 @@ local config = { ModName = "RtaTimer", DisplayTimer = true, MultiWeapon = false, + LowPerformance = false, } RtaTimer.config = config @@ -54,7 +55,20 @@ function RtaTimer.UpdateRtaTimer() local current_time = "00:00.00" -- If the timer has been reset, it should stay at 00:00.00 until it "starts" again if not RtaTimer.TimerWasReset then - current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartTime, GetTime({ })) + -- Use _worldTime to prevent overusing system calls + if config.LowPerformance then + current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartWorldTime, _worldTime + RtaTimer.Offset) + RtaTimer.Cycle = RtaTimer.Cycle + 1 + + -- Update offset every 15 cycles to prevent drift during pauses + if RtaTimer.Cycle == 15 then + RtaTimer.Offset = (GetTime({ }) - RtaTimer.StartTime) - (_worldTime - RtaTimer.StartWorldTime) + current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartTime, GetTime({ })) + RtaTimer.Cycle = 0 + end + else + current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartTime, GetTime({ })) + end end PrintUtil.createOverlayLine( @@ -73,6 +87,9 @@ end function RtaTimer.StartRtaTimer() RtaTimer.Running = true + RtaTimer.Cycle = 0 + RtaTimer.Offset = 0.0 + while RtaTimer.Running do RtaTimer.UpdateRtaTimer() -- Update once per frame @@ -92,6 +109,7 @@ ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) -- If multiweapon, only restart if timer was reset if not config.MultiWeapon or RtaTimer.TimerWasReset then RtaTimer.StartTime = GetTime({ }) + RtaTimer.StartWorldTime = _worldTime RtaTimer.TimerWasReset = false end @@ -116,6 +134,7 @@ ModUtil.LoadOnce( else RtaTimer.TimerWasReset = false RtaTimer.StartTime = GetTime({ }) + RtaTimer.StartWorldTime = _worldTime thread(RtaTimer.StartRtaTimer) end end From 9bdafc3d342b89becbc85c203ac5d95abfc59fba Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 11 Feb 2022 16:22:22 -0500 Subject: [PATCH 38/77] CharonSackControl: Add QoL menu option --- .../QualityOfLifeMenu.lua | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index 02dbe76..c905dbb 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -67,8 +67,8 @@ function HSMConfigMenu.CreateQolMenu(screen) screen.Components["DisableIntroCheckBox"].Config = "RemoveCutscenes.config.RemoveIntro" screen.Components["DisableIntroCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["DisableIntroCheckBox"]) - itemLocationY = itemLocationY + itemSpacingY + screen.Components["DisableOuttroTextBox"] = CreateScreenComponent({ Name = "BlankObstacle", Scale = 1, @@ -136,6 +136,39 @@ function HSMConfigMenu.CreateQolMenu(screen) screen.Components["RestartCheckBox"].Config = "QuickRestart.config.Enabled" screen.Components["RestartCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["RestartCheckBox"]) + itemLocationY = itemLocationY + itemSpacingY + + ----------------------------- + -- Charon Sack Control + ----------------------------- + + screen.Components["CharonSackControlTextBox"] = CreateScreenComponent({ + Name = "BlankObstacle", + Scale = 1, + X = itemLocationX, + Y = itemLocationY, + Group = "Combat_Menu" }) + CreateTextBox({ + Id = screen.Components["CharonSackControlTextBox"].Id, + Text = "Force Charon Sack Spawn: ", + Color = Color.BoonPatchCommon, + FontSize = 16, + OffsetX = 0, OffsetY = 0, + Font = "AlegrayaSansSCRegular", + ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, + Justification = "Left", + }) + screen.Components["CharonSackControlCheckBox"] = CreateScreenComponent({ + Name = "RadioButton", + Scale = 1, + X = itemLocationX + itemSpacingX, + Y = itemLocationY, + Group = "CombatMenu" + }) + screen.Components["CharonSackControlCheckBox"].Config = "CharonSackControl.config.SpawnSack" + screen.Components["CharonSackControlCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" + HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["CharonSackControlCheckBox"]) + itemLocationY = itemLocationY + itemSpacingY ----------------------------- -- Right Column From f31d30a13f6c89830454ae978b4c4b63009ddb84 Mon Sep 17 00:00:00 2001 From: Museus Date: Sat, 5 Mar 2022 13:30:23 -0500 Subject: [PATCH 39/77] Make QuickRestart spawn you in Courtyard Overrides HandleDeath - if normal death, spawn in pool. Otherwise spawn in Courtyard --- QuickRestart/QuickRestart.lua | 205 +++++++++++++++++++++++----------- 1 file changed, 142 insertions(+), 63 deletions(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index faf0d59..cded438 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -1,94 +1,173 @@ ModUtil.RegisterMod("QuickRestart") local config = { - Enabled = false + Enabled = false } QuickRestart.config = config function QuickRestart.CanReset() - -- QuickRestart must be Enabled - if not config.Enabled then return false end + -- QuickRestart must be Enabled + if not config.Enabled then return false end - -- Short delay to handle edge cases - wait(0.1) + -- Short delay to handle edge cases + wait(0.1) - -- Zag must not be in the House - if ModUtil.PathGet("CurrentDeathAreaRoom") then return false end + -- Zag must not be in the House + if ModUtil.PathGet("CurrentDeathAreaRoom") then return false end - -- Zag must not be frozen - if not IsEmpty( CurrentRun.Hero.FreezeInputKeys ) then return false end + -- Zag must not be frozen + if not IsEmpty( CurrentRun.Hero.FreezeInputKeys ) then return false end - -- Combat UI must be visible - if not (ShowingCombatUI or false) then return false end + -- Combat UI must be visible + if not (ShowingCombatUI or false) then return false end - -- If we're in a Thanatos Room, enemies must have already spawned - if (CurrentRun ~= nil and CurrentRun.CurrentRoom ~= nil and - CurrentRun.CurrentRoom.Encounter ~= nil and - CurrentRun.CurrentRoom.Encounter.ThanatosId ~= nil - and GetActiveEnemyCount() == 0) then - return false - end + -- If we're in a Thanatos Room, enemies must have already spawned + if (CurrentRun ~= nil and CurrentRun.CurrentRoom ~= nil and + CurrentRun.CurrentRoom.Encounter ~= nil and + CurrentRun.CurrentRoom.Encounter.ThanatosId ~= nil + and GetActiveEnemyCount() == 0) then + return false + end - return true + return true end function QuickRestart.ResetRun(triggerArgs) - if not QuickRestart.CanReset() then - ModUtil.Hades.PrintOverhead("Can't reset now!", 2) - return - end + if not QuickRestart.CanReset() then + ModUtil.Hades.PrintOverhead("Can't reset now!", 2) + return + end - if RtaTimer then - RtaTimer__ResetRtaTimer() - end + if RtaTimer then + RtaTimer__ResetRtaTimer() + end - KillHero( CurrentRun.Hero, triggerArgs ) + QuickRestart.UsedQuickRestart = true + KillHero( CurrentRun.Hero, triggerArgs ) end OnControlPressed{ "Assist", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end + function(triggerArgs) + if config.Enabled then + if IsControlDown({ Name = "Assist" }) + and IsControlDown({ Name = "Use" }) + and IsControlDown({ Name = "Shout" }) + and IsControlDown({ Name = "Reload" }) then + QuickRestart.ResetRun(triggerArgs) + end + end end} OnControlPressed{ "Use", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end + function(triggerArgs) + if config.Enabled then + if IsControlDown({ Name = "Assist" }) + and IsControlDown({ Name = "Use" }) + and IsControlDown({ Name = "Shout" }) + and IsControlDown({ Name = "Reload" }) then + QuickRestart.ResetRun(triggerArgs) + end + end end} OnControlPressed{ "Shout", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end + function(triggerArgs) + if config.Enabled then + if IsControlDown({ Name = "Assist" }) + and IsControlDown({ Name = "Use" }) + and IsControlDown({ Name = "Shout" }) + and IsControlDown({ Name = "Reload" }) then + QuickRestart.ResetRun(triggerArgs) + end + end end} OnControlPressed{ "Reload", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end + function(triggerArgs) + if config.Enabled then + if IsControlDown({ Name = "Assist" }) + and IsControlDown({ Name = "Use" }) + and IsControlDown({ Name = "Shout" }) + and IsControlDown({ Name = "Reload" }) then + QuickRestart.ResetRun(triggerArgs) + end + end end} + +OnAnyLoad{ "RoomPreRun", + function ( triggerArgs ) + if QuickRestart.UsedQuickRestart then + thread( PlayVoiceLines, GlobalVoiceLines.EnteredDeathAreaVoiceLines ) + QuickRestart.UsedQuickRestart = false + end + end +} + +ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWeapon ) + if GetConfigOptionValue({ Name = "EditingMode" }) then + SetAnimation({ Name = "ZagreusDeadStartBlood", DestinationId = currentRun.Hero.ObjectId }) + return + end + + SendSaveFileEmail({ }) + + AddTimerBlock( currentRun, "HandleDeath" ) + if ScreenAnchors.TraitTrayScreen ~= nil then + CloseAdvancedTooltipScreen() + end + ClearHealthShroud() + CurrentRun.Hero.HandlingDeath = true + CurrentRun.Hero.IsDead = true + CurrentRun.ActiveBiomeTimer = false + if ConfigOptionCache.EasyMode and not currentRun.Cleared then + GameState.EasyModeLevel = GameState.EasyModeLevel + 1 + end + if not CurrentRun.Cleared then -- Already recorded if cleared + RecordRunStats() + end + + InvalidateCheckpoint() + + ZeroSuperMeter() + FinishTargetMarker( killer ) + + local deathPresentationName = currentRun.DeathPresentationFunctionName or "DeathPresentation" + local deathPresentationFunction = _G[deathPresentationName] + deathPresentationFunction( currentRun, killer, killingUnitWeapon ) + AddInputBlock({ Name = "MapLoad" }) + + currentRun.CurrentRoom.EndingHealth = currentRun.Hero.Health + currentRun.EndingMoney = currentRun.Money + table.insert( currentRun.RoomHistory, currentRun.CurrentRoom ) + UpdateRunHistoryCache( currentRun, currentRun.CurrentRoom ) + + currentRun.Money = 0 + currentRun.NumRerolls = GetNumMetaUpgrades( "RerollMetaUpgrade" ) + GetNumMetaUpgrades("RerollPanelMetaUpgrade") + + ResetObjectives() + ActiveScreens = {} + + CurrentRun.Hero.HandlingDeath = false + CurrentRun.Hero.Health = CurrentRun.Hero.MaxHealth + + local currentRoom = currentRun.CurrentRoom + local deathMap = "DeathArea" + + -- Changes Here + if QuickRestart.UsedQuickRestart then + deathMap = "RoomPreRun" + end + -- End Changes + + GameState.LocationName = "Location_Home" + RandomSetNextInitSeed() + SaveCheckpoint({ StartNextMap = deathMap, DevSaveName = CreateDevSaveName( currentRun, { StartNextMap = deathMap } ) }) + ClearUpgrades() + + SetConfigOption({ Name = "FlipMapThings", Value = false }) + + local runNumber = ( GetCompletedRuns() + 1 ) + local runDepth = GetRunDepth( currentRun ) + + LoadMap({ Name = deathMap, ResetBinks = true, ResetWeaponBinks = true }) +end, QuickRestart) \ No newline at end of file From e382aa24d1c9710cd2d5a90b3a3c11b1d9655a3c Mon Sep 17 00:00:00 2001 From: Museus Date: Sat, 5 Mar 2022 20:56:52 -0500 Subject: [PATCH 40/77] QuickRestart: set temporary InputBlock for autosplitter to detect --- QuickRestart/QuickRestart.lua | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index cded438..56fa232 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -43,6 +43,11 @@ function QuickRestart.ResetRun(triggerArgs) end QuickRestart.UsedQuickRestart = true + AddInputBlock({ Name = "QuickRestart" }) + + -- Short delay to let Livesplit grab flag + wait(0.1) + KillHero( CurrentRun.Hero, triggerArgs ) end @@ -111,6 +116,10 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe SendSaveFileEmail({ }) + if QuickRestart.UsedQuickRestart then + RemoveInputBlock({ Name = "QuickRestart" }) + end + AddTimerBlock( currentRun, "HandleDeath" ) if ScreenAnchors.TraitTrayScreen ~= nil then CloseAdvancedTooltipScreen() From 8fa93885d8330c72726adb8447dc6e04f91141b4 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 6 Mar 2022 01:06:57 -0500 Subject: [PATCH 41/77] Make LowPerformance mode the default --- .../QualityOfLifeMenu.lua | 28 ------------------- RtaTimer/RtaTimer.lua | 21 ++++++-------- 2 files changed, 8 insertions(+), 41 deletions(-) diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index c905dbb..cf525ca 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -235,34 +235,6 @@ function HSMConfigMenu.CreateQolMenu(screen) HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["RtaTimerMultiRunCheckBox"]) itemLocationY = itemLocationY + itemSpacingY - screen.Components["RtaTimerLowPerformanceTextBox"] = CreateScreenComponent({ - Name = "BlankObstacle", - Scale = 1, - X = itemLocationX, - Y = itemLocationY, - Group = "Combat_Menu" }) - CreateTextBox({ - Id = screen.Components["RtaTimerLowPerformanceTextBox"].Id, - Text = " - Use Low-Performance Mode: ", - Color = Color.BoonPatchCommon, - FontSize = 16, - OffsetX = 0, OffsetY = 0, - Font = "AlegrayaSansSCRegular", - ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, - Justification = "Left" - }) - screen.Components["RtaTimerLowPerformanceCheckBox"] = CreateScreenComponent({ - Name = "RadioButton", - Scale = 1, - X = itemLocationX + itemSpacingX, - Y = itemLocationY, - Group = "CombatMenu" - }) - screen.Components["RtaTimerLowPerformanceCheckBox"].Config = "RtaTimer.config.LowPerformance" - screen.Components["RtaTimerLowPerformanceCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" - HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["RtaTimerLowPerformanceCheckBox"]) - itemLocationY = itemLocationY + itemSpacingY - screen.Components["RtaTimerResetButton"] = CreateScreenComponent({ Name = "MarketSlot", Scale = 1, diff --git a/RtaTimer/RtaTimer.lua b/RtaTimer/RtaTimer.lua index 2631674..a191e35 100644 --- a/RtaTimer/RtaTimer.lua +++ b/RtaTimer/RtaTimer.lua @@ -11,7 +11,6 @@ local config = { ModName = "RtaTimer", DisplayTimer = true, MultiWeapon = false, - LowPerformance = false, } RtaTimer.config = config @@ -56,18 +55,14 @@ function RtaTimer.UpdateRtaTimer() -- If the timer has been reset, it should stay at 00:00.00 until it "starts" again if not RtaTimer.TimerWasReset then -- Use _worldTime to prevent overusing system calls - if config.LowPerformance then - current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartWorldTime, _worldTime + RtaTimer.Offset) - RtaTimer.Cycle = RtaTimer.Cycle + 1 - - -- Update offset every 15 cycles to prevent drift during pauses - if RtaTimer.Cycle == 15 then - RtaTimer.Offset = (GetTime({ }) - RtaTimer.StartTime) - (_worldTime - RtaTimer.StartWorldTime) - current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartTime, GetTime({ })) - RtaTimer.Cycle = 0 - end - else - current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartTime, GetTime({ })) + current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartWorldTime, _worldTime + RtaTimer.Offset) + RtaTimer.Cycle = RtaTimer.Cycle + 1 + + -- Update offset every 15 cycles to prevent drift during pauses + if RtaTimer.Cycle == 15 then + RtaTimer.Offset = (GetTime({ }) - RtaTimer.StartTime) - (_worldTime - RtaTimer.StartWorldTime) + current_time = RtaTimer.FormatElapsedTime(RtaTimer.StartTime, GetTime({ })) + RtaTimer.Cycle = 0 end end From 44d6f0da33153f468214f68287e863f06f5f28a4 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 6 Mar 2022 12:17:44 -0500 Subject: [PATCH 42/77] QuickRestart: add setting to re-equip starting keepsake --- .../QualityOfLifeMenu.lua | 29 +++++++++++ QuickRestart/KeepsakeList.lua | 34 +++++++++++++ QuickRestart/QuickRestart.lua | 48 ++++++++++++++++++- QuickRestart/modfile.txt | 1 + 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 QuickRestart/KeepsakeList.lua diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index cf525ca..28086b2 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -138,6 +138,35 @@ function HSMConfigMenu.CreateQolMenu(screen) HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["RestartCheckBox"]) itemLocationY = itemLocationY + itemSpacingY + + screen.Components["StartingKeepsakeTextBox"] = CreateScreenComponent({ + Name = "BlankObstacle", + Scale = 1, + X = itemLocationX, + Y = itemLocationY, + Group = "Combat_Menu" }) + CreateTextBox({ + Id = screen.Components["StartingKeepsakeTextBox"].Id, + Text = " - Re-equip Starting Keepsake: ", + Color = Color.BoonPatchCommon, + FontSize = 16, + OffsetX = 0, OffsetY = 0, + Font = "AlegrayaSansSCRegular", + ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, + Justification = "Left", + }) + screen.Components["StartingKeepsakeCheckBox"] = CreateScreenComponent({ + Name = "RadioButton", + Scale = 1, + X = itemLocationX + itemSpacingX, + Y = itemLocationY, + Group = "CombatMenu" + }) + screen.Components["StartingKeepsakeCheckBox"].Config = "QuickRestart.config.KeepStartingKeepsake" + screen.Components["StartingKeepsakeCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" + HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["StartingKeepsakeCheckBox"]) + itemLocationY = itemLocationY + itemSpacingY + ----------------------------- -- Charon Sack Control ----------------------------- diff --git a/QuickRestart/KeepsakeList.lua b/QuickRestart/KeepsakeList.lua new file mode 100644 index 0000000..47dd423 --- /dev/null +++ b/QuickRestart/KeepsakeList.lua @@ -0,0 +1,34 @@ +if QuickRestart then + QuickRestart.Keepsakes = { + -- row 1 + "MaxHealthKeepsakeTrait", -- cerberus + "DirectionalArmorTrait", -- achilles + "BackstabAlphaStrikeTrait", -- nyx + "PerfectClearDamageBonusTrait", -- thanatos + "ShopDurationTrait", -- charon + "BonusMoneyTrait", -- hypnos + "LowHealthDamageTrait", -- megaera + "DistanceDamageTrait", -- orpheus + "LifeOnUrnTrait", -- dusa + "ReincarnationTrait", -- skelly + + -- row 2 + "ForceZeusBoonTrait", -- zeus + "ForcePoseidonBoonTrait", -- poseidon + "ForceAthenaBoonTrait", -- athena + "ForceAphroditeBoonTrait", -- aphrodite + "ForceAresBoonTrait", -- ares + "ForceArtemisBoonTrait", -- artemis + "ForceDionysusBoonTrait", -- dionysus + "FastClearDodgeBonusTrait", -- hermes + "ForceDemeterBoonTrait", -- demeter + "ChaosBoonTrait", -- primordial chaos + + -- row 3 + "VanillaTrait", -- sisyphus + "ShieldBossTrait", -- eurydice + "ShieldAfterHitTrait", -- patroclus + "ChamberStackTrait", -- persephone + "HadesShoutKeepsake", -- hades + } +end \ No newline at end of file diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 56fa232..54b7938 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -1,7 +1,8 @@ ModUtil.RegisterMod("QuickRestart") local config = { - Enabled = false + Enabled = true, + KeepStartingKeepsake = true, } QuickRestart.config = config @@ -32,6 +33,16 @@ function QuickRestart.CanReset() return true end +function QuickRestart.EquipStartingKeepsake() + for idx, keepsake in ipairs(QuickRestart.Keepsakes) do + if HeroHasTrait(keepsake) and keepsake ~= QuickRestart.StartingKeepsake then + DebugPrint({ Text = "QuickRestart: Unequipping " .. keepsake }) + UnequipKeepsake(CurrentRun.Hero, keepsake) + EquipKeepsake(CurrentRun.Hero, QuickRestart.StartingKeepsake) + end + end +end + function QuickRestart.ResetRun(triggerArgs) if not QuickRestart.CanReset() then ModUtil.Hades.PrintOverhead("Can't reset now!", 2) @@ -104,6 +115,12 @@ OnAnyLoad{ "RoomPreRun", if QuickRestart.UsedQuickRestart then thread( PlayVoiceLines, GlobalVoiceLines.EnteredDeathAreaVoiceLines ) QuickRestart.UsedQuickRestart = false + + -- Reset Starting Keepsake + if QuickRestart.config.KeepStartingKeepsake and QuickRestart.StartingKeepsake ~= nil then + DebugPrint({ Text="Setting keepsake trigger" }) + QuickRestart.TriggerKeepsakeChange = true + end end end } @@ -179,4 +196,31 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe local runDepth = GetRunDepth( currentRun ) LoadMap({ Name = deathMap, ResetBinks = true, ResetWeaponBinks = true }) -end, QuickRestart) \ No newline at end of file +end, QuickRestart) + +ModUtil.WrapBaseFunction("ShowCombatUI", function( baseFunc, ... ) + local val = baseFunc(...) + + -- Get starting keepsake + if QuickRestart.TriggerKeepsakeChange then + DebugPrint({ Text="Equipping Keepsake" }) + QuickRestart.EquipStartingKeepsake() + end + + QuickRestart.TriggerKeepsakeChange = false + + return val +end, QuickRestart) + +ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) + local val = baseFunc(...) + + -- Get starting keepsake + for idx, keepsake in ipairs(QuickRestart.Keepsakes) do + if HeroHasTrait(keepsake) then + QuickRestart.StartingKeepsake = keepsake + end + end + + return val +end, QuickRestart) diff --git a/QuickRestart/modfile.txt b/QuickRestart/modfile.txt index 19ff908..c887cd8 100644 --- a/QuickRestart/modfile.txt +++ b/QuickRestart/modfile.txt @@ -1 +1,2 @@ Import "QuickRestart.lua" +Import "KeepsakeList.lua" \ No newline at end of file From 82c27b37fecbf6e3baf694fa9ecf0aad46b24fb8 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 6 Mar 2022 12:27:09 -0500 Subject: [PATCH 43/77] CharonSackControl: update to functional version --- hades-CharonSackControl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hades-CharonSackControl b/hades-CharonSackControl index ce1b1dc..0daf7d9 160000 --- a/hades-CharonSackControl +++ b/hades-CharonSackControl @@ -1 +1 @@ -Subproject commit ce1b1dc23c351b7deb3cb8fd700d394622fdf601 +Subproject commit 0daf7d92c571988a627650676557e2b589d88a1c From aec385cabe36502944bea82abb83459f2563df62 Mon Sep 17 00:00:00 2001 From: Museus Date: Sun, 6 Mar 2022 15:54:49 -0500 Subject: [PATCH 44/77] Update CHANGELOG and release v1.2.0-rc.1 --- CHANGELOG.md | 54 +++++++++++++++++++ .../HadesSpeedrunningModpack.lua | 2 +- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 69c958e..3f20d12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,60 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/), and will follow [Semantic Versioning](https://semver.org/). +## [1.2.0] - 2022-03-06 + +### Added + + - Can now toggle guaranteed Charon Sack spawn for Loyalty Card + - QuickRestart now sets a flag for the Livesplit autosplitter to reset + +### Changed + - QuickRestart spawns you in the Courtyard instead of the blood pool + - QuickRestart re-equips your starting keepsake + - RtaTimer is now more efficient - should remove any lag issues that it was causing + +## [1.1.1] - 2021-09-14 + +### Added + + - Can now toggle Hell Mode in mod settings + +### Removed + + - Starting Boon Selector was not legal on any rulesets + +### Changed + + - Clarified ruleset text + - Cleaned up menu and credits + +### Bug fixes + + - Remove Approval Process blocking on the DontGetVorimed boon + - Slight delay before QuickRestart to remove interaction bugs + +## [1.1.0] - 2021-08-01 + +### Added + + - Single-run ruleset + +### Changed + + - Quick Restart also resets RtaTimer + - Replaced FixedHammers with RunStartControl + - Asterius removed from Leaderboard ruleset + +### Bug fixes + + - Can no longer reset during the pause before Thanatos Encounter spawns to prevent broken GameState + + + +## [1.0.0] - 2021-06-04 + +No major bugs were found with v1.0.0-rc.2 so we are moving forward with leaderboard creation! + ## [1.0.0-rc.2] - 2021-05-30 ### Changed diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index a5a1827..f2c1b41 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.1.1" + Version = "v1.2.0-rc.1" } HadesSpeedrunningModpack.config = config From 995d691d1f1514b448d54ceedb523f7a0c6c1db1 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 7 Mar 2022 13:37:48 -0500 Subject: [PATCH 45/77] Adding GameState update for keepsake alteration --- QuickRestart/QuickRestart.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 54b7938..1746f8b 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -39,6 +39,7 @@ function QuickRestart.EquipStartingKeepsake() DebugPrint({ Text = "QuickRestart: Unequipping " .. keepsake }) UnequipKeepsake(CurrentRun.Hero, keepsake) EquipKeepsake(CurrentRun.Hero, QuickRestart.StartingKeepsake) + GameState.LastAwardTrait = QuickRestart.StartingKeepsake end end end From 0edf3e16145044108688d00a91b9553080fa7cd7 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 7 Mar 2022 17:57:51 -0500 Subject: [PATCH 46/77] RC 0.3 updates --- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- QuickRestart/QuickRestart.lua | 8 +++----- RunStartControl | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index f2c1b41..14192e8 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.2.0-rc.1" + Version = "v1.2.0-rc.3" } HadesSpeedrunningModpack.config = config diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 1746f8b..d9d227a 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -35,7 +35,7 @@ end function QuickRestart.EquipStartingKeepsake() for idx, keepsake in ipairs(QuickRestart.Keepsakes) do - if HeroHasTrait(keepsake) and keepsake ~= QuickRestart.StartingKeepsake then + if QuickRestart.StartingKeepsake and HeroHasTrait(keepsake) and keepsake ~= QuickRestart.StartingKeepsake then DebugPrint({ Text = "QuickRestart: Unequipping " .. keepsake }) UnequipKeepsake(CurrentRun.Hero, keepsake) EquipKeepsake(CurrentRun.Hero, QuickRestart.StartingKeepsake) @@ -204,12 +204,10 @@ ModUtil.WrapBaseFunction("ShowCombatUI", function( baseFunc, ... ) -- Get starting keepsake if QuickRestart.TriggerKeepsakeChange then + QuickRestart.TriggerKeepsakeChange = false DebugPrint({ Text="Equipping Keepsake" }) QuickRestart.EquipStartingKeepsake() end - - QuickRestart.TriggerKeepsakeChange = false - return val end, QuickRestart) @@ -219,7 +217,7 @@ ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) -- Get starting keepsake for idx, keepsake in ipairs(QuickRestart.Keepsakes) do if HeroHasTrait(keepsake) then - QuickRestart.StartingKeepsake = keepsake + QuickRestart.StartingKeepsake = keepsake end end diff --git a/RunStartControl b/RunStartControl index 9028216..8bdfd0f 160000 --- a/RunStartControl +++ b/RunStartControl @@ -1 +1 @@ -Subproject commit 90282163b61aa015c497e4cd93e89a488b6f9da3 +Subproject commit 8bdfd0f013a098242d95a81d69aaa312005c12b0 From 2e7ee804c65e4475248681c321cf539561080692 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 7 Mar 2022 18:00:08 -0500 Subject: [PATCH 47/77] updating changelog to rc3 --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f20d12..981b08a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/), and will - QuickRestart spawns you in the Courtyard instead of the blood pool - QuickRestart re-equips your starting keepsake - RtaTimer is now more efficient - should remove any lag issues that it was causing + - Default Hammer for Rama Aspect of the Bow is now Twin Shot ## [1.1.1] - 2021-09-14 From a83eed0ce865db088fc78f1c806859f757a22325 Mon Sep 17 00:00:00 2001 From: Caleb Date: Mon, 7 Mar 2022 18:08:04 -0500 Subject: [PATCH 48/77] RSC submodule update --- RunStartControl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RunStartControl b/RunStartControl index 8bdfd0f..ceadae1 160000 --- a/RunStartControl +++ b/RunStartControl @@ -1 +1 @@ -Subproject commit 8bdfd0f013a098242d95a81d69aaa312005c12b0 +Subproject commit ceadae1ff41447bdea14511379c0a99a3d1edee3 From 825d224a14e668e47f66546564158b5c7914603a Mon Sep 17 00:00:00 2001 From: Zyruvias Date: Thu, 12 May 2022 08:17:28 -0400 Subject: [PATCH 49/77] add comment prefix --- ShowChamberNumber/modfile.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/ShowChamberNumber/modfile.txt b/ShowChamberNumber/modfile.txt index 8636f76..2f79db0 100644 --- a/ShowChamberNumber/modfile.txt +++ b/ShowChamberNumber/modfile.txt @@ -1,3 +1,4 @@ +-: ShowChamberNumber Authors: Museus (Discord: Museus#7777) From ca207dad28857734c56dfcc2fc41e948ebc3d0af Mon Sep 17 00:00:00 2001 From: Caleb Date: Fri, 13 May 2022 23:13:38 -0400 Subject: [PATCH 50/77] RSC update for modpack branch --- RunStartControl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RunStartControl b/RunStartControl index ceadae1..cceb981 160000 --- a/RunStartControl +++ b/RunStartControl @@ -1 +1 @@ -Subproject commit ceadae1ff41447bdea14511379c0a99a3d1edee3 +Subproject commit cceb981438f9a21ba51b826bb5097f7b986f005d From 0cd9c0d35da351b0117a26f8ebd48bdb2e783293 Mon Sep 17 00:00:00 2001 From: Museus <69139347+Museus@users.noreply.github.com> Date: Thu, 19 May 2022 14:13:31 -0400 Subject: [PATCH 51/77] QuickRestart: do not allow during Death Defiance animation (#64) Spamming QuickRestart during Death Defiance animation results in weird behavior with MapState. Don't allow during DD or while already QuickRestarting. --- QuickRestart/QuickRestart.lua | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index d9d227a..1614389 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -22,6 +22,12 @@ function QuickRestart.CanReset() -- Combat UI must be visible if not (ShowingCombatUI or false) then return false end + -- We can't be LastStand-ing + if QuickRestart.LastStanding then return false end + + -- We can't be QuickRestart-ing + if QuickRestart.UsedQuickRestart then return false end + -- If we're in a Thanatos Room, enemies must have already spawned if (CurrentRun ~= nil and CurrentRun.CurrentRoom ~= nil and CurrentRun.CurrentRoom.Encounter ~= nil and @@ -223,3 +229,14 @@ ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) return val end, QuickRestart) + +ModUtil.WrapBaseFunction("PlayerLastStandPresentationStart", function( baseFunc, ... ) + QuickRestart.LastStanding = true + return baseFunc( ... ) +end, QuickRestart) + +ModUtil.WrapBaseFunction("PlayerLastStandPresentationEnd", function( baseFunc, ... ) + local val = baseFunc( ... ) + QuickRestart.LastStanding = false + return val +end, QuickRestart) From 65c8c617c834183309c05ca3d4b298cab7795480 Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 19 May 2022 14:31:32 -0400 Subject: [PATCH 52/77] Added flag for blocking resets mid-trial boon selection --- QuickRestart/QuickRestart.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 1614389..572858c 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -28,6 +28,9 @@ function QuickRestart.CanReset() -- We can't be QuickRestart-ing if QuickRestart.UsedQuickRestart then return false end + -- We can't be mid-trial god selection + if QuickRestart.MidDevotion then return false end + -- If we're in a Thanatos Room, enemies must have already spawned if (CurrentRun ~= nil and CurrentRun.CurrentRoom ~= nil and CurrentRun.CurrentRoom.Encounter ~= nil and @@ -240,3 +243,10 @@ ModUtil.WrapBaseFunction("PlayerLastStandPresentationEnd", function( baseFunc, . QuickRestart.LastStanding = false return val end, QuickRestart) + +ModUtil.WrapBaseFunction("StartDevotionTestPresentation", function( baseFunc, ... ) + QuickRestart.MidDevotion = true + baseFunc(...) + QuickRestart.MidDevotion = false +end, QuickRestart) + From e865d57cef35da81106c3c928855dd830fb6f69f Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 19 May 2022 15:28:13 -0400 Subject: [PATCH 53/77] Tidied up keepsake replacement, hopefully fixing some of the order of operations bugs, made keepsake setting persist across quit+reload --- QuickRestart/QuickRestart.lua | 48 ++++++++++------------------------- 1 file changed, 13 insertions(+), 35 deletions(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 572858c..bc99d71 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -42,17 +42,6 @@ function QuickRestart.CanReset() return true end -function QuickRestart.EquipStartingKeepsake() - for idx, keepsake in ipairs(QuickRestart.Keepsakes) do - if QuickRestart.StartingKeepsake and HeroHasTrait(keepsake) and keepsake ~= QuickRestart.StartingKeepsake then - DebugPrint({ Text = "QuickRestart: Unequipping " .. keepsake }) - UnequipKeepsake(CurrentRun.Hero, keepsake) - EquipKeepsake(CurrentRun.Hero, QuickRestart.StartingKeepsake) - GameState.LastAwardTrait = QuickRestart.StartingKeepsake - end - end -end - function QuickRestart.ResetRun(triggerArgs) if not QuickRestart.CanReset() then ModUtil.Hades.PrintOverhead("Can't reset now!", 2) @@ -66,7 +55,6 @@ function QuickRestart.ResetRun(triggerArgs) QuickRestart.UsedQuickRestart = true AddInputBlock({ Name = "QuickRestart" }) - -- Short delay to let Livesplit grab flag wait(0.1) KillHero( CurrentRun.Hero, triggerArgs ) @@ -126,10 +114,14 @@ OnAnyLoad{ "RoomPreRun", thread( PlayVoiceLines, GlobalVoiceLines.EnteredDeathAreaVoiceLines ) QuickRestart.UsedQuickRestart = false + RemoveLastAwardTrait() + UnequipWeaponUpgrade() + RemoveLastAssistTrait() + -- Reset Starting Keepsake - if QuickRestart.config.KeepStartingKeepsake and QuickRestart.StartingKeepsake ~= nil then + if QuickRestart.config.KeepStartingKeepsake then DebugPrint({ Text="Setting keepsake trigger" }) - QuickRestart.TriggerKeepsakeChange = true + GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake or GameState.LastAwardTrait end end end @@ -141,12 +133,12 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe return end - SendSaveFileEmail({ }) - if QuickRestart.UsedQuickRestart then RemoveInputBlock({ Name = "QuickRestart" }) end + SendSaveFileEmail({ }) + AddTimerBlock( currentRun, "HandleDeath" ) if ScreenAnchors.TraitTrayScreen ~= nil then CloseAdvancedTooltipScreen() @@ -193,6 +185,10 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe if QuickRestart.UsedQuickRestart then deathMap = "RoomPreRun" end + + if QuickRestart.KeepStartingKeepsake and GameState.QuickRestartStartingKeepsake then + GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake + end -- End Changes GameState.LocationName = "Location_Home" @@ -208,28 +204,10 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe LoadMap({ Name = deathMap, ResetBinks = true, ResetWeaponBinks = true }) end, QuickRestart) -ModUtil.WrapBaseFunction("ShowCombatUI", function( baseFunc, ... ) - local val = baseFunc(...) - - -- Get starting keepsake - if QuickRestart.TriggerKeepsakeChange then - QuickRestart.TriggerKeepsakeChange = false - DebugPrint({ Text="Equipping Keepsake" }) - QuickRestart.EquipStartingKeepsake() - end - return val -end, QuickRestart) - ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) local val = baseFunc(...) - -- Get starting keepsake - for idx, keepsake in ipairs(QuickRestart.Keepsakes) do - if HeroHasTrait(keepsake) then - QuickRestart.StartingKeepsake = keepsake - end - end - + GameState.QuickRestartStartingKeepsake = GameState.LastAwardTrait return val end, QuickRestart) From a55b79a83ff83cbeac44d918e84209b5e56b587e Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 19 May 2022 15:34:37 -0400 Subject: [PATCH 54/77] Removed KeepsakeList.lua and import thereof in modfile.txt. --- QuickRestart/KeepsakeList.lua | 34 ---------------------------------- QuickRestart/modfile.txt | 3 +-- 2 files changed, 1 insertion(+), 36 deletions(-) delete mode 100644 QuickRestart/KeepsakeList.lua diff --git a/QuickRestart/KeepsakeList.lua b/QuickRestart/KeepsakeList.lua deleted file mode 100644 index 47dd423..0000000 --- a/QuickRestart/KeepsakeList.lua +++ /dev/null @@ -1,34 +0,0 @@ -if QuickRestart then - QuickRestart.Keepsakes = { - -- row 1 - "MaxHealthKeepsakeTrait", -- cerberus - "DirectionalArmorTrait", -- achilles - "BackstabAlphaStrikeTrait", -- nyx - "PerfectClearDamageBonusTrait", -- thanatos - "ShopDurationTrait", -- charon - "BonusMoneyTrait", -- hypnos - "LowHealthDamageTrait", -- megaera - "DistanceDamageTrait", -- orpheus - "LifeOnUrnTrait", -- dusa - "ReincarnationTrait", -- skelly - - -- row 2 - "ForceZeusBoonTrait", -- zeus - "ForcePoseidonBoonTrait", -- poseidon - "ForceAthenaBoonTrait", -- athena - "ForceAphroditeBoonTrait", -- aphrodite - "ForceAresBoonTrait", -- ares - "ForceArtemisBoonTrait", -- artemis - "ForceDionysusBoonTrait", -- dionysus - "FastClearDodgeBonusTrait", -- hermes - "ForceDemeterBoonTrait", -- demeter - "ChaosBoonTrait", -- primordial chaos - - -- row 3 - "VanillaTrait", -- sisyphus - "ShieldBossTrait", -- eurydice - "ShieldAfterHitTrait", -- patroclus - "ChamberStackTrait", -- persephone - "HadesShoutKeepsake", -- hades - } -end \ No newline at end of file diff --git a/QuickRestart/modfile.txt b/QuickRestart/modfile.txt index c887cd8..027d9da 100644 --- a/QuickRestart/modfile.txt +++ b/QuickRestart/modfile.txt @@ -1,2 +1 @@ -Import "QuickRestart.lua" -Import "KeepsakeList.lua" \ No newline at end of file +Import "QuickRestart.lua" \ No newline at end of file From bb8fc45643e772b2e2efff3113ff131b3841191c Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 19 May 2022 15:35:59 -0400 Subject: [PATCH 55/77] switching to kill from killhero --- QuickRestart/QuickRestart.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index bc99d71..f3f5ed1 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -57,7 +57,7 @@ function QuickRestart.ResetRun(triggerArgs) wait(0.1) - KillHero( CurrentRun.Hero, triggerArgs ) + Kill( CurrentRun.Hero, triggerArgs ) end OnControlPressed{ "Assist", From 4a21c5ebde7ce03d2ea71105c625942ac87f7beb Mon Sep 17 00:00:00 2001 From: Caleb Date: Thu, 19 May 2022 16:46:38 -0400 Subject: [PATCH 56/77] Renamed sgg-mod-modutil to reduce duplicate ModUtil user installs --- .gitmodules | 2 +- sgg-mod-modutil => ModUtil | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename sgg-mod-modutil => ModUtil (100%) diff --git a/.gitmodules b/.gitmodules index 24f94f2..c95a582 100644 --- a/.gitmodules +++ b/.gitmodules @@ -2,7 +2,7 @@ path = RunStartControl url = https://github.com/Museus/RunStartControl.git [submodule "sgg-mod-modutil"] - path = sgg-mod-modutil + path = ModUtil url = https://github.com/SGG-Modding/sgg-mod-modutil.git [submodule "HellModeToggle"] path = HellModeToggle diff --git a/sgg-mod-modutil b/ModUtil similarity index 100% rename from sgg-mod-modutil rename to ModUtil From e9ab569038a825f0421f712df4080357c138da2e Mon Sep 17 00:00:00 2001 From: Museus Date: Thu, 19 May 2022 17:50:07 -0400 Subject: [PATCH 57/77] Release v1.2.0-rc.4 --- CHANGELOG.md | 4 ++++ HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 981b08a..f9aada4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/), and will - QuickRestart re-equips your starting keepsake - RtaTimer is now more efficient - should remove any lag issues that it was causing - Default Hammer for Rama Aspect of the Bow is now Twin Shot + - Renamed sgg-mod-modutil to reduce duplicate ModUtil user installs + +### Bug fixes + - ShowChamberNumber now works with newer versions of modimporter ## [1.1.1] - 2021-09-14 diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 14192e8..0248be8 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.2.0-rc.3" + Version = "v1.2.0-rc.4" } HadesSpeedrunningModpack.config = config From e7ea557c5423b4898db5245d99c754cf1b660246 Mon Sep 17 00:00:00 2001 From: Museus <69139347+Museus@users.noreply.github.com> Date: Fri, 20 May 2022 19:21:37 -0400 Subject: [PATCH 58/77] Changelog: specify that v1.2.0 is not yet released --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f9aada4..50ded9a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/), and will follow [Semantic Versioning](https://semver.org/). -## [1.2.0] - 2022-03-06 +## [1.2.0] - TBD (rc.4 released 2021-05-19) ### Added @@ -111,4 +111,4 @@ Initial "Beta" release of the modpack. This includes: - ShowChamberNumber - ThanatosControl -as well as a ruleset preset for "Multiweapon Leaderboard" runs. \ No newline at end of file +as well as a ruleset preset for "Multiweapon Leaderboard" runs. From d7859a4af3a7b1b8d2cde39bbebb5561e45de0e4 Mon Sep 17 00:00:00 2001 From: Museus Date: Mon, 30 May 2022 11:40:05 -0400 Subject: [PATCH 59/77] v1.2.0: update version string and Changelog --- CHANGELOG.md | 2 +- HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 50ded9a..f4f5b92 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/), and will follow [Semantic Versioning](https://semver.org/). -## [1.2.0] - TBD (rc.4 released 2021-05-19) +## [1.2.0] - 2022-05-30 ### Added diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 0248be8..5f7d0ba 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,6 +1,6 @@ ModUtil.RegisterMod("HadesSpeedrunningModpack") config = { - Version = "v1.2.0-rc.4" + Version = "v1.2.0" } HadesSpeedrunningModpack.config = config From 75c7f4896b1964676a949f77b8be4f0e93da4357 Mon Sep 17 00:00:00 2001 From: Zyruvias <36384758+Zyruvias@users.noreply.github.com> Date: Fri, 30 Sep 2022 17:51:41 -0400 Subject: [PATCH 60/77] setting: treat all non-completion Deaths as QuickRestarts Setting is added under QuickRestart in mod configuration menu Reviewed-by: Museus <69139347+Museus@users.noreply.github.com> --- .../QualityOfLifeMenu.lua | 33 +++++++++++++++++++ QuickRestart/QuickRestart.lua | 14 ++++++-- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua index 28086b2..e61fd3c 100644 --- a/HadesSpeedrunningModpack/QualityOfLifeMenu.lua +++ b/HadesSpeedrunningModpack/QualityOfLifeMenu.lua @@ -167,6 +167,38 @@ function HSMConfigMenu.CreateQolMenu(screen) HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["StartingKeepsakeCheckBox"]) itemLocationY = itemLocationY + itemSpacingY + ----------------------------- + -- Quick Restart on Death + ----------------------------- + + screen.Components["QuickDeathTextBox"] = CreateScreenComponent({ + Name = "BlankObstacle", + Scale = 1, + X = itemLocationX, + Y = itemLocationY, + Group = "Combat_Menu" }) + CreateTextBox({ + Id = screen.Components["QuickDeathTextBox"].Id, + Text = "Enable Quick Death:", + Color = Color.BoonPatchCommon, + FontSize = 16, + OffsetX = 0, OffsetY = 0, + Font = "AlegrayaSansSCRegular", + ShadowBlur = 0, ShadowColor = { 0, 0, 0, 1 }, ShadowOffset = { 0, 2 }, + Justification = "Left" + }) + screen.Components["QuickDeathCheckBox"] = CreateScreenComponent({ + Name = "RadioButton", + Scale = 1, + X = itemLocationX + itemSpacingX, + Y = itemLocationY, + Group = "CombatMenu" + }) + screen.Components["QuickDeathCheckBox"].Config = "QuickRestart.config.QuickDeathEnabled" + screen.Components["QuickDeathCheckBox"].OnPressedFunctionName = "HSMConfigMenu__ToggleGenericConfigCheckBox" + HSMConfigMenu__UpdateGenericConfigCheckbox(screen, screen.Components["QuickDeathCheckBox"]) + itemLocationY = itemLocationY + itemSpacingY + ----------------------------- -- Charon Sack Control ----------------------------- @@ -328,6 +360,7 @@ function HSMConfigMenu.CreateQolMenu(screen) }) screen.Components["HellModeToggleButton"].OnPressedFunctionName = "HellModeToggle.ToggleHellMode" itemLocationY = itemLocationY + itemSpacingY + end diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index f3f5ed1..6c5eb21 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -3,7 +3,9 @@ ModUtil.RegisterMod("QuickRestart") local config = { Enabled = true, KeepStartingKeepsake = true, + QuickDeathEnabled = true, } + QuickRestart.config = config function QuickRestart.CanReset() @@ -182,12 +184,18 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe local deathMap = "DeathArea" -- Changes Here - if QuickRestart.UsedQuickRestart then + local quickDeathApplicable = config.QuickDeathEnabled and not currentRun.Cleared + if QuickRestart.UsedQuickRestart or quickDeathApplicable then deathMap = "RoomPreRun" + + if QuickRestart.KeepStartingKeepsake and GameState.QuickRestartStartingKeepsake then + GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake + end end - if QuickRestart.KeepStartingKeepsake and GameState.QuickRestartStartingKeepsake then - GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake + -- Set UsedQuickRestart Flag so Keepsake and InputBlock are appropriately set. + if quickDeathApplicable then + QuickRestart.UsedQuickRestart = true end -- End Changes From be2eeea6b1ab7072baa631f6b7c10a1c1307947b Mon Sep 17 00:00:00 2001 From: Museus Date: Fri, 30 Sep 2022 18:14:35 -0400 Subject: [PATCH 61/77] build(deps): update ModUtil from 2021Jun30 to v2.7.0 --- ModUtil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ModUtil b/ModUtil index 6a98494..8c67846 160000 --- a/ModUtil +++ b/ModUtil @@ -1 +1 @@ -Subproject commit 6a98494b82f80fdfbb785395acc7bc7689327279 +Subproject commit 8c678461717ab7972675ace28851e8e65d63fd82 From aea5d3feafcf8f8bac401a7a1ddb77c16173f0a7 Mon Sep 17 00:00:00 2001 From: Kenorah Date: Sat, 29 Oct 2022 18:52:14 +1100 Subject: [PATCH 62/77] refactor(deprecated): update ModUtil calls to newest version Fix: #80 --- Colorblind/colorblind.lua | 28 +++++++++---------- DoorVisualIndicators/DoorVisualIndicators.lua | 6 ++-- EmoteMod/EmoteMod.lua | 2 +- ErumiUILib/ErumiUILib.lua | 2 +- HadesSpeedrunningModpack/HSMConfigMenu.lua | 4 +-- .../HadesSpeedrunningModpack.lua | 4 +-- HadesSpeedrunningModpack/RngBalanceMenu.lua | 10 +++---- HadesSpeedrunningModpack/SettingsHashMenu.lua | 4 +-- InteractableChaos/InteractableChaos.lua | 8 +++--- LootChoiceExt/LootChoiceExt.lua | 10 +++---- MinibossControl/MinibossControl.lua | 16 +++++------ ModConfigMenu/ModConfigMenu.lua | 6 ++-- ModdedWarning/ModdedWarning.lua | 2 +- PrintUtil/PrintUtil.lua | 2 +- QuickRestart/QuickRestart.lua | 14 +++++----- RemoveCutscenes/RemoveCutscenes.lua | 6 ++-- RoomDeterminism/RoomDeterminism.lua | 10 +++---- RtaTimer/RtaTimer.lua | 8 +++--- SatyrSackControl/SatyrSackControl.lua | 8 +++--- ShowChamberNumber/ShowChamberNumber.lua | 8 +++--- ThanatosControl/ThanatosControl.lua | 8 +++--- 21 files changed, 83 insertions(+), 83 deletions(-) diff --git a/Colorblind/colorblind.lua b/Colorblind/colorblind.lua index 4d07906..5c31f33 100644 --- a/Colorblind/colorblind.lua +++ b/Colorblind/colorblind.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("ColorblindMod") +ModUtil.Mod.Register("ColorblindMod") local config = { @@ -98,7 +98,7 @@ function IsOutlineLegal(enemy) return true end -ModUtil.WrapBaseFunction("CreateLevelDisplay", function ( baseFunc, newEnemy, currentRun ) +ModUtil.Path.Wrap("CreateLevelDisplay", function ( baseFunc, newEnemy, currentRun ) local biome = CurrentRun.CurrentRoom.RoomSetName if ColorblindMod.config[(biome or "nil") .. "Enabled"] and IsOutlineLegal(newEnemy) then @@ -109,7 +109,7 @@ ModUtil.WrapBaseFunction("CreateLevelDisplay", function ( baseFunc, newEnemy, cu baseFunc(newEnemy, currentRun) end, ColorblindMod) -ModUtil.BaseOverride("DoEnemyHealthBufferDeplete", function ( enemy ) +ModUtil.Path.Override("DoEnemyHealthBufferDeplete", function ( enemy ) if enemy.OnHealthBufferDepleteFunctionName ~= nil then _G[enemy.OnHealthBufferDepleteFunctionName]( enemy ) end @@ -131,7 +131,7 @@ ModUtil.BaseOverride("DoEnemyHealthBufferDeplete", function ( enemy ) end, ColorblindMod) OnAnyLoad{function () - local biome = ModUtil.SafeGet(_G, ModUtil.PathToIndexArray("CurrentRun.CurrentRoom.RoomSetName")) + local biome = ModUtil.IndexArray.Get(_G, ModUtil.Path.IndexArray("CurrentRun.CurrentRoom.RoomSetName")) while ColorblindMod.config[(biome or "nil") .. "Enabled"] do local ammoIds = GetIdsByType({ Name = "AmmoPack" }) if ammoIds ~= nil then @@ -145,52 +145,52 @@ OnAnyLoad{function () end end} -ModUtil.WrapBaseFunction( "HarpyKillPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "HarpyKillPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = unit.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "HydraKillPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "HydraKillPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = unit.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "HydraKillPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "HydraKillPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = unit.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "TheseusMinotaurKillPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "TheseusMinotaurKillPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = unit.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "CrawlerMiniBossKillPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "CrawlerMiniBossKillPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = unit.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "HadesKillPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "HadesKillPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = unit.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "KillPresentation", function(baseFunc, victim, args) +ModUtil.Path.Wrap( "KillPresentation", function(baseFunc, victim, args) RemoveOutline({ Id = victim.ObjectId}) return baseFunc(victim, args) end) -ModUtil.WrapBaseFunction( "DeathPresentation", function(baseFunc, currentRun, killer, killingUnitWeapon ) +ModUtil.Path.Wrap( "DeathPresentation", function(baseFunc, currentRun, killer, killingUnitWeapon ) RemoveOutline({ Id = currentRun.Hero.ObjectId}) return baseFunc(currentRun, killer, killingUnitWeapon ) end) -ModUtil.WrapBaseFunction( "BoatToDeathAreaTransition", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "BoatToDeathAreaTransition", function(baseFunc, unit, args) RemoveOutline({ Id = CurrentRun.Hero.ObjectId}) return baseFunc(unit, args) end) -ModUtil.WrapBaseFunction( "SurfaceDeathPresentation", function(baseFunc, unit, args) +ModUtil.Path.Wrap( "SurfaceDeathPresentation", function(baseFunc, unit, args) RemoveOutline({ Id = CurrentRun.Hero.ObjectId}) return baseFunc(unit, args) end) diff --git a/DoorVisualIndicators/DoorVisualIndicators.lua b/DoorVisualIndicators/DoorVisualIndicators.lua index 021699a..4a49a5b 100644 --- a/DoorVisualIndicators/DoorVisualIndicators.lua +++ b/DoorVisualIndicators/DoorVisualIndicators.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("DoorVisualIndicators") +ModUtil.Mod.Register("DoorVisualIndicators") local config = { ModName = "Door Visual Indicators", @@ -42,10 +42,10 @@ DoorVisualIndicators.MiniBossAnimations = { D_MiniBoss04 = {"EnemyStyxThiefIdle"}, } -ModUtil.WrapBaseFunction("CreateDoorPreviewIcon", function ( baseFunc, exitDoor, args ) +ModUtil.Path.Wrap("CreateDoorPreviewIcon", function ( baseFunc, exitDoor, args ) baseFunc(exitDoor, args) - local room_name = ModUtil.PathGet('RoomData.Name', args) or "" + local room_name = ModUtil.Path.Get('RoomData.Name', args) or "" local is_miniboss = RoomData[room_name].IsMiniBossRoom or false -- If the room is a fountain, add a visual effect to the door icon diff --git a/EmoteMod/EmoteMod.lua b/EmoteMod/EmoteMod.lua index 3c28e07..0e7bbd3 100644 --- a/EmoteMod/EmoteMod.lua +++ b/EmoteMod/EmoteMod.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("EmoteMod") +ModUtil.Mod.Register("EmoteMod") local config = { ModName = "EmoteMod", diff --git a/ErumiUILib/ErumiUILib.lua b/ErumiUILib/ErumiUILib.lua index e70051e..bcb56b4 100644 --- a/ErumiUILib/ErumiUILib.lua +++ b/ErumiUILib/ErumiUILib.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("ErumiUILib") +ModUtil.Mod.Register("ErumiUILib") ErumiUILib = { Slider = {}, Dropdown = {}, diff --git a/HadesSpeedrunningModpack/HSMConfigMenu.lua b/HadesSpeedrunningModpack/HSMConfigMenu.lua index 1e551d9..6b72a3c 100644 --- a/HadesSpeedrunningModpack/HSMConfigMenu.lua +++ b/HadesSpeedrunningModpack/HSMConfigMenu.lua @@ -47,14 +47,14 @@ function HSMConfigMenu.SaveSettingsToGlobal() HSMConfigMenu_SavedPersonalization.ModdedWarningColor = ModdedWarning.config.Color end -ModUtil.WrapBaseFunction("UnfreezePlayerUnit", function ( baseFunc, flag ) +ModUtil.Path.Wrap("UnfreezePlayerUnit", function ( baseFunc, flag ) DebugPrint({Text="This is in the wrapper"}) HSMConfigMenu.SaveSettingsToGlobal() baseFunc(flag) end, HSMConfigMenu) OnAnyLoad{function() - if ModUtil.PathGet("CurrentDeathAreaRoom") then + if ModUtil.Path.Get("CurrentDeathAreaRoom") then HSMConfigMenu.updateRulesetHashDisplay() end end} diff --git a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua index 5f7d0ba..9653be1 100644 --- a/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua +++ b/HadesSpeedrunningModpack/HadesSpeedrunningModpack.lua @@ -1,7 +1,7 @@ -ModUtil.RegisterMod("HadesSpeedrunningModpack") +ModUtil.Mod.Register("HadesSpeedrunningModpack") config = { Version = "v1.2.0" } HadesSpeedrunningModpack.config = config -ModUtil.RegisterMod("HSMConfigMenu") +ModUtil.Mod.Register("HSMConfigMenu") diff --git a/HadesSpeedrunningModpack/RngBalanceMenu.lua b/HadesSpeedrunningModpack/RngBalanceMenu.lua index 0e373e1..df36772 100644 --- a/HadesSpeedrunningModpack/RngBalanceMenu.lua +++ b/HadesSpeedrunningModpack/RngBalanceMenu.lua @@ -1,6 +1,6 @@ HSMConfigMenu__UpdateGenericConfigCheckbox = function(screen, button) local radioButtonValue = "RadioButton_Unselected" - if ModUtil.SafeGet(_G, ModUtil.PathToIndexArray(button.Config)) then + if ModUtil.IndexArray.Get(_G, ModUtil.Path.IndexArray(button.Config)) then radioButtonValue = "RadioButton_Selected" end SetThingProperty({ @@ -27,13 +27,13 @@ function HSMConfigMenu__ToggleGenericConfigCheckBox(screen, button, overrideValu if button.Enabled == false then return end - local configPathArray = ModUtil.PathToIndexArray(button.Config) - local configValue = ModUtil.SafeGet(_G, configPathArray) + local configPathArray = ModUtil.Path.IndexArray(button.Config) + local configValue = ModUtil.IndexArray.Get(_G, configPathArray) if configValue ~= nil then if overrideValue ~= nil then - ModUtil.SafeSet(_G, configPathArray, overrideValue) + ModUtil.IndexArray.Set(_G, configPathArray, overrideValue) else - ModUtil.SafeSet(_G, configPathArray, not configValue) + ModUtil.IndexArray.Set(_G, configPathArray, not configValue) end HSMConfigMenu__UpdateGenericConfigCheckbox(screen, button) end diff --git a/HadesSpeedrunningModpack/SettingsHashMenu.lua b/HadesSpeedrunningModpack/SettingsHashMenu.lua index 22a0ddf..f0f18cc 100644 --- a/HadesSpeedrunningModpack/SettingsHashMenu.lua +++ b/HadesSpeedrunningModpack/SettingsHashMenu.lua @@ -237,7 +237,7 @@ end function GetConfigBits(parentTable, config) for i, val in ipairs(config.Values) do - local currentConfigVal = ModUtil.SafeGet(parentTable, ModUtil.PathToIndexArray(config.Key)) + local currentConfigVal = ModUtil.IndexArray.Get(parentTable, ModUtil.Path.IndexArray(config.Key)) if currentConfigVal == nil then currentConfigVal = config.Default -- One liner will not work for boolean values as it will be interpreted as a logical or end @@ -256,7 +256,7 @@ function SetConfigBits(parentTable, config, configBits) setValue = config.Default -- One liner will not work for boolean values as it will be interpreted as a logical or end - ModUtil.SafeSet(parentTable, ModUtil.PathToIndexArray(config.Key), setValue) + ModUtil.IndexArray.Set(parentTable, ModUtil.Path.IndexArray(config.Key), setValue) end function CalculateHash(settings, parentTable) diff --git a/InteractableChaos/InteractableChaos.lua b/InteractableChaos/InteractableChaos.lua index 5b1dd18..d7b7d62 100644 --- a/InteractableChaos/InteractableChaos.lua +++ b/InteractableChaos/InteractableChaos.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("InteractableChaos") +ModUtil.Mod.Register("InteractableChaos") local config = { ModName = "Interactable Chaos", @@ -7,7 +7,7 @@ local config = { InteractableChaos.config = config -- Spawn the chaos interactable and prevent interaction with the normal chaos gate -ModUtil.WrapBaseFunction("DoUnlockRoomExits", function ( baseFunc, run, room ) +ModUtil.Path.Wrap("DoUnlockRoomExits", function ( baseFunc, run, room ) baseFunc(run, room) if not config.Enabled then @@ -34,7 +34,7 @@ ModUtil.WrapBaseFunction("DoUnlockRoomExits", function ( baseFunc, run, room ) end, InteractableChaos) -- Prevent chaos gates from unlocking visually -ModUtil.WrapBaseFunction("ExitDoorUnlockedPresentation", function ( baseFunc, exitDoor ) +ModUtil.Path.Wrap("ExitDoorUnlockedPresentation", function ( baseFunc, exitDoor ) -- If this is a chaos gate, do not unlock if exitDoor.Name == "SecretDoor" and config.Enabled then return @@ -45,7 +45,7 @@ ModUtil.WrapBaseFunction("ExitDoorUnlockedPresentation", function ( baseFunc, ex end, InteractableChaos) -- Handling for picking up the special chaos interactable -ModUtil.WrapBaseFunction("HandleLootPickup", function ( baseFunc, currentRun, loot ) +ModUtil.Path.Wrap("HandleLootPickup", function ( baseFunc, currentRun, loot ) if loot.IsInteractableChaosLoot and config.Enabled then -- Calculate health cost and reduce it to 0 if chaos egg is equipped diff --git a/LootChoiceExt/LootChoiceExt.lua b/LootChoiceExt/LootChoiceExt.lua index e467278..74fa32b 100644 --- a/LootChoiceExt/LootChoiceExt.lua +++ b/LootChoiceExt/LootChoiceExt.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("LootChoiceExt") +ModUtil.Mod.Register("LootChoiceExt") local config = { MinExtraLootChoices = 0, @@ -19,16 +19,16 @@ OnAnyLoad{ function() LootChoiceExt.LastLootChoices = LootChoiceExt.Choices + RandomInt( config.MinExtraLootChoices, config.MaxExtraLootChoices ) end} -ModUtil.BaseOverride("GetTotalLootChoices", function() +ModUtil.Path.Override("GetTotalLootChoices", function() return LootChoiceExt.LastLootChoices end, LootChoiceExt) -ModUtil.BaseOverride("CalcNumLootChoices", function() +ModUtil.Path.Override("CalcNumLootChoices", function() local numChoices = LootChoiceExt.LastLootChoices - GetNumMetaUpgrades("ReducedLootChoicesShrineUpgrade") return numChoices end, LootChoiceExt) -ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData, reroll ) +ModUtil.Path.Override("CreateBoonLootButtons", function( lootData, reroll ) -- BASE CODE ... (From Live Steam version v1.37996, pulled on 2021.04.27) local components = ScreenAnchors.ChoiceScreen.Components @@ -528,7 +528,7 @@ ModUtil.BaseOverride("CreateBoonLootButtons", function( lootData, reroll ) end, LootChoiceExt) -ModUtil.BaseOverride("DestroyBoonLootButtons", function( lootData ) +ModUtil.Path.Override("DestroyBoonLootButtons", function( lootData ) local components = ScreenAnchors.ChoiceScreen.Components local toDestroy = {} for index = 1, GetTotalLootChoices() do diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 4c5904f..37e8ff0 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -6,7 +6,7 @@ Change the proportions of miniboss chambers, allowing minibosses to be removed or replaced with others. ]] -ModUtil.RegisterMod("MinibossControl") +ModUtil.Mod.Register("MinibossControl") local config = { MinibossSetting = "Leaderboard" @@ -116,7 +116,7 @@ end -- Apply the configured miniboss settings to the game data function MinibossControl.UpdateMaxCreations() - ModUtil.MapSetTable(RoomSetData, { + ModUtil.Table.Merge(RoomSetData, { -- [[ Tartarus Miniboss Counts ]] Tartarus = { A_MiniBoss01 = { @@ -159,21 +159,21 @@ function MinibossControl.UpdateMaxCreations() -- Remove Tiny Vermin if MinibossControl.Presets[config.MinibossSetting].RemoveTinyVermin then - ModUtil.MapSetTable(RoomSetData.Styx.D_MiniBoss03, { + ModUtil.Table.Merge(RoomSetData.Styx.D_MiniBoss03, { LegalEncounters = { "MiniBossHeavyRangedForked" }, }) else - ModUtil.MapSetTable(RoomSetData.Styx.D_MiniBoss03, { - LegalEncounters = { "MiniBossCrawler", "MiniBossHeavyRangedForked" }, + ModUtil.Table.Merge(RoomSetData.Styx.D_MiniBoss03, { + LegalEncounters = { "MiniBossCrawler", "MiniBossHeavyRangedForked" }, }) end end -- Scripts/RunManager.lua : 515 -ModUtil.WrapBaseFunction("ChooseNextRoomData", function( baseFunc, currentRun, args ) +ModUtil.Path.Wrap("ChooseNextRoomData", function( baseFunc, currentRun, args ) -- IsRoomEligible looks at RoomCreations and ignores it if it's nil. Make sure it's not nil -- Easier and cleaner than overriding the IsRoomEligible function to fix nil behavior - ModUtil.MapSetTable(CurrentRun.RoomCreations, { + ModUtil.Table.Merge(CurrentRun.RoomCreations, { A_MiniBoss01 = currentRun.RoomCreations.A_MiniBoss01 or 0, A_MiniBoss02 = currentRun.RoomCreations.A_MiniBoss02 or 0, A_MiniBoss03 = currentRun.RoomCreations.A_MiniBoss03 or 0, @@ -193,7 +193,7 @@ ModUtil.LoadOnce( function() end) -- When a new run is started, make sure to apply the miniboss modifications -ModUtil.WrapBaseFunction("StartNewRun", function ( baseFunc, currentRun ) +ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) MinibossControl.UpdateMaxCreations() return baseFunc(currentRun) end, MinibossControl) diff --git a/ModConfigMenu/ModConfigMenu.lua b/ModConfigMenu/ModConfigMenu.lua index 2db0e20..3e2ed61 100644 --- a/ModConfigMenu/ModConfigMenu.lua +++ b/ModConfigMenu/ModConfigMenu.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("ModConfigMenu") +ModUtil.Mod.Register("ModConfigMenu") ModConfigMenu.Menus = {} ModConfigMenu.CurrentMenuIdx = 1 @@ -279,9 +279,9 @@ function ModConfigMenu__Close( screen, button ) OnScreenClosed({ Flag = screen.Name }) end -ModUtil.WrapBaseFunction("CreatePrimaryBacking", function ( baseFunc ) +ModUtil.Path.Wrap("CreatePrimaryBacking", function ( baseFunc ) -- Only show menu between runs - if not ModUtil.PathGet("CurrentDeathAreaRoom") then + if not ModUtil.Path.Get("CurrentDeathAreaRoom") then return baseFunc() end diff --git a/ModdedWarning/ModdedWarning.lua b/ModdedWarning/ModdedWarning.lua index 83c1e1a..75ca2fb 100644 --- a/ModdedWarning/ModdedWarning.lua +++ b/ModdedWarning/ModdedWarning.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("ModdedWarning") +ModUtil.Mod.Register("ModdedWarning") local config = { WarningMessage = "MODDED GAME", diff --git a/PrintUtil/PrintUtil.lua b/PrintUtil/PrintUtil.lua index 1a90d64..db9b688 100644 --- a/PrintUtil/PrintUtil.lua +++ b/PrintUtil/PrintUtil.lua @@ -8,7 +8,7 @@ manner, and to get the current stack trace, to determine who is calling a function and from where. ]] -ModUtil.RegisterMod("PrintUtil") +ModUtil.Mod.Register("PrintUtil") --- Util method to draw text to the screen -- @param obstacleName Name of textbox diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 6c5eb21..9342a5d 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("QuickRestart") +ModUtil.Mod.Register("QuickRestart") local config = { Enabled = true, @@ -16,7 +16,7 @@ function QuickRestart.CanReset() wait(0.1) -- Zag must not be in the House - if ModUtil.PathGet("CurrentDeathAreaRoom") then return false end + if ModUtil.Path.Get("CurrentDeathAreaRoom") then return false end -- Zag must not be frozen if not IsEmpty( CurrentRun.Hero.FreezeInputKeys ) then return false end @@ -129,7 +129,7 @@ OnAnyLoad{ "RoomPreRun", end } -ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWeapon ) +ModUtil.Path.Override( "HandleDeath", function( currentRun, killer, killingUnitWeapon ) if GetConfigOptionValue({ Name = "EditingMode" }) then SetAnimation({ Name = "ZagreusDeadStartBlood", DestinationId = currentRun.Hero.ObjectId }) return @@ -212,25 +212,25 @@ ModUtil.BaseOverride( "HandleDeath", function( currentRun, killer, killingUnitWe LoadMap({ Name = deathMap, ResetBinks = true, ResetWeaponBinks = true }) end, QuickRestart) -ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) +ModUtil.Path.Wrap("WindowDropEntrance", function( baseFunc, ... ) local val = baseFunc(...) -- Get starting keepsake GameState.QuickRestartStartingKeepsake = GameState.LastAwardTrait return val end, QuickRestart) -ModUtil.WrapBaseFunction("PlayerLastStandPresentationStart", function( baseFunc, ... ) +ModUtil.Path.Wrap("PlayerLastStandPresentationStart", function( baseFunc, ... ) QuickRestart.LastStanding = true return baseFunc( ... ) end, QuickRestart) -ModUtil.WrapBaseFunction("PlayerLastStandPresentationEnd", function( baseFunc, ... ) +ModUtil.Path.Wrap("PlayerLastStandPresentationEnd", function( baseFunc, ... ) local val = baseFunc( ... ) QuickRestart.LastStanding = false return val end, QuickRestart) -ModUtil.WrapBaseFunction("StartDevotionTestPresentation", function( baseFunc, ... ) +ModUtil.Path.Wrap("StartDevotionTestPresentation", function( baseFunc, ... ) QuickRestart.MidDevotion = true baseFunc(...) QuickRestart.MidDevotion = false diff --git a/RemoveCutscenes/RemoveCutscenes.lua b/RemoveCutscenes/RemoveCutscenes.lua index 5c4364a..cfb46b2 100644 --- a/RemoveCutscenes/RemoveCutscenes.lua +++ b/RemoveCutscenes/RemoveCutscenes.lua @@ -5,7 +5,7 @@ Optionally removes intro and outro cutscenes to runs ]] -ModUtil.RegisterMod("RemoveCutscenes") +ModUtil.Mod.Register("RemoveCutscenes") local config = { ModName = "RemoveCutscenes", @@ -15,7 +15,7 @@ local config = { RemoveCutscenes.config = config -- Remove starting cutscene -ModUtil.WrapBaseFunction("ShowRunIntro", function( baseFunc ) +ModUtil.Path.Wrap("ShowRunIntro", function( baseFunc ) if config.RemoveIntro then return end @@ -24,7 +24,7 @@ ModUtil.WrapBaseFunction("ShowRunIntro", function( baseFunc ) end, RemoveCutscenes) -ModUtil.WrapBaseFunction("EndEarlyAccessPresentation", function ( baseFunc ) +ModUtil.Path.Wrap("EndEarlyAccessPresentation", function ( baseFunc ) if config.RemoveOutro then CurrentRun.ActiveBiomeTimer = false diff --git a/RoomDeterminism/RoomDeterminism.lua b/RoomDeterminism/RoomDeterminism.lua index eafdd6f..84d2752 100644 --- a/RoomDeterminism/RoomDeterminism.lua +++ b/RoomDeterminism/RoomDeterminism.lua @@ -1,4 +1,4 @@ -ModUtil.RegisterMod("RoomDeterminism") +ModUtil.Mod.Register("RoomDeterminism") local config = { -- TODO: these configs can yield infinite loops. Needs addressing before exposing to player via a UI @@ -330,11 +330,11 @@ function GetRoomBiome(room_name_to_find) error("Invalid room name passed to GetRoomBiome: " .. (room_name_to_find or "nil")) end -ModUtil.WrapBaseFunction("ChooseNextRoomData", function ( baseFunc, run, args ) +ModUtil.Path.Wrap("ChooseNextRoomData", function ( baseFunc, run, args ) local next_room_data = baseFunc( run, args ) -- If the mod isn't enabled or the next room is a vanilla chaos, then don't override the next room - if (not config.Enabled) or ModUtil.SafeGet(args, {"RoomDataSet"}) ~= nil then + if (not config.Enabled) or ModUtil.IndexArray.Get(args, {"RoomDataSet"}) ~= nil then return next_room_data end @@ -372,14 +372,14 @@ ModUtil.WrapBaseFunction("ChooseNextRoomData", function ( baseFunc, run, args ) return next_room_data end, RoomDeterminism) -ModUtil.WrapBaseFunction("LeaveRoom", function ( baseFunc, currentRun, door ) +ModUtil.Path.Wrap("LeaveRoom", function ( baseFunc, currentRun, door ) -- Reset our naive exit door counter RoomDeterminism.CurrentExitForDepth = 1 baseFunc(currentRun, door) end, RoomDeterminism) -ModUtil.WrapBaseFunction("StartNewRun", function ( baseFunc, currentRun ) +ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) if config.Enabled and config.RoomGenerationAlgorithm ~= nil then RandomSynchronize(3110) RoomDeterminism.RoomGenerationAlgorithms[config.RoomGenerationAlgorithm]() diff --git a/RtaTimer/RtaTimer.lua b/RtaTimer/RtaTimer.lua index a191e35..25fa729 100644 --- a/RtaTimer/RtaTimer.lua +++ b/RtaTimer/RtaTimer.lua @@ -5,7 +5,7 @@ Track RTA time and display it below the IGT timer ]] -ModUtil.RegisterMod("RtaTimer") +ModUtil.Mod.Register("RtaTimer") local config = { ModName = "RtaTimer", @@ -97,7 +97,7 @@ function RtaTimer__ResetRtaTimer() RtaTimer.UpdateRtaTimer() end -ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) +ModUtil.Path.Wrap("WindowDropEntrance", function( baseFunc, ... ) local val = baseFunc(...) -- If single run, timer should always restart @@ -114,7 +114,7 @@ ModUtil.WrapBaseFunction("WindowDropEntrance", function( baseFunc, ... ) end, RtaTimer) -- Stop timer when Hades dies (but leave it on screen) -ModUtil.WrapBaseFunction("HadesKillPresentation", function( baseFunc, ...) +ModUtil.Path.Wrap("HadesKillPresentation", function( baseFunc, ...) RtaTimer.Running = false baseFunc(...) end, RtaTimer) @@ -122,7 +122,7 @@ end, RtaTimer) ModUtil.LoadOnce( function() -- If not in a run, reset timer and prepare for run start - if ModUtil.PathGet("CurrentDeathAreaRoom") then + if ModUtil.Path.Get("CurrentDeathAreaRoom") then RtaTimer.TimerWasReset = true -- If in a run, just start the timer from the time the mod was loaded diff --git a/SatyrSackControl/SatyrSackControl.lua b/SatyrSackControl/SatyrSackControl.lua index b1b1181..4e48edd 100644 --- a/SatyrSackControl/SatyrSackControl.lua +++ b/SatyrSackControl/SatyrSackControl.lua @@ -5,7 +5,7 @@ Forces the Styx satyr sack to appear in a specified range. ]] -ModUtil.RegisterMod("SatyrSackControl") +ModUtil.Mod.Register("SatyrSackControl") local config = { Enabled = true, -- If true, sack wil fall between MinSack and MaxSack @@ -15,17 +15,17 @@ local config = { SatyrSackControl.config = config -- Scripts/RoomManager.lua : 1874 -ModUtil.WrapBaseFunction("StartRoom", function ( baseFunc, currentRun, currentRoom ) +ModUtil.Path.Wrap("StartRoom", function ( baseFunc, currentRun, currentRoom ) baseFunc(currentRun, currentRoom) end, SatyrSackControl) -- Scripts/UIScripts.lua : 145 -ModUtil.WrapBaseFunction("ShowCombatUI", function ( baseFunc, flag ) +ModUtil.Path.Wrap("ShowCombatUI", function ( baseFunc, flag ) baseFunc(flag) end, SatyrSackControl) -- Scripts/RunManager.lua : 591 -ModUtil.BaseOverride("IsRoomForced", function(currentRun, currentRoom, nextRoomData, args) +ModUtil.Path.Override("IsRoomForced", function(currentRun, currentRoom, nextRoomData, args) if nextRoomData.AlwaysForce then return true end diff --git a/ShowChamberNumber/ShowChamberNumber.lua b/ShowChamberNumber/ShowChamberNumber.lua index 855ee4a..8fd2a2e 100644 --- a/ShowChamberNumber/ShowChamberNumber.lua +++ b/ShowChamberNumber/ShowChamberNumber.lua @@ -7,7 +7,7 @@ Shows the current Chamber Number immediately upon starting a room. If that fails for some reason, fall back to showing the Depth during ShowCombatUI. ]] -ModUtil.RegisterMod("ShowChamberNumber") +ModUtil.Mod.Register("ShowChamberNumber") local config = { ShowDepth = true, @@ -15,7 +15,7 @@ local config = { ShowChamberNumber.config = config -- Scripts/RoomManager.lua : 1874 -ModUtil.WrapBaseFunction("StartRoom", function ( baseFunc, currentRun, currentRoom ) +ModUtil.Path.Wrap("StartRoom", function ( baseFunc, currentRun, currentRoom ) if config.ShowDepth then ShowDepthCounter() end @@ -24,7 +24,7 @@ ModUtil.WrapBaseFunction("StartRoom", function ( baseFunc, currentRun, currentRo end, ShowChamberNumber) -- Scripts/UIScripts.lua : 145 -ModUtil.WrapBaseFunction("ShowCombatUI", function ( baseFunc, flag ) +ModUtil.Path.Wrap("ShowCombatUI", function ( baseFunc, flag ) if config.ShowDepth then ShowDepthCounter() end @@ -33,7 +33,7 @@ ModUtil.WrapBaseFunction("ShowCombatUI", function ( baseFunc, flag ) end, ShowChamberNumber) -- Hiding Depth Counter doesn't actually do anything -ModUtil.WrapBaseFunction("HideDepthCounter", function ( baseFunc ) +ModUtil.Path.Wrap("HideDepthCounter", function ( baseFunc ) if config.ShowDepth then return end diff --git a/ThanatosControl/ThanatosControl.lua b/ThanatosControl/ThanatosControl.lua index 5561376..2cae21b 100644 --- a/ThanatosControl/ThanatosControl.lua +++ b/ThanatosControl/ThanatosControl.lua @@ -7,7 +7,7 @@ Gives options to modify or remove Thanatos ]] -ModUtil.RegisterMod("ThanatosControl") +ModUtil.Mod.Register("ThanatosControl") local config = { ThanatosSetting = "Removed" @@ -69,7 +69,7 @@ function updateNumberOfThanatos() if config.ThanatosSetting == "Removed" then maxThans = 0 end - ModUtil.MapSetTable(EncounterData, { + ModUtil.Table.Merge(EncounterData, { ThanatosTartarus = { MaxThanatosSpawnsThisRun = maxThans, }, @@ -92,7 +92,7 @@ function updateThanatosValues(data) data.MaxWaves = ThanatosControl.Presets[config.ThanatosSetting][biome].MaxWaves end -ModUtil.WrapBaseFunction("SetupEncounter", function( baseFunc, encounterData, room ) +ModUtil.Path.Wrap("SetupEncounter", function( baseFunc, encounterData, room ) -- TODO: Make this actually work if false and config.ThanatosSetting ~= "Removed" then local moddedEncounter = DeepCopyTable( encounterData ) @@ -106,7 +106,7 @@ ModUtil.WrapBaseFunction("SetupEncounter", function( baseFunc, encounterData, ro end, ThanatosControl) -- When a new run is started, make sure to apply the than modifications -ModUtil.WrapBaseFunction("StartNewRun", function ( baseFunc, currentRun ) +ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) updateNumberOfThanatos() return baseFunc(currentRun) end, ThanatosControl) From a664a95e60d8b1eba5513376deb9c6c64ec62c7b Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Tue, 15 Nov 2022 19:01:58 +0000 Subject: [PATCH 63/77] (feat)(MinibossControl: Add Hypermodded ruleset, which removes Tisiphone) Adds A_Boss01-03 as options in all rulesets (all always true, except A_Boss03 being disabled in Hypermodded). If a Fury is invalid for the current ruleset, it will be marked ineligible as a room after A_PreBoss01 (endshop); editing LinkedRooms directly does not work. Fix: #86 Co-authored-by: SleepSoul Co-authored-by: Museus --- MinibossControl/MinibossControl.lua | 70 +++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 37e8ff0..7e8968a 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -23,6 +23,13 @@ MinibossControl.Presets = { -- Tartarus_Sneak A_MiniBoss03 = 1, + --Megaera + A_Boss01 = true, + --Alecto + A_Boss02 = true, + --Tisiphone + A_Boss03 = true, + -- Asphodel_Barge B_Wrapping01 = 1, -- Asphodel_PowerCouple, @@ -46,6 +53,13 @@ MinibossControl.Presets = { -- Tartarus_Sneak A_MiniBoss03 = 1, + --Megaera + A_Boss01 = true, + --Alecto + A_Boss02 = true, + --Tisiphone + A_Boss03 = true, + -- Asphodel_Barge B_Wrapping01 = 0, -- Asphodel_PowerCouple, @@ -69,6 +83,13 @@ MinibossControl.Presets = { -- Tartarus_Sneak A_MiniBoss03 = 1, + --Megaera + A_Boss01 = true, + --Alecto + A_Boss02 = true, + --Tisiphone + A_Boss03 = true, + -- Asphodel_Barge B_Wrapping01 = 0, -- Asphodel_PowerCouple, @@ -92,6 +113,43 @@ MinibossControl.Presets = { -- Tartarus_Sneak A_MiniBoss03 = 1, + --Megaera + A_Boss01 = true, + --Alecto + A_Boss02 = true, + --Tisiphone + A_Boss03 = true, + + -- Asphodel_Barge + B_Wrapping01 = 0, + -- Asphodel_PowerCouple, + B_MiniBoss01 = 1, + -- Asphodel_Witches + B_MiniBoss02 = 2, + + -- Elysium_ButterflyBall + C_MiniBoss02 = 2, + -- Elysium_Asterius + C_MiniBoss01 = 0, + + -- If true, Tiny Vermin will not spawn + RemoveTinyVermin = true, + }, + Hypermodded = { + -- Tartarus_Bombers + A_MiniBoss01 = 1, + -- Tartarus_Doomstone TODO: Handle middle management? + A_MiniBoss04 = 1, + -- Tartarus_Sneak + A_MiniBoss03 = 1, + + --Megaera + A_Boss01 = true, + --Alecto + A_Boss02 = true, + --Tisiphone + A_Boss03 = false, + -- Asphodel_Barge B_Wrapping01 = 0, -- Asphodel_PowerCouple, @@ -197,3 +255,15 @@ ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) MinibossControl.UpdateMaxCreations() return baseFunc(currentRun) end, MinibossControl) + +-- Remove ineligible Furies +-- Scripts/RunManager.lua : 652 +ModUtil.Path.Wrap("IsRoomEligible", function( baseFunc, currentRun, currentRoom, nextRoomData, args ) + if currentRoom ~= nil and currentRoom.Name == "A_PreBoss01" then + if not MinibossControl.Presets[MinibossControl.config.MinibossSetting][nextRoomData.Name] then + DebugPrint({ Text="Rejecting " .. nextRoomData.Name }) + return false + end + end + return baseFunc(currentRun, currentRoom, nextRoomData, args ) +end, MinibossControl) From 9a97f81d06d26b9b9d537fdfdf8ecbcdea92eecc Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Wed, 16 Nov 2022 16:31:03 +0000 Subject: [PATCH 64/77] feat(ChaosControl): Add ChaosControl mod, allowing certain blessings and curses to be added or removed depending on selected ruleset. Uses new Table.MergeKeyed func from ModUtil 2.8.0 as Table.Merge does not remove remaining curses from LootData table. No config UI as yet, will be written when new UI lib is complete; also remove Debug ruleset at this point as it only exists for testing and should not be displayed. Co-authored-by: SleepSoul#6006 Co-authored-by: Museus --- ChaosControl/ChaosControl.lua | 147 ++++++++++++++++++++++++++++++++++ ChaosControl/modfile.txt | 2 + 2 files changed, 149 insertions(+) create mode 100644 ChaosControl/ChaosControl.lua create mode 100644 ChaosControl/modfile.txt diff --git a/ChaosControl/ChaosControl.lua b/ChaosControl/ChaosControl.lua new file mode 100644 index 0000000..6b56a62 --- /dev/null +++ b/ChaosControl/ChaosControl.lua @@ -0,0 +1,147 @@ +--[[ + ChaosControl + Authors: + SleepSoul (Discord: SleepSoul#6006) + Museus (Discord: Museus#7777) + Change the eligible offerings from Chaos, allowing certain curses or blessings to be removed. +]] +ModUtil.Mod.Register("ChaosControl") + +local config = { + ChaosSetting = "Vanilla" +} +ChaosControl.config = config --TODO add config option in menu +ChaosControl.EligibleBlessings = {} +ChaosControl.EligibleCurses = {} + +ChaosControl.Presets = { --Define rulesets + Vanilla = { + Blessings = { + ChaosBlessingMeleeTrait = true, + ChaosBlessingRangedTrait = true, + ChaosBlessingAmmoTrait = true, + ChaosBlessingMaxHealthTrait = true, + ChaosBlessingBoonRarityTrait = true, + ChaosBlessingMoneyTrait = true, + ChaosBlessingMetapointTrait = true, + ChaosBlessingSecondaryTrait = true, + ChaosBlessingDashAttackTrait = true, + ChaosBlessingExtraChanceTrait = true, + ChaosBlessingBackstabTrait = true, + ChaosBlessingAlphaStrikeTrait = true, + }, + Curses = { + ChaosCurseNoMoneyTrait = true, + ChaosCurseAmmoUseDelayTrait = true, + ChaosCursePrimaryAttackTrait = true, + ChaosCurseSecondaryAttackTrait = true, + ChaosCurseCastAttackTrait = true, + ChaosCurseDeathWeaponTrait = true, + ChaosCurseHiddenRoomReward = true, + ChaosCurseDamageTrait = true, + ChaosCurseTrapDamageTrait = true, + ChaosCurseHealthTrait = true, + ChaosCurseMoveSpeedTrait = true, + ChaosCurseSpawnTrait = true, + ChaosCurseDashRangeTrait = true, + } + }, + Hypermodded = { + Blessings = { + ChaosBlessingMeleeTrait = true, + ChaosBlessingRangedTrait = true, + ChaosBlessingAmmoTrait = true, + ChaosBlessingMaxHealthTrait = true, + ChaosBlessingBoonRarityTrait = true, + ChaosBlessingMoneyTrait = true, + ChaosBlessingSecondaryTrait = true, + ChaosBlessingDashAttackTrait = true, + ChaosBlessingExtraChanceTrait = true, + ChaosBlessingBackstabTrait = true, + ChaosBlessingAlphaStrikeTrait = true, + }, + Curses = { + ChaosCurseNoMoneyTrait = true, + ChaosCurseAmmoUseDelayTrait = true, + ChaosCursePrimaryAttackTrait = true, + ChaosCurseSecondaryAttackTrait = true, + ChaosCurseCastAttackTrait = true, + ChaosCurseDeathWeaponTrait = true, + ChaosCurseHiddenRoomReward = true, + ChaosCurseDamageTrait = true, + ChaosCurseTrapDamageTrait = true, + ChaosCurseHealthTrait = true, + ChaosCurseMoveSpeedTrait = true, + ChaosCurseDashRangeTrait = true, + } + }, + Debug = { + Blessings = { + ChaosBlessingRangedTrait = true, + }, + Curses = { + ChaosCurseTrapDamageTrait = true, + } + } +} + +function ChaosControl.PopulateMinLength(targetTable, inputTable, minLength) --Populates a target table with the contents of an input table, repeatedly inserting until a minimum length is reached. + local i = 0 + while i < minLength do + for _, name in pairs(inputTable) do + table.insert(targetTable, name) + i = i + 1 + end + end +end + +function ChaosControl.GetEligibleTraits(inputTable) + local eligibleTraits = {} + for name, bool in pairs(inputTable) do + if ( bool ) then + table.insert(eligibleTraits, name) + end + end + return eligibleTraits +end + +function ChaosControl.RegisterPreset(name, preset) + ChaosControl.Presets[name] = preset +end + +function ChaosControl.ReadPreset() --Read current preset and create table of blessings and curses marked to eligible, including failsafe for presets with <3 traits, which otherwise cause crash + ChaosControl.EligibleBlessings = {} + ChaosControl.EligibleCurses = {} + ChaosControl.PopulateMinLength( + ChaosControl.EligibleBlessings, + ChaosControl.GetEligibleTraits(ChaosControl.Presets[config.ChaosSetting].Blessings), + 3 + ) + ChaosControl.PopulateMinLength( + ChaosControl.EligibleCurses, + ChaosControl.GetEligibleTraits(ChaosControl.Presets[config.ChaosSetting].Curses), + 3 + ) +end + +function ChaosControl.UpdateOfferings() --Inject eligible blessings/curses into table of Chaos' offerings in LootData + ModUtil.Table.MergeKeyed(LootData, { + TrialUpgrade = { + PermanentTraits = ChaosControl.EligibleBlessings, + TemporaryTraits = ChaosControl.EligibleCurses, + } + }) + DebugPrint({Text = "Chaos Offerings: "..config.ChaosSetting}) +end + +ModUtil.LoadOnce( function() + ChaosControl.ReadPreset() + ChaosControl.UpdateOfferings() +end) + +-- When a new run is started, make sure to apply the offering settings +ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) + ChaosControl.ReadPreset() + ChaosControl.UpdateOfferings() + return baseFunc(currentRun) +end, ChaosControl) diff --git a/ChaosControl/modfile.txt b/ChaosControl/modfile.txt new file mode 100644 index 0000000..2e359a8 --- /dev/null +++ b/ChaosControl/modfile.txt @@ -0,0 +1,2 @@ +:: Miniboss Control +Import "ChaosControl.lua" From 818536c5a8ab1715590a689111d17e3d582e580e Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Wed, 16 Nov 2022 20:50:46 +0000 Subject: [PATCH 65/77] build(deps): update ModUtil to v2.8.0 --- ModUtil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ModUtil b/ModUtil index 8c67846..d1e9b33 160000 --- a/ModUtil +++ b/ModUtil @@ -1 +1 @@ -Subproject commit 8c678461717ab7972675ace28851e8e65d63fd82 +Subproject commit d1e9b330cac5c878096462a3731b7b0fe6aebaf7 From 7ce27abe5c7c8d8081b0c53dc11eacc59131ba4c Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Wed, 16 Nov 2022 22:44:08 +0000 Subject: [PATCH 66/77] (refactor)(MinibossControl): Simplify Fury code using ModUtil v2.8.0 function Table.MergeKeyed; Remove previous Path.Wrap --- MinibossControl/MinibossControl.lua | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/MinibossControl/MinibossControl.lua b/MinibossControl/MinibossControl.lua index 7e8968a..b8463d8 100644 --- a/MinibossControl/MinibossControl.lua +++ b/MinibossControl/MinibossControl.lua @@ -174,7 +174,19 @@ end -- Apply the configured miniboss settings to the game data function MinibossControl.UpdateMaxCreations() - ModUtil.Table.Merge(RoomSetData, { + AllowedBossesTartarus = {} + + if MinibossControl.Presets[config.MinibossSetting].A_Boss01 then + table.insert(AllowedBossesTartarus,"A_Boss01") + end + if MinibossControl.Presets[config.MinibossSetting].A_Boss02 then + table.insert(AllowedBossesTartarus,"A_Boss02") + end + if MinibossControl.Presets[config.MinibossSetting].A_Boss03 then + table.insert(AllowedBossesTartarus,"A_Boss03") + end + + ModUtil.Table.MergeKeyed(RoomSetData, { --Table.Merge doesn't remove unwanted furies from LinkedRooms; this sets them to nil -- [[ Tartarus Miniboss Counts ]] Tartarus = { A_MiniBoss01 = { @@ -191,6 +203,9 @@ function MinibossControl.UpdateMaxCreations() -- Vanilla Doomstone MaxCreationsThisRun = MinibossControl.Presets[config.MinibossSetting].A_MiniBoss04, }, + A_PreBoss01 = { + LinkedRooms = AllowedBossesTartarus, + } }, -- [[ Asphodel Miniboss Counts ]] Asphodel = { @@ -255,15 +270,3 @@ ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) MinibossControl.UpdateMaxCreations() return baseFunc(currentRun) end, MinibossControl) - --- Remove ineligible Furies --- Scripts/RunManager.lua : 652 -ModUtil.Path.Wrap("IsRoomEligible", function( baseFunc, currentRun, currentRoom, nextRoomData, args ) - if currentRoom ~= nil and currentRoom.Name == "A_PreBoss01" then - if not MinibossControl.Presets[MinibossControl.config.MinibossSetting][nextRoomData.Name] then - DebugPrint({ Text="Rejecting " .. nextRoomData.Name }) - return false - end - end - return baseFunc(currentRun, currentRoom, nextRoomData, args ) -end, MinibossControl) From bff7fe38eebbaa9fd77a9443b908ea6861a21ecb Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Sat, 26 Nov 2022 23:16:58 +0000 Subject: [PATCH 67/77] build(deps): update ModUtil to v2.8.1 --- ModUtil | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ModUtil b/ModUtil index d1e9b33..7f97ae5 160000 --- a/ModUtil +++ b/ModUtil @@ -1 +1 @@ -Subproject commit d1e9b330cac5c878096462a3731b7b0fe6aebaf7 +Subproject commit 7f97ae5330f4e7bf0ff398ecfbbd0d8bf6a375a3 From 5c407f5a9e40f3f01c63b94b9c1bf0cd2d338f19 Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Sat, 26 Nov 2022 23:22:24 +0000 Subject: [PATCH 68/77] feat(EnemyControl): Add EnemyControl mod, allowing enemy pools to be defined on a per-biome basis, or enemies to be removed from the vanilla pools. --- EnemyControl/EnemyControl.lua | 178 ++++++++++++++++++++++++++++++++++ EnemyControl/Map.lua | 108 +++++++++++++++++++++ EnemyControl/modfile.txt | 3 + 3 files changed, 289 insertions(+) create mode 100644 EnemyControl/EnemyControl.lua create mode 100644 EnemyControl/Map.lua create mode 100644 EnemyControl/modfile.txt diff --git a/EnemyControl/EnemyControl.lua b/EnemyControl/EnemyControl.lua new file mode 100644 index 0000000..e19bbd1 --- /dev/null +++ b/EnemyControl/EnemyControl.lua @@ -0,0 +1,178 @@ +--[[ + EnemyControl + Authors: + SleepSoul (Discord: SleepSoul#6006) + Museus (Discord: Museus#7777) + Change the pool of enemies eligible in each room, allowing certain enemy types to be removed. +]] +ModUtil.Mod.Register("EnemyControl") + +local config = { + EnemySetting = "Vanilla" +} +EnemyControl.config = config --TODO add config option in menu + +EnemyControl.Presets = { --Define rulesets + Vanilla = {}, + Debug = { + Tartarus = { + Lout = true, + } + }, + Hypermodded1 = { + StyxSmallRoom = { + TinyRat = false, + } + }, + Hypermodded2 = { + Tartarus = { + Numbskull = false, + Witch = false, + }, + Asphodel = { + Bloodless = false, + Gorgon = false, + }, + Elysium = { + Spearman = false, + Bowman = false, + Shieldsman = false, + Swordsman = false, + }, + StyxSmallRoom = { + TinyRat = false, + } + } +} + +EnemyControl.PresetTypes = { + Hypermodded1 = { + StyxSmallRoom = true, + }, + Hypermodded2 = { + Tartarus = true, + Asphodel = true, + Elysium = true, + StyxSmallRoom = true, + } +} + +function EnemyControl.PopulateMinLength(targetTable, inputTable, minLength) --Populates a target table with the contents of an input table, repeatedly inserting until a minimum length is reached. + local i = 0 + while i < minLength do + for _, name in pairs(inputTable) do + table.insert(targetTable, name) + i = i + 1 + end + end +end + +function EnemyControl.GetEligible(inputTable) + local eligible = {} + for name, bool in pairs(inputTable) do + if ( bool ) then + table.insert(eligible, EnemyControl.NameToCode.Enemies[name]) + DebugPrint({Text = EnemyControl.NameToCode.Enemies[name]}) + end + end + return eligible +end + +function EnemyControl.RemoveIneligibleFromBools(inputTable,baseTable) + local eligible = {} + local check = "" + local match = false + for name, bool in pairs(baseTable) do + match = false + check = name + DebugPrint({Text = check}) + if ( bool ) then + if ( next(inputTable) == nil ) then + table.insert(eligible,check) + else + for name2, bool2 in pairs(inputTable) do + if ( EnemyControl.NameToCode.Enemies[name2] == check and bool2 == false ) then + match = true + end + end + if ( match == false ) then + table.insert(eligible,check) + end + end + end + end + return eligible +end + +function EnemyControl.RemoveIneligibleFromStrings(inputTable,baseTable) + local eligible = {} + local check = "" + local match = false + for _, name in ipairs(baseTable) do + match = false + check = name + if ( next(inputTable) == nil ) then + table.insert(eligible,name) + else + for name2, bool in pairs(inputTable) do + DebugPrint({Text = name2}) + if ( EnemyControl.NameToCode.Enemies[name2] == check and bool == false ) then + match = true + end + end + if ( match == false ) then + table.insert(eligible,name) + end + end + end + return eligible +end + +function EnemyControl.RegisterPreset(name, preset) + EnemyControl.Presets[name] = preset +end + +function EnemyControl.ReadPreset() --Read current preset and create table of enemies marked as eligible + EnemyControl.EligibleEnemies = {} + local Preset = EnemyControl.Presets[config.EnemySetting] + local PresetType = EnemyControl.PresetTypes[config.EnemySetting] + for biome, _ in pairs(Preset) do + EnemyControl.EligibleEnemies[biome] = {} + if ( PresetType[biome] == true ) then + EnemyControl.PopulateMinLength( + EnemyControl.EligibleEnemies[biome], + EnemyControl.RemoveIneligibleFromStrings(Preset[biome],EnemyControl.VanillaSets[EnemyControl.NameToCode.Biomes[biome]]), + 1 + ) + else + EnemyControl.PopulateMinLength( + EnemyControl.EligibleEnemies[biome], + EnemyControl.GetEligible(Preset[biome]), + 1 + ) + end + end +end + +function EnemyControl.UpdatePools() + for biome, pool in pairs(EnemyControl.EligibleEnemies) do + EnemyControl.Target = EnemyControl.NameToCode.Biomes[biome] + EnemyControl.Pool = pool + DebugPrint({Text = EnemyControl.Target}) + DebugPrint({Text = EnemyControl.Pool}) + ModUtil.Table.Replace(EnemySets[EnemyControl.Target], EnemyControl.Pool) + end +end + +ModUtil.LoadOnce( function() + EnemyControl.VanillaSets = ModUtil.Table.Copy(EnemySets) + EnemyControl.ReadPreset() + EnemyControl.UpdatePools() +end) + +-- When a new run is started, make sure to apply the pool settings +ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) + EnemyControl.ReadPreset() + EnemyControl.UpdatePools() + return baseFunc(currentRun) +end, EnemyControl) diff --git a/EnemyControl/Map.lua b/EnemyControl/Map.lua new file mode 100644 index 0000000..1fa629f --- /dev/null +++ b/EnemyControl/Map.lua @@ -0,0 +1,108 @@ +EnemyControl.NameToCode = { + Enemies = { + ArmoredNumbskull = "SwarmerElite", + ArmoredThug = "HeavyMeleeElite", + ArmoredWringer = "DisembodiedHandElite", + ArmoredLout = "PunchingBagElite", + ArmoredPest = "ThiefMineLayerElite", + ArmoredBother = "ThiefImpulseMineLayerElite", + ArmoredWitch = "LightRangedElite", + ArmoredSpreader = "SpreadShotUnitElite", + ArmoredSplitter = "SplitShotUnitElite", + ArmoredBrimstone = "HeavyRangedElite", + ArmoredSnakestone = "HeavyRangedForkElite", + ArmoredDoomstone = "HeavyRangedSplitterElite", + ArmoredDoomstoneFragment = "HeavyRangedSplitterFragmentElite", + ArmoredVoidstone = "ShieldRangedElite", + ArmoredDracon = "RangedBurrowerElite", + ArmoredChariot = "ChariotElite", + ArmoredFlamewheel = "ChariotSuicideElite", + ArmoredGhostShield = "SupportShieldsElite", + ArmoredSkullomat = "LightSpawnerElite", + ArmoredSoulCatcher = "FlurrySpawnerElite", + ArmoredBloodless = "BloodlessNakedElite", + ArmoredBoneRaker = "BloodlessNakedBerserkerElite", + ArmoredWaveMaker = "BloodlessWaveFistElite", + ArmoredInfernoBomber = "BloodlessGrenadierElite", + ArmoredSlamDancer = "BloodlessSelfDestructElite", + ArmoredBurnFlinger = "BloodlessPitcherElite", + ArmoredSkullCrusher = "CrusherElite", + ArmoredGorgon = "FreezeShotElite", + ArmoredMegaGorgon = "HitAndRunElite", + ArmoredEyeball = "ShadeNakedElite", + ArmoredArcher = "ShadeBowUnitElite", + ArmoredSpearman = "ShadeSpearUnitElite", + ArmoredShield = "ShadeShieldUnitElite", + ArmoredSwordsman = "ShadeSwordUnitElite", + ArmoredSatyr = "SatyrRangedElite", + ArmoredTinyRat = "CrawlerElite", + ArmoredRat = "RatThugElite", + Numbskull = "Swarmer", + Thug = "HeavyMelee", + Wringer = "DisembodiedHand", + Lout = "PunchingBag", + Pest = "ThiefMineLayer", + Bother = "ThiefImpulseMineLayer", + Witch = "LightRanged", + Spreader = "SpreadShotUnit", + Splitter = "SplitShotUnit", + Brimstone = "HeavyRanged", + Snakestone = "HeavyRangedFork", + Doomstone = "HeavyRangedSplitter", + DoomstoneFragment = "HeavyRangedSplitterFragment", + Voidstone = "ShieldRanged", + Dracon = "RangedBurrower", + Chariot = "Chariot", + Flamewheel = "ChariotSuicide", + GhostShield = "SupportShields", + Skullomat = "LightSpawner", + SoulCatcher = "FlurrySpawner", + Bloodless = "BloodlessNaked", + BoneRaker = "BloodlessNakedBerserker", + WaveMaker = "BloodlessWaveFist", + InfernoBomber = "BloodlessGrenadier", + SlamDancer = "BloodlessSelfDestruct", + BurnFlinger = "BloodlessPitcher", + SkullCrusher = "CrusherUnit", + Gorgon = "FreezeShotUnit", + MegaGorgon = "HitAndRun", + Eyeball = "ShadeNaked", + Bowman = "ShadeBowUnit", + Spearman = "ShadeSpearUnit", + Shieldsman = "ShadeShieldUnit", + Swordsman = "ShadeSwordUnit", + Satyr = "SatyrRanged", + TinyRat = "Crawler", + Rat = "RatThug", + }, + Biomes = { + Tartarus = "EnemiesBiome1", + TartarusTrial = "EnemiesBiome1Devotion", + TartarusSurvival = "EnemiesBiome1Survival", + TartarusElite = "EnemiesBiome1Hard", + TartarusThanatos = "EnemiesBiome1Thanatos", + Asphodel = "EnemiesBiome2", + AsphodelTrial = "EnemiesBiome2Devotion", + AsphodelSurvival = "EnemiesBiome2Survival", -- it's here if you ever wanna use it LMAO + AsphodelElite = "EnemiesBiome2Hard", + AsphodelThanatos = "EnemiesBiome2Thanatos", + AsphodelBarge = "EnemiesBiome2Wrapping", + AsphodelTrove = "EnemiesBiome2Challenge", + Elysium = "EnemiesBiome3", + ElysiumTrial = "EnemiesBiome3Devotion", + ElysiumSurvival = "EnemiesBiome3Survival", + ElysiumElite = "EnemiesBiome3Hard", + StyxBigRoom = "EnemiesBiome4Mini", + StyxSmallRoom = "EnemiesBiome4Mini", + StyxSmallRoomElite = "EnemiesBiome4MiniHard", + StyxMinibossAdds = "EnemiesBiome4MiniBossFodder", + HydraHeads = "HydraHeads" + } +} +EnemyControl.CodeToName = { + Enemies = {}, + Biomes = {} +} + +EnemyControl.CodeToName.Enemies = ModUtil.Table.Transpose(EnemyControl.NameToCode.Enemies) +EnemyControl.CodeToName.Biomes = ModUtil.Table.Transpose(EnemyControl.NameToCode.Biomes) diff --git a/EnemyControl/modfile.txt b/EnemyControl/modfile.txt new file mode 100644 index 0000000..30425bc --- /dev/null +++ b/EnemyControl/modfile.txt @@ -0,0 +1,3 @@ +:: Enemy Control +Import "EnemyControl.lua" +Import "Map.lua" From ed2c02e04a09e04388c685637e2f740c688dba8f Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Sun, 27 Nov 2022 21:33:38 +0000 Subject: [PATCH 69/77] feat(SatyrSackControl): Add option to force short tunnels Wraps IsRoomEligible to mark the small rooms as ineligible if 3 rooms or higher into a tunnel. --- SatyrSackControl/SatyrSackControl.lua | 86 +++++++++++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/SatyrSackControl/SatyrSackControl.lua b/SatyrSackControl/SatyrSackControl.lua index 4e48edd..57335c6 100644 --- a/SatyrSackControl/SatyrSackControl.lua +++ b/SatyrSackControl/SatyrSackControl.lua @@ -9,6 +9,7 @@ ModUtil.Mod.Register("SatyrSackControl") local config = { Enabled = true, -- If true, sack wil fall between MinSack and MaxSack + ForceShortTunnels = false, -- If true, all tunnels will be short MinSack = 2, -- Lowest tunnel count to see Sack MaxSack = 2 -- Highest tunnel count to see Sack } @@ -105,3 +106,88 @@ ModUtil.Path.Override("IsRoomForced", function(currentRun, currentRoom, nextRoom return false end) + +-- Scripts/RunManager.lua : 652 +ModUtil.Path.Override("IsRoomEligible", function(currentRun, currentRoom, nextRoomData, args) + if args == nil then + args = {} + end + + if nextRoomData == nil then + return false + end + + local excludedNames = args.ExcludedNames + local excludedTypes = args.ExcludedTypes + local excludedRewards = args.ExcludedRewards + local roomsSkipped = args.DepthSkip or 0 + + if nextRoomData.DebugOnly then + return false + end + + if excludedNames ~= nil and excludedNames[nextRoomData.Name] then + return false + end + + if excludedTypes ~= nil and excludedTypes[nextRoomData.Type] then + return false + end + + if excludedRewards ~= nil and not IsEmpty(nextRoomData.RewardTypes) and ContainsAll(excludedRewards, nextRoomData.RewardTypes) then + return false + end + + if nextRoomData.ForceAtBiomeDepth ~= nil and currentRun.BiomeDepthCache ~= nextRoomData.ForceAtBiomeDepth then + return false + end + if nextRoomData.ForceAtBiomeDepthMin ~= nil and currentRun.BiomeDepthCache < nextRoomData.ForceAtBiomeDepthMin then + return false + end + + if currentRoom ~= nil then + if nextRoomData.Name == currentRoom.Name then + return false + end + if nextRoomData.Starting and not currentRoom.AllowNextRoomStarting then + return false + end + if nextRoomData.RequiresLinked then + if currentRoom.LinkedRoom ~= nextRoomData.Name and ( currentRoom.LinkedRooms == nil or not Contains( currentRoom.LinkedRooms, nextRoomData.Name ) ) then + return false + end + end + + -- If in a MiniBoss wing and we are not a MiniBoss, we are ineligible (except Reprieve) + if currentRoom.RequireWingEndMiniBoss and nextRoomData.WingEndRoom and not nextRoomData.WingEndMiniBoss and not nextRoomData.AllowAsAnyWingEnd then + return false + end + + -- If in a regular wing and we are a MiniBoss room, we are ineligible (except Reprieve) + if not currentRoom.RequireWingEndMiniBoss and nextRoomData.WingEndRoom and nextRoomData.WingEndMiniBoss and not nextRoomData.AllowAsAnyWingEnd then + return false + end + + -- [[ CHANGES MADE HERE ]] + if SatyrSackControl.config.ForceShortTunnels and currentRun.WingDepth >= 3 and not nextRoomData.WingEndRoom then + return false + end + -- [[ END OF CHANGES ]] + end + + if nextRoomData.MaxAppearancesThisRun ~= nil and currentRun.RoomCountCache[nextRoomData.Name] ~= nil and currentRun.RoomCountCache[nextRoomData.Name] >= nextRoomData.MaxAppearancesThisRun then + return false + end + if nextRoomData.MaxAppearancesThisBiome ~= nil and currentRun.BiomeRoomCountCache[nextRoomData.Name] ~= nil and currentRun.BiomeRoomCountCache[nextRoomData.Name] >= nextRoomData.MaxAppearancesThisBiome then + return false + end + + if nextRoomData.MaxCreationsThisRun ~= nil and currentRun.RoomCreations[nextRoomData.Name] ~= nil and currentRun.RoomCreations[nextRoomData.Name] >= nextRoomData.MaxCreationsThisRun then + return false + end + + if not IsGameStateEligible( currentRun, nextRoomData , nextRoomData.GameStateRequirements, args) then + return false + end + return true +end) \ No newline at end of file From af792237b5135f6a9da92e8f718bb663ac182ee3 Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Wed, 30 Nov 2022 13:45:24 +0000 Subject: [PATCH 70/77] refactor(SatyrSackControl): Replace Path.Override with Path.Wrap for both features Path.Wrap is simpler and avoids any future messiness with other mods that need to edit IsRoomEligible and IsRoomForced. Author: Zyruvias#3283 --- SatyrSackControl/SatyrSackControl.lua | 174 +++----------------------- 1 file changed, 17 insertions(+), 157 deletions(-) diff --git a/SatyrSackControl/SatyrSackControl.lua b/SatyrSackControl/SatyrSackControl.lua index 57335c6..36dbe0c 100644 --- a/SatyrSackControl/SatyrSackControl.lua +++ b/SatyrSackControl/SatyrSackControl.lua @@ -2,14 +2,15 @@ SatyrSackControl v1.0 Author: Museus (Discord: Museus#7777) - + SleepSoul (Discord: SleepSoul#6006) + Zyruvias (Discord: Zyruvias#3283) Forces the Styx satyr sack to appear in a specified range. ]] ModUtil.Mod.Register("SatyrSackControl") local config = { Enabled = true, -- If true, sack wil fall between MinSack and MaxSack - ForceShortTunnels = false, -- If true, all tunnels will be short + ForceShortTunnels = true, -- If true, all tunnels will be short MinSack = 2, -- Lowest tunnel count to see Sack MaxSack = 2 -- Highest tunnel count to see Sack } @@ -26,168 +27,27 @@ ModUtil.Path.Wrap("ShowCombatUI", function ( baseFunc, flag ) end, SatyrSackControl) -- Scripts/RunManager.lua : 591 -ModUtil.Path.Override("IsRoomForced", function(currentRun, currentRoom, nextRoomData, args) - if nextRoomData.AlwaysForce then - return true - end - - if - nextRoomData.ForceIfEncounterNotCompleted ~= nil and - not HasEncounterBeenCompleted(nextRoomData.ForceIfEncounterNotCompleted) - then - return true - end - - if - nextRoomData.ForceIfUnseenForRuns ~= nil and - not HasSeenRoomInNumRuns(nextRoomData.Name, nextRoomData.ForceIfUnseenForRuns) - then - DebugPrint({Text = "Forcing = " .. nextRoomData.Name}) - return true - end - - args = args or {} - - local depthSkip = args.RoomsSkipped or 0 - local currentRunDepth = currentRun.RunDepthCache + depthSkip - if nextRoomData.ForceAtRunDepth ~= nil and currentRunDepth == nextRoomData.ForceAtRunDepth then - return true - end - if nextRoomData.ForceAtRunDepthMin ~= nil and currentRunDepth >= nextRoomData.ForceAtRunDepthMin then - if currentRunDepth >= nextRoomData.ForceAtRunDepthMax then - return true - else - local forcedChance = 1 / (nextRoomData.ForceAtRunDepthMax - currentRunDepth) - if RandomChance(forcedChance) then - return true - end - end - end - local currentBiomeDepth = currentRun.BiomeDepthCache + depthSkip - if nextRoomData.ForceAtBiomeDepth ~= nil and currentBiomeDepth == nextRoomData.ForceAtBiomeDepth then - return true - end - if nextRoomData.ForceAtBiomeDepthMin ~= nil and currentBiomeDepth >= nextRoomData.ForceAtBiomeDepthMin then - if currentBiomeDepth >= nextRoomData.ForceAtBiomeDepthMax then - return true - else - local forcedChance = 1 / (nextRoomData.ForceAtBiomeDepthMax - currentBiomeDepth) - if RandomChance(forcedChance) then - return true - end - end - end - - if - currentRoom ~= nil and currentRoom.ForceWingEndMiniBoss and nextRoomData.WingEndMiniBoss and - (currentRun.CompletedStyxWings < 4 or HasSeenRoomInRun(currentRun, "D_Reprieve01")) - then - return true - end - +ModUtil.Path.Wrap("IsRoomForced", function ( baseFunction, currentRun, currentRoom, nextRoomData, args ) if nextRoomData.ForceChanceByRemainingWings then - -- [[ CHANGES MADE HERE ]] - if SatyrSackControl.config.Enabled then - if (currentRun.CompletedStyxWings + 1) < SatyrSackControl.config.MinSack then - return false - end - - if (currentRun.CompletedStyxWings + 1) >= SatyrSackControl.config.MaxSack then - return true + if SatyrSackControl.config.Enabled then + if (currentRun.CompletedStyxWings + 1) < SatyrSackControl.config.MinSack then + return false + end + + if (currentRun.CompletedStyxWings + 1) >= SatyrSackControl.config.MaxSack then + return true + end end end - -- [[ END OF CHANGES ]] - - local chance = 1 / (5 - currentRun.CompletedStyxWings) - if RandomChance(chance) then - return true - end - end - - return false -end) + return baseFunction( currentRun, currentRoom, nextRoomData, args ) + end) -- Scripts/RunManager.lua : 652 -ModUtil.Path.Override("IsRoomEligible", function(currentRun, currentRoom, nextRoomData, args) - if args == nil then - args = {} - end - - if nextRoomData == nil then - return false - end - - local excludedNames = args.ExcludedNames - local excludedTypes = args.ExcludedTypes - local excludedRewards = args.ExcludedRewards - local roomsSkipped = args.DepthSkip or 0 - - if nextRoomData.DebugOnly then - return false - end - - if excludedNames ~= nil and excludedNames[nextRoomData.Name] then - return false - end - - if excludedTypes ~= nil and excludedTypes[nextRoomData.Type] then - return false - end - - if excludedRewards ~= nil and not IsEmpty(nextRoomData.RewardTypes) and ContainsAll(excludedRewards, nextRoomData.RewardTypes) then - return false - end - - if nextRoomData.ForceAtBiomeDepth ~= nil and currentRun.BiomeDepthCache ~= nextRoomData.ForceAtBiomeDepth then - return false - end - if nextRoomData.ForceAtBiomeDepthMin ~= nil and currentRun.BiomeDepthCache < nextRoomData.ForceAtBiomeDepthMin then - return false - end - +ModUtil.Path.Wrap("IsRoomEligible", function ( baseFunction, currentRun, currentRoom, nextRoomData, args ) if currentRoom ~= nil then - if nextRoomData.Name == currentRoom.Name then - return false - end - if nextRoomData.Starting and not currentRoom.AllowNextRoomStarting then - return false - end - if nextRoomData.RequiresLinked then - if currentRoom.LinkedRoom ~= nextRoomData.Name and ( currentRoom.LinkedRooms == nil or not Contains( currentRoom.LinkedRooms, nextRoomData.Name ) ) then - return false - end - end - - -- If in a MiniBoss wing and we are not a MiniBoss, we are ineligible (except Reprieve) - if currentRoom.RequireWingEndMiniBoss and nextRoomData.WingEndRoom and not nextRoomData.WingEndMiniBoss and not nextRoomData.AllowAsAnyWingEnd then - return false - end - - -- If in a regular wing and we are a MiniBoss room, we are ineligible (except Reprieve) - if not currentRoom.RequireWingEndMiniBoss and nextRoomData.WingEndRoom and nextRoomData.WingEndMiniBoss and not nextRoomData.AllowAsAnyWingEnd then - return false - end - - -- [[ CHANGES MADE HERE ]] - if SatyrSackControl.config.ForceShortTunnels and currentRun.WingDepth >= 3 and not nextRoomData.WingEndRoom then + if SatyrSackControl.config.ForceShortTunnels and currentRun.WingDepth >= 3 and not nextRoomData.WingEndRoom then return false end - -- [[ END OF CHANGES ]] - end - - if nextRoomData.MaxAppearancesThisRun ~= nil and currentRun.RoomCountCache[nextRoomData.Name] ~= nil and currentRun.RoomCountCache[nextRoomData.Name] >= nextRoomData.MaxAppearancesThisRun then - return false - end - if nextRoomData.MaxAppearancesThisBiome ~= nil and currentRun.BiomeRoomCountCache[nextRoomData.Name] ~= nil and currentRun.BiomeRoomCountCache[nextRoomData.Name] >= nextRoomData.MaxAppearancesThisBiome then - return false - end - - if nextRoomData.MaxCreationsThisRun ~= nil and currentRun.RoomCreations[nextRoomData.Name] ~= nil and currentRun.RoomCreations[nextRoomData.Name] >= nextRoomData.MaxCreationsThisRun then - return false - end - - if not IsGameStateEligible( currentRun, nextRoomData , nextRoomData.GameStateRequirements, args) then - return false end - return true + return baseFunction( currentRun, currentRoom, nextRoomData, args ) end) \ No newline at end of file From f6f85599c43998ebfe0a940802e5e9ae9169fb42 Mon Sep 17 00:00:00 2001 From: Zyruvias Date: Wed, 30 Nov 2022 08:59:15 -0500 Subject: [PATCH 71/77] use wraps --- QuickRestart/QuickRestart.lua | 92 +++++++---------------------------- 1 file changed, 17 insertions(+), 75 deletions(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index 9342a5d..f25efe2 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -129,87 +129,29 @@ OnAnyLoad{ "RoomPreRun", end } -ModUtil.Path.Override( "HandleDeath", function( currentRun, killer, killingUnitWeapon ) - if GetConfigOptionValue({ Name = "EditingMode" }) then - SetAnimation({ Name = "ZagreusDeadStartBlood", DestinationId = currentRun.Hero.ObjectId }) - return - end - +ModUtil.Path.Wrap("HandleDeath", function(baseFunc, currentRun, killer, killingUnitWeapon) if QuickRestart.UsedQuickRestart then RemoveInputBlock({ Name = "QuickRestart" }) end + QuickRestart.QuickDeathApplicable = config.QuickDeathEnabled and not currentRun.Cleared + return baseFunc(currentRun, killer, killingUnitWeapon) +end, QuickRestart) - SendSaveFileEmail({ }) - - AddTimerBlock( currentRun, "HandleDeath" ) - if ScreenAnchors.TraitTrayScreen ~= nil then - CloseAdvancedTooltipScreen() - end - ClearHealthShroud() - CurrentRun.Hero.HandlingDeath = true - CurrentRun.Hero.IsDead = true - CurrentRun.ActiveBiomeTimer = false - if ConfigOptionCache.EasyMode and not currentRun.Cleared then - GameState.EasyModeLevel = GameState.EasyModeLevel + 1 - end - if not CurrentRun.Cleared then -- Already recorded if cleared - RecordRunStats() - end - - InvalidateCheckpoint() - - ZeroSuperMeter() - FinishTargetMarker( killer ) - - local deathPresentationName = currentRun.DeathPresentationFunctionName or "DeathPresentation" - local deathPresentationFunction = _G[deathPresentationName] - deathPresentationFunction( currentRun, killer, killingUnitWeapon ) - AddInputBlock({ Name = "MapLoad" }) - - currentRun.CurrentRoom.EndingHealth = currentRun.Hero.Health - currentRun.EndingMoney = currentRun.Money - table.insert( currentRun.RoomHistory, currentRun.CurrentRoom ) - UpdateRunHistoryCache( currentRun, currentRun.CurrentRoom ) - - currentRun.Money = 0 - currentRun.NumRerolls = GetNumMetaUpgrades( "RerollMetaUpgrade" ) + GetNumMetaUpgrades("RerollPanelMetaUpgrade") - - ResetObjectives() - ActiveScreens = {} - - CurrentRun.Hero.HandlingDeath = false - CurrentRun.Hero.Health = CurrentRun.Hero.MaxHealth - - local currentRoom = currentRun.CurrentRoom - local deathMap = "DeathArea" - - -- Changes Here - local quickDeathApplicable = config.QuickDeathEnabled and not currentRun.Cleared - if QuickRestart.UsedQuickRestart or quickDeathApplicable then - deathMap = "RoomPreRun" - - if QuickRestart.KeepStartingKeepsake and GameState.QuickRestartStartingKeepsake then - GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake - end - end - - -- Set UsedQuickRestart Flag so Keepsake and InputBlock are appropriately set. - if quickDeathApplicable then - QuickRestart.UsedQuickRestart = true - end - -- End Changes - - GameState.LocationName = "Location_Home" - RandomSetNextInitSeed() - SaveCheckpoint({ StartNextMap = deathMap, DevSaveName = CreateDevSaveName( currentRun, { StartNextMap = deathMap } ) }) - ClearUpgrades() - - SetConfigOption({ Name = "FlipMapThings", Value = false }) +ModUtil.Path.Context.Wrap("HandleDeath", function () + ModUtil.Path.Wrap("LoadMap", function(baseFunc, argTable) + if QuickRestart.UsedQuickRestart or QuickRestart.QuickDeathApplicable then + argTable.Name = "RoomPreRun" - local runNumber = ( GetCompletedRuns() + 1 ) - local runDepth = GetRunDepth( currentRun ) + if QuickRestart.KeepStartingKeepsake and GameState.QuickRestartStartingKeepsake then + GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake + end + end - LoadMap({ Name = deathMap, ResetBinks = true, ResetWeaponBinks = true }) + -- Set UsedQuickRestart Flag so Keepsake and InputBlock are appropriately set. + if quickDeathApplicable then + QuickRestart.UsedQuickRestart = true + end + end, QuickRestart) end, QuickRestart) ModUtil.Path.Wrap("WindowDropEntrance", function( baseFunc, ... ) From 15a21c12e793da9bfde2c3581bf8931f9ac1614a Mon Sep 17 00:00:00 2001 From: Zyruvias Date: Mon, 30 May 2022 13:57:33 -0400 Subject: [PATCH 72/77] add ModUtil to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e915029..4ff58a4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ ################################################################################ /.vs +ModUtil/** \ No newline at end of file From 094d7dcf00062485814799a1ff120ebfbdca7cf2 Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Thu, 1 Dec 2022 22:29:08 +0000 Subject: [PATCH 73/77] feat(RCLib): Add RCLib mod Standardises functions used in ChaosControl and EnemyControl, as well as a lookup table for readable alternatives to internal names. --- RCLib/Map.lua | 260 ++++++++++++++++++++++++++++++++++++++++++++++ RCLib/RCLib.lua | 80 ++++++++++++++ RCLib/modfile.txt | 3 + 3 files changed, 343 insertions(+) create mode 100644 RCLib/Map.lua create mode 100644 RCLib/RCLib.lua create mode 100644 RCLib/modfile.txt diff --git a/RCLib/Map.lua b/RCLib/Map.lua new file mode 100644 index 0000000..f5349c8 --- /dev/null +++ b/RCLib/Map.lua @@ -0,0 +1,260 @@ +RCLib.NameToCode = { + ChaosBlessings = { + -- Uses in-game names for blessings and curses. + -- Several curses and blessings are unused- these are not currently included. + Strike = "ChaosBlessingMeleeTrait", + Shot = "ChaosBlessingRangedTrait", + Grasp = "ChaosBlessingAmmoTrait", + Soul = "ChaosBlessingMaxHealthTrait", + Favor = "ChaosBlessingBoonRarityTrait", + Affluence = "ChaosBlessingMoneyTrait", + Eclipse = "ChaosBlessingMetapointTrait", + Flourish = "ChaosBlessingSecondaryTrait", + Lunge = "ChaosBlessingDashAttackTrait", + Defiance = "ChaosBlessingExtraChanceTrait", + Ambush = "ChaosBlessingBackstabTrait", + Assault = "ChaosBlessingAlphaStrikeTrait", + }, + ChaosCurses = { + Paupers = "ChaosCurseNoMoneyTrait", + Slippery = "ChaosCurseAmmoUseDelayTrait", + Maimed = "ChaosCursePrimaryAttackTrait", + Flayed = "ChaosCurseSecondaryAttackTrait", + Addled = "ChaosCurseCastAttackTrait", + Caustic = "ChaosCurseDeathWeaponTrait", + Enshrouded = "ChaosCurseHiddenRoomReward", + Excruciating = "ChaosCurseDamageTrait", + Abyssal = "ChaosCurseTrapDamageTrait", + Atrophic = "ChaosCurseHealthTrait", + Slothful = "ChaosCurseMoveSpeedTrait", + Roiling = "ChaosCurseSpawnTrait", + Halting = "ChaosCurseDashRangeTrait", + }, + Enemies = { + -- Uses in-game names for enemies except when a fan name is much more popular. + -- Elite is referred to as Armored due to this being the more common term. + -- Unused enemies, bosses, and enemies not in EnemyData are currently not included. + -- Note that some enemies do not have Super Elite variations, some Elite variations only appear as midbosses, etc. + + -- Tartarus Enemies + Brimstone = "HeavyRanged", + Lout = "PunchingBag", + Numbskull = "Swarmer", + Pest = "ThiefMineLayer", + Skullomat = "LightSpawner", + Thug = "HeavyMelee", + Witch = "LightRanged", + Wringer = "DisembodiedHand", + + -- Tartarus Armored + ArmoredBrimstone = "HeavyRangedElite", + ArmoredLout = "PunchingBagElite", + ArmoredNumbskull = "SwarmerElite", + ArmoredPest = "ThiefMineLayerElite", + ArmoredSkullomat = "LightSpawnerElite", + ArmoredThug = "HeavyMeleeElite", + ArmoredWitch = "LightRangedElite", + ArmoredWringer = "DisembodiedHandElite", + + -- Tartarus Super Elite + SuperEliteLout = "PunchingBagSuperElite", + SuperEliteNumbskull = "SwarmerSuperElite", + SuperEliteSkullomat = "LightSpawnerSuperElite", + SuperEliteThug = "HeavyMeleeSuperElite", + SuperEliteWitch = "LightRangedSuperElite", + SuperEliteWringer = "DisembodiedHandSuperElite", + + SuperEliteDoomstone = "HeavyRangedSplitterSuperElite", + + -- Tartarus Minibosses + Doomstone = "HeavyRangedSplitterMiniboss", + DoomstoneFragment = "HeavyRangedSplitterFragment", + Sneak = "WretchAssassinMiniboss", + + -- Asphodel + Bloodless = "BloodlessNaked", + BoneRaker = "BloodlessNakedBerserker", + WaveMaker = "BloodlessWaveFist", + + BurnFlinger = "BloodlessPitcher", + InfernoBomber = "BloodlessGrenadier", + SlamDancer = "BloodlessSelfDestruct", + + Dracon = "RangedBurrower", + Gorgon = "FreezeShotUnit", + SkullCrusher = "CrusherUnit", + Spreader = "SpreadShotUnit", + + -- Asphodel Armored + ArmoredBloodless = "BloodlessNakedElite", + ArmoredBoneRaker = "BloodlessNakedBerserkerElite", + ArmoredWaveMaker = "BloodlessWaveFistElite", + + ArmoredBurnFlinger = "BloodlessPitcherElite", + ArmoredInfernoBomber = "BloodlessGrenadierElite", + ArmoredSlamDancer = "BloodlessSelfDestructElite", + + ArmoredDracon = "RangedBurrowerElite", + ArmoredGorgon = "FreezeShotUnitElite", + ArmoredSkullomat = "LightSpawnerElite", + ArmoredSpreader = "SpreadShotUnitElite", + + -- Asphodel Super Elite + SuperEliteBloodless = "BloodlessNakedSuperElite", + SuperEliteBurnFlinger = "BloodlessPitcherSuperElite", + SuperEliteDracon = "RangedBurrowerSuperElite", + SuperEliteSkullomat = "LightSpawnerSuperElite", + SuperEliteSpreader = "SpreadShotUnitSuperElite", + + SuperEliteMegaGorgon = "HitAndRunUnitSuperElite", + + -- Asphodel Minibosses + ArmoredSkullCrusher = "CrusherElite", + MegaGorgon = "HitAndRunUnitElite", --HitAndRunUnit exists but is unused + MinibossSpreader = "SpreadShotUnitMiniboss", + BargeVoidstone = "ShieldRangedMiniboss", + + -- Elysium Enemies + Eyeball = "ShadeNaked", + Bowman = "ShadeBowUnit", + Shieldsman = "ShadeShieldUnit", + Spearman = "ShadeSpearUnit", + Swordsman = "ShadeSwordUnit", + + Chariot = "Chariot", + Flamewheel = "ChariotSuicide", + GhostShield = "SupportShields", + SoulCatcher = "FlurrySpawner", + Splitter = "SplitShotUnit", + Voidstone = "ShieldRanged", + + -- Elysium Armored + ArmoredEyeball = "ShadeNakedElite", + ArmoredBowman = "ShadeBowUnitElite", + ArmoredShieldsman = "ShadeShieldUnitElite", + ArmoredSpearman = "ShadeSpearUnitElite", + ArmoredSwordsman = "ShadeSwordUnitElite", + + ArmoredChariot = "ChariotElite", + ArmoredFlamewheel = "ChariotSuicideElite", + ArmoredSplitter = "SplitShotUnitElite", + ArmoredVoidstone = "ShieldRangedElite", + + -- Elysium Super Elite + SuperEliteEyeball = "ShadeNakedSuperElite", + SuperEliteBowman = "ShadeBowUnitSuperElite", + SuperEliteShieldsman = "ShadeShieldUnitSuperElite", + SuperEliteSpearman = "ShadeSpearUnitSuperElite", + SuperEliteSwordsman = "ShadeSwordUnitSuperElite", + + SuperEliteChariot = "ChariotSuperElite", + SuperEliteSoulCatcher = "FlurrySpawnerSuperElite", + SuperEliteVoidstone = "ShieldRangedSuperElite", + + SuperEliteiteButterflyBall = "FlurrySpawnerSuperElite", + + -- Elysium Minibosses + ButterflyBall = "FlurrySpawnerElite", + + -- Styx Enemies + Rat = "RatThug", + TinyRat = "Crawler", + Satyr = "SatyrRanged", + Snakestone = "HeavyRangedForked", + Bother = "ThiefImpulseMineLayer", + + -- Styx Armored + ArmoredRat = "RatThugElite", + ArmoredSatyr = "SatyrRangedElite", + ArmoredSnakestone = "HeavyRangedForkedElite", + + -- Styx Super Elite + SuperEliteSnakestone = "HeavyRangedForkedSuperElite", + + -- Styx Minibosses + GiantVermin = "RatThugElite", -- Anthony + SnakestoneMiniboss = "HeavyRangedForkedMiniboss", + MegaSatyr = "SatyrRangedMiniboss", + TinyVermin = "CrawlerMiniboss", -- Tony + }, + EnemySets = { + -- There are several unused enemy sets, such as survival rooms for the other two biomes- these are not included. + Tartarus = "EnemiesBiome1", + TartarusTrial = "EnemiesBiome1Devotion", + TartarusSurvival = "EnemiesBiome1Survival", + TartarusElite = "EnemiesBiome1Hard", + TartarusThanatos = "EnemiesBiome1Thanatos", + TartarusErebus = "ShrineChallengeTartarus", + + Asphodel = "EnemiesBiome2", + AsphodelTrial = "EnemiesBiome2Devotion", + AsphodelElite = "EnemiesBiome2Hard", + AsphodelBarge = "EnemiesBiome2Wrapping", + AsphodelTrove = "EnemiesBiome2Challenge", + AsphodelThanatos = "EnemiesBiome2Thanatos", + AsphodelErebus = "ShrineChallengeAsphodel", + + HydraHeads = "HydraHeads", + + Elysium = "EnemiesBiome3", + ElysiumTrial = "EnemiesBiome3Devotion", + ElysiumElite = "EnemiesBiome3Hard", + ElysiumErebus = "ShrineChallengeElysium", + + StyxBigRoom = "EnemiesBiome4", + StyxSmallRoom = "EnemiesBiome4Mini", + StyxSmallRoomElite = "EnemiesBiome4MiniHard", + StyxSmallRoomSingle = "EnemiesBiome4MiniSingle", + StyxMinibossAdds = "EnemiesBiome4MiniBossFodder", + + HadesSmallAdds = "EnemiesHadesSmall", + HadesLargeAdds = "EnemiesHadesLarge", + EM4HadesSmallAdds = "EnemiesHadesSmall", + EM4HadesLargeAdds = "EnemiesHadesLarge", + + ErebusSuperElite = "ShrineChallengeSuperElite", + } +} +RCLib.CodeToName = { + ChaosBlessings = {}, + ChaosCurses = {}, + Enemies = {}, + EnemySets = {}, +} + +RCLib.CodeToName.ChaosBlessings = ModUtil.Table.Transpose(RCLib.NameToCode.ChaosBlessings) +RCLib.CodeToName.ChaosCurses = ModUtil.Table.Transpose(RCLib.NameToCode.ChaosCurses) +RCLib.CodeToName.Enemies = ModUtil.Table.Transpose(RCLib.NameToCode.Enemies) +RCLib.CodeToName.EnemySets = ModUtil.Table.Transpose(RCLib.NameToCode.EnemySets) + +function RCLib.EncodeChaosBlessing(name) + return RCLib.NameToCode.ChaosBlessings[name] +end + +function RCLib.DecodeChaosBlessing(name) + return RCLib.CodeToName.ChaosBlessings[name] +end + +function RCLib.EncodeChaosCurse(name) + return RCLib.NameToCode.ChaosCurses[name] +end + +function RCLib.DecodeChaosCurse(name) + return RCLib.CodeToName.ChaosCurses[name] +end + +function RCLib.EncodeEnemy(name) + return RCLib.NameToCode.Enemies[name] +end + +function RCLib.DecodeEnemy(name) + return RCLib.CodeToName.Enemies[name] +end + +function RCLib.EncodeEnemySet(name) + return RCLib.NameToCode.EnemySets[name] +end + +function RCLib.DecodeEnemySet(name) + return RCLib.CodeToName.EnemySets[name] +end diff --git a/RCLib/RCLib.lua b/RCLib/RCLib.lua new file mode 100644 index 0000000..cd10c1e --- /dev/null +++ b/RCLib/RCLib.lua @@ -0,0 +1,80 @@ +--[[ + RCLib/RunControlLib + Author: + SleepSoul (Discord: SleepSoul#6006) + + A set of common utility functions used to modify run events and game content in mods like ChaosControl, EnemyControl, etc. +]] +ModUtil.Mod.Register("RCLib") + +function RCLib.GetEligible(inputTable,lookupTable) -- Read a table of bools, returning a table of the names of all that are true. Optionally use a lookup table to convert the names in inputTable. + local eligible = {} + for name, bool in pairs(inputTable) do + if bool then + if lookupTable ~= nil then + table.insert(eligible, lookupTable[name]) + else + table.insert(eligible, name) + end + end + end + return eligible +end + +function RCLib.RemoveIneligibleBools(inputTable, baseTable, lookupTable) -- Read two tables of bools, returning a table with all the values set to true in baseTable minus all the values set to false in inputTable. Optionally use a lookup table to convert the names in inputTable. + local eligible = {} + local match = false + for name, bool in pairs(baseTable) do + match = false + if bool then + if next(inputTable) == nil then + table.insert(eligible,name) + else + for name2, bool2 in pairs(inputTable) do + if lookupTable ~= nil and lookupTable[name2] == name and not bool2 then + match = true + elseif name2 == name and not bool2 then + match = true + end + end + if match == false then + table.insert(eligible, name) + end + end + end + end + return eligible +end + +function RCLib.RemoveIneligibleStrings(inputTable, baseTable, lookupTable) -- Read a table of bools and a table of strings, returning a table with all the strings in baseTable minus all the values set to false in inputTable. Optionally use a lookup table to convert the names in inputTable. + local eligible = {} + local match = false + for _, name in ipairs(baseTable) do + match = false + if next(inputTable) == nil then + table.insert(eligible,name) + else + for name2, bool in pairs(inputTable) do + if lookupTable ~= nil and lookupTable[name2] == name and not bool then + match = true + elseif name2 == name and not bool then + match = true + end + end + if match == false then + table.insert(eligible, name) + end + end + end + return eligible +end + +function RCLib.PopulateMinLength(targetTable, inputTable, minLength) -- Populates a target table with the contents of an input table, repeatedly inserting until a minimum length is reached. + local i = 0 + while i < minLength do + for _, name in pairs(inputTable) do + table.insert(targetTable, name) + i = i + 1 + end + end +end diff --git a/RCLib/modfile.txt b/RCLib/modfile.txt new file mode 100644 index 0000000..0b0a809 --- /dev/null +++ b/RCLib/modfile.txt @@ -0,0 +1,3 @@ +::Run Control Lib +Import "RCLib.lua" +Import "Map.lua" From a32ab64c6e819212271db5a53dcc34fe37b41c3a Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Thu, 1 Dec 2022 22:55:48 +0000 Subject: [PATCH 74/77] refactor(EnemyControl): Update to use RCLib Add limited RuleOverride function to force enemies as eligible, Remove inbuilt functions/lookup table and replace with references to RCLib, Refactor also adds ability to edit Hades adds and use SuperElite enemies --- EnemyControl/EnemyControl.lua | 202 ++++++++++++++++++---------------- EnemyControl/Map.lua | 108 ------------------ EnemyControl/modfile.txt | 1 - 3 files changed, 108 insertions(+), 203 deletions(-) delete mode 100644 EnemyControl/Map.lua diff --git a/EnemyControl/EnemyControl.lua b/EnemyControl/EnemyControl.lua index e19bbd1..1e0508e 100644 --- a/EnemyControl/EnemyControl.lua +++ b/EnemyControl/EnemyControl.lua @@ -3,22 +3,20 @@ Authors: SleepSoul (Discord: SleepSoul#6006) Museus (Discord: Museus#7777) + Dependencies: ModUtil, RCLib Change the pool of enemies eligible in each room, allowing certain enemy types to be removed. ]] ModUtil.Mod.Register("EnemyControl") local config = { - EnemySetting = "Vanilla" + EnemySetting = "Vanilla" } EnemyControl.config = config --TODO add config option in menu +EnemyControl.EligibleEnemies = {} +EnemyControl.VanillaSets = {} -EnemyControl.Presets = { --Define rulesets +EnemyControl.Presets = { -- Define rulesets Vanilla = {}, - Debug = { - Tartarus = { - Lout = true, - } - }, Hypermodded1 = { StyxSmallRoom = { TinyRat = false, @@ -38,14 +36,79 @@ EnemyControl.Presets = { --Define rulesets Bowman = false, Shieldsman = false, Swordsman = false, + Flamewheel = false, }, StyxSmallRoom = { TinyRat = false, } - } + }, + RatsOClock = { + Tartarus = { + TinyRat = true, + }, + TartarusElite = { + TinyRat = true, + }, + TartarusSurvival = { + TinyRat = true, + }, + Asphodel = { + TinyRat = true, + }, + AsphodelElite = { + TinyRat = true, + }, + Elysium = { + TinyRat = true, + }, + ElysiumElite = { + TinyRat = true, + }, + StyxSmallRoom = { + TinyRat = true, + }, + StyxSmallRoomElite = { + TinyRat = true, + }, + StyxSmallRoomSingle = { + TinyRat = true, + }, + }, + Neuron = { + Tartarus = { + ArmoredSplitter = true, + }, + TartarusElite = { + ArmoredSplitter = true, + }, + TartarusSurvival = { + ArmoredSplitter = true, + }, + Asphodel = { + ArmoredSplitter = true, + }, + AsphodelElite = { + ArmoredSplitter = true, + }, + Elysium = { + ArmoredSplitter = true, + }, + ElysiumElite = { + ArmoredSplitter = true, + }, + StyxSmallRoom = { + ArmoredSplitter = true, + }, + StyxSmallRoomElite = { + ArmoredSplitter = true, + }, + StyxSmallRoomSingle = { + ArmoredSplitter = true, + }, + }, } -EnemyControl.PresetTypes = { +EnemyControl.InheritVanilla = { -- Biomes set to true will inherit the vanilla enemy set and only remove those set to false, biomes set to false will start from 0 and only add those set to true. Hypermodded1 = { StyxSmallRoom = true, }, @@ -57,110 +120,48 @@ EnemyControl.PresetTypes = { } } -function EnemyControl.PopulateMinLength(targetTable, inputTable, minLength) --Populates a target table with the contents of an input table, repeatedly inserting until a minimum length is reached. - local i = 0 - while i < minLength do - for _, name in pairs(inputTable) do - table.insert(targetTable, name) - i = i + 1 - end - end -end - -function EnemyControl.GetEligible(inputTable) - local eligible = {} - for name, bool in pairs(inputTable) do - if ( bool ) then - table.insert(eligible, EnemyControl.NameToCode.Enemies[name]) - DebugPrint({Text = EnemyControl.NameToCode.Enemies[name]}) - end - end - return eligible -end - -function EnemyControl.RemoveIneligibleFromBools(inputTable,baseTable) - local eligible = {} - local check = "" - local match = false - for name, bool in pairs(baseTable) do - match = false - check = name - DebugPrint({Text = check}) - if ( bool ) then - if ( next(inputTable) == nil ) then - table.insert(eligible,check) - else - for name2, bool2 in pairs(inputTable) do - if ( EnemyControl.NameToCode.Enemies[name2] == check and bool2 == false ) then - match = true - end - end - if ( match == false ) then - table.insert(eligible,check) - end - end - end - end - return eligible -end - -function EnemyControl.RemoveIneligibleFromStrings(inputTable,baseTable) - local eligible = {} - local check = "" - local match = false - for _, name in ipairs(baseTable) do - match = false - check = name - if ( next(inputTable) == nil ) then - table.insert(eligible,name) - else - for name2, bool in pairs(inputTable) do - DebugPrint({Text = name2}) - if ( EnemyControl.NameToCode.Enemies[name2] == check and bool == false ) then - match = true - end - end - if ( match == false ) then - table.insert(eligible,name) - end - end - end - return eligible -end - -function EnemyControl.RegisterPreset(name, preset) - EnemyControl.Presets[name] = preset -end +EnemyControl.RuleOverrides = { -- Any overrides to enemy eligibility are made here. Only option currently supported is HardForce, which will make the enemy always eligible to appear. TODO add overrides for minimum and maximum biome depth per biome + Neuron = { + ArmoredSplitter = { + HardForce = true, + }, + }, +} function EnemyControl.ReadPreset() --Read current preset and create table of enemies marked as eligible - EnemyControl.EligibleEnemies = {} local Preset = EnemyControl.Presets[config.EnemySetting] - local PresetType = EnemyControl.PresetTypes[config.EnemySetting] + local InheritVanilla = {} + if EnemyControl.InheritVanilla[config.EnemySetting] ~= nil then + InheritVanilla = EnemyControl.InheritVanilla[config.EnemySetting] + end for biome, _ in pairs(Preset) do EnemyControl.EligibleEnemies[biome] = {} - if ( PresetType[biome] == true ) then - EnemyControl.PopulateMinLength( + if InheritVanilla[biome] == true then + RCLib.PopulateMinLength( EnemyControl.EligibleEnemies[biome], - EnemyControl.RemoveIneligibleFromStrings(Preset[biome],EnemyControl.VanillaSets[EnemyControl.NameToCode.Biomes[biome]]), + RCLib.RemoveIneligibleStrings(Preset[biome],EnemyControl.VanillaSets[RCLib.EncodeEnemySet(biome)],RCLib.NameToCode.Enemies), 1 ) else - EnemyControl.PopulateMinLength( + RCLib.PopulateMinLength( EnemyControl.EligibleEnemies[biome], - EnemyControl.GetEligible(Preset[biome]), + RCLib.GetEligible(Preset[biome],RCLib.NameToCode.Enemies), 1 ) end end end -function EnemyControl.UpdatePools() +function EnemyControl.UpdatePools() -- Inject every non-empty biome of the current preset into the relevant biomes in EnemySets.lua + DebugPrint({Text = "Enemy preset: "..EnemyControl.config.EnemySetting}) + if EnemyControl.InheritVanilla[config.EnemySetting] ~= nil then + InheritVanilla = EnemyControl.InheritVanilla[config.EnemySetting] + end for biome, pool in pairs(EnemyControl.EligibleEnemies) do - EnemyControl.Target = EnemyControl.NameToCode.Biomes[biome] + EnemyControl.Target = RCLib.EncodeEnemySet(biome) EnemyControl.Pool = pool - DebugPrint({Text = EnemyControl.Target}) - DebugPrint({Text = EnemyControl.Pool}) ModUtil.Table.Replace(EnemySets[EnemyControl.Target], EnemyControl.Pool) + DebugPrint({Text = "Updated enemy pool for "..biome}) end end @@ -176,3 +177,16 @@ ModUtil.Path.Wrap("StartNewRun", function ( baseFunc, currentRun ) EnemyControl.UpdatePools() return baseFunc(currentRun) end, EnemyControl) + +ModUtil.Path.Wrap("IsEnemyEligible", function ( baseFunc, enemyName, encounter, wave ) + local Preset = EnemyControl.config.EnemySetting + local EnemyRef = RCLib.DecodeEnemy(enemyName) + local Overrides = {} + if EnemyControl.RuleOverrides[Preset][EnemyRef] ~= nil then + Overrides = EnemyControl.RuleOverrides[Preset][EnemyRef] + end + if Overrides.HardForce then + return true + end + return baseFunc( enemyName, encounter, wave ) +end, EnemyControl) diff --git a/EnemyControl/Map.lua b/EnemyControl/Map.lua deleted file mode 100644 index 1fa629f..0000000 --- a/EnemyControl/Map.lua +++ /dev/null @@ -1,108 +0,0 @@ -EnemyControl.NameToCode = { - Enemies = { - ArmoredNumbskull = "SwarmerElite", - ArmoredThug = "HeavyMeleeElite", - ArmoredWringer = "DisembodiedHandElite", - ArmoredLout = "PunchingBagElite", - ArmoredPest = "ThiefMineLayerElite", - ArmoredBother = "ThiefImpulseMineLayerElite", - ArmoredWitch = "LightRangedElite", - ArmoredSpreader = "SpreadShotUnitElite", - ArmoredSplitter = "SplitShotUnitElite", - ArmoredBrimstone = "HeavyRangedElite", - ArmoredSnakestone = "HeavyRangedForkElite", - ArmoredDoomstone = "HeavyRangedSplitterElite", - ArmoredDoomstoneFragment = "HeavyRangedSplitterFragmentElite", - ArmoredVoidstone = "ShieldRangedElite", - ArmoredDracon = "RangedBurrowerElite", - ArmoredChariot = "ChariotElite", - ArmoredFlamewheel = "ChariotSuicideElite", - ArmoredGhostShield = "SupportShieldsElite", - ArmoredSkullomat = "LightSpawnerElite", - ArmoredSoulCatcher = "FlurrySpawnerElite", - ArmoredBloodless = "BloodlessNakedElite", - ArmoredBoneRaker = "BloodlessNakedBerserkerElite", - ArmoredWaveMaker = "BloodlessWaveFistElite", - ArmoredInfernoBomber = "BloodlessGrenadierElite", - ArmoredSlamDancer = "BloodlessSelfDestructElite", - ArmoredBurnFlinger = "BloodlessPitcherElite", - ArmoredSkullCrusher = "CrusherElite", - ArmoredGorgon = "FreezeShotElite", - ArmoredMegaGorgon = "HitAndRunElite", - ArmoredEyeball = "ShadeNakedElite", - ArmoredArcher = "ShadeBowUnitElite", - ArmoredSpearman = "ShadeSpearUnitElite", - ArmoredShield = "ShadeShieldUnitElite", - ArmoredSwordsman = "ShadeSwordUnitElite", - ArmoredSatyr = "SatyrRangedElite", - ArmoredTinyRat = "CrawlerElite", - ArmoredRat = "RatThugElite", - Numbskull = "Swarmer", - Thug = "HeavyMelee", - Wringer = "DisembodiedHand", - Lout = "PunchingBag", - Pest = "ThiefMineLayer", - Bother = "ThiefImpulseMineLayer", - Witch = "LightRanged", - Spreader = "SpreadShotUnit", - Splitter = "SplitShotUnit", - Brimstone = "HeavyRanged", - Snakestone = "HeavyRangedFork", - Doomstone = "HeavyRangedSplitter", - DoomstoneFragment = "HeavyRangedSplitterFragment", - Voidstone = "ShieldRanged", - Dracon = "RangedBurrower", - Chariot = "Chariot", - Flamewheel = "ChariotSuicide", - GhostShield = "SupportShields", - Skullomat = "LightSpawner", - SoulCatcher = "FlurrySpawner", - Bloodless = "BloodlessNaked", - BoneRaker = "BloodlessNakedBerserker", - WaveMaker = "BloodlessWaveFist", - InfernoBomber = "BloodlessGrenadier", - SlamDancer = "BloodlessSelfDestruct", - BurnFlinger = "BloodlessPitcher", - SkullCrusher = "CrusherUnit", - Gorgon = "FreezeShotUnit", - MegaGorgon = "HitAndRun", - Eyeball = "ShadeNaked", - Bowman = "ShadeBowUnit", - Spearman = "ShadeSpearUnit", - Shieldsman = "ShadeShieldUnit", - Swordsman = "ShadeSwordUnit", - Satyr = "SatyrRanged", - TinyRat = "Crawler", - Rat = "RatThug", - }, - Biomes = { - Tartarus = "EnemiesBiome1", - TartarusTrial = "EnemiesBiome1Devotion", - TartarusSurvival = "EnemiesBiome1Survival", - TartarusElite = "EnemiesBiome1Hard", - TartarusThanatos = "EnemiesBiome1Thanatos", - Asphodel = "EnemiesBiome2", - AsphodelTrial = "EnemiesBiome2Devotion", - AsphodelSurvival = "EnemiesBiome2Survival", -- it's here if you ever wanna use it LMAO - AsphodelElite = "EnemiesBiome2Hard", - AsphodelThanatos = "EnemiesBiome2Thanatos", - AsphodelBarge = "EnemiesBiome2Wrapping", - AsphodelTrove = "EnemiesBiome2Challenge", - Elysium = "EnemiesBiome3", - ElysiumTrial = "EnemiesBiome3Devotion", - ElysiumSurvival = "EnemiesBiome3Survival", - ElysiumElite = "EnemiesBiome3Hard", - StyxBigRoom = "EnemiesBiome4Mini", - StyxSmallRoom = "EnemiesBiome4Mini", - StyxSmallRoomElite = "EnemiesBiome4MiniHard", - StyxMinibossAdds = "EnemiesBiome4MiniBossFodder", - HydraHeads = "HydraHeads" - } -} -EnemyControl.CodeToName = { - Enemies = {}, - Biomes = {} -} - -EnemyControl.CodeToName.Enemies = ModUtil.Table.Transpose(EnemyControl.NameToCode.Enemies) -EnemyControl.CodeToName.Biomes = ModUtil.Table.Transpose(EnemyControl.NameToCode.Biomes) diff --git a/EnemyControl/modfile.txt b/EnemyControl/modfile.txt index 30425bc..8c0b6c6 100644 --- a/EnemyControl/modfile.txt +++ b/EnemyControl/modfile.txt @@ -1,3 +1,2 @@ :: Enemy Control Import "EnemyControl.lua" -Import "Map.lua" From a895283b399a5a700ce69e2c87f372f4c888a2c2 Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Thu, 1 Dec 2022 22:59:24 +0000 Subject: [PATCH 75/77] refactor(ChaosControl): Update to use RCLib Remove inbuilt functions and replace with references to RCLib, Refactor also adds vanilla table inheritance and readable names for traits --- ChaosControl/ChaosControl.lua | 155 ++++++++++++++-------------------- 1 file changed, 63 insertions(+), 92 deletions(-) diff --git a/ChaosControl/ChaosControl.lua b/ChaosControl/ChaosControl.lua index 6b56a62..e87589c 100644 --- a/ChaosControl/ChaosControl.lua +++ b/ChaosControl/ChaosControl.lua @@ -3,6 +3,7 @@ Authors: SleepSoul (Discord: SleepSoul#6006) Museus (Discord: Museus#7777) + Dependencies: ModUtil, RCLib Change the eligible offerings from Chaos, allowing certain curses or blessings to be removed. ]] ModUtil.Mod.Register("ChaosControl") @@ -13,128 +14,98 @@ local config = { ChaosControl.config = config --TODO add config option in menu ChaosControl.EligibleBlessings = {} ChaosControl.EligibleCurses = {} +ChaosControl.VanillaBlessings = {} +ChaosControl.VanillaCurses = {} ChaosControl.Presets = { --Define rulesets - Vanilla = { - Blessings = { - ChaosBlessingMeleeTrait = true, - ChaosBlessingRangedTrait = true, - ChaosBlessingAmmoTrait = true, - ChaosBlessingMaxHealthTrait = true, - ChaosBlessingBoonRarityTrait = true, - ChaosBlessingMoneyTrait = true, - ChaosBlessingMetapointTrait = true, - ChaosBlessingSecondaryTrait = true, - ChaosBlessingDashAttackTrait = true, - ChaosBlessingExtraChanceTrait = true, - ChaosBlessingBackstabTrait = true, - ChaosBlessingAlphaStrikeTrait = true, - }, - Curses = { - ChaosCurseNoMoneyTrait = true, - ChaosCurseAmmoUseDelayTrait = true, - ChaosCursePrimaryAttackTrait = true, - ChaosCurseSecondaryAttackTrait = true, - ChaosCurseCastAttackTrait = true, - ChaosCurseDeathWeaponTrait = true, - ChaosCurseHiddenRoomReward = true, - ChaosCurseDamageTrait = true, - ChaosCurseTrapDamageTrait = true, - ChaosCurseHealthTrait = true, - ChaosCurseMoveSpeedTrait = true, - ChaosCurseSpawnTrait = true, - ChaosCurseDashRangeTrait = true, - } - }, + Vanilla = {}, Hypermodded = { Blessings = { - ChaosBlessingMeleeTrait = true, - ChaosBlessingRangedTrait = true, - ChaosBlessingAmmoTrait = true, - ChaosBlessingMaxHealthTrait = true, - ChaosBlessingBoonRarityTrait = true, - ChaosBlessingMoneyTrait = true, - ChaosBlessingSecondaryTrait = true, - ChaosBlessingDashAttackTrait = true, - ChaosBlessingExtraChanceTrait = true, - ChaosBlessingBackstabTrait = true, - ChaosBlessingAlphaStrikeTrait = true, + Eclipse = false, }, Curses = { - ChaosCurseNoMoneyTrait = true, - ChaosCurseAmmoUseDelayTrait = true, - ChaosCursePrimaryAttackTrait = true, - ChaosCurseSecondaryAttackTrait = true, - ChaosCurseCastAttackTrait = true, - ChaosCurseDeathWeaponTrait = true, - ChaosCurseHiddenRoomReward = true, - ChaosCurseDamageTrait = true, - ChaosCurseTrapDamageTrait = true, - ChaosCurseHealthTrait = true, - ChaosCurseMoveSpeedTrait = true, - ChaosCurseDashRangeTrait = true, - } + Roiling = false, + }, }, Debug = { Blessings = { - ChaosBlessingRangedTrait = true, + Shot = true, }, Curses = { - ChaosCurseTrapDamageTrait = true, - } + Abyssal = true, + }, } } -function ChaosControl.PopulateMinLength(targetTable, inputTable, minLength) --Populates a target table with the contents of an input table, repeatedly inserting until a minimum length is reached. - local i = 0 - while i < minLength do - for _, name in pairs(inputTable) do - table.insert(targetTable, name) - i = i + 1 +ChaosControl.InheritVanilla = { -- Presets set to true will inherit the vanilla traits and only remove those set to false, Presets set to false will start from 0 and only add those set to true. + Hypermodded = { + Blessings = true, + Curses = true, + }, +} + +function ChaosControl.ReadPreset() -- Read current preset and create table of blessings and curses marked to eligible, including failsafe for presets with <3 traits + local Preset = { + Blessings = {}, + Curses = {}, + } + local InheritVanilla = {} + if ChaosControl.Presets[config.ChaosSetting] ~= nil then + Preset = ChaosControl.Presets[config.ChaosSetting] + end + if ChaosControl.InheritVanilla[config.ChaosSetting] ~= nil then + InheritVanilla = ChaosControl.InheritVanilla[config.ChaosSetting] + end + if Preset.Blessings ~= nil then + if InheritVanilla.Blessings then + RCLib.PopulateMinLength( + ChaosControl.EligibleBlessings, + RCLib.RemoveIneligibleStrings(Preset.Blessings,ChaosControl.VanillaBlessings,RCLib.NameToCode.ChaosBlessings), + 3 + ) + else + RCLib.PopulateMinLength( + ChaosControl.EligibleBlessings, + RCLib.GetEligible(Preset.Blessings,RCLib.NameToCode.ChaosBlessings), + 3 + ) end + else + ChaosControl.EligibleBlessings = ChaosControl.VanillaBlessings end -end - -function ChaosControl.GetEligibleTraits(inputTable) - local eligibleTraits = {} - for name, bool in pairs(inputTable) do - if ( bool ) then - table.insert(eligibleTraits, name) + if Preset.Curses ~= nil then + if InheritVanilla.Curses then + RCLib.PopulateMinLength( + ChaosControl.EligibleCurses, + RCLib.RemoveIneligibleStrings(Preset.Curses,ChaosControl.VanillaCurses,RCLib.NameToCode.ChaosCurses), + 3 + ) + else + RCLib.PopulateMinLength( + ChaosControl.EligibleCurses, + RCLib.GetEligible(Preset.Curses,RCLib.NameToCode.ChaosCurses), + 3 + ) end + else + ChaosControl.EligibleCurses = ChaosControl.VanillaCurses end - return eligibleTraits -end - -function ChaosControl.RegisterPreset(name, preset) - ChaosControl.Presets[name] = preset -end - -function ChaosControl.ReadPreset() --Read current preset and create table of blessings and curses marked to eligible, including failsafe for presets with <3 traits, which otherwise cause crash - ChaosControl.EligibleBlessings = {} - ChaosControl.EligibleCurses = {} - ChaosControl.PopulateMinLength( - ChaosControl.EligibleBlessings, - ChaosControl.GetEligibleTraits(ChaosControl.Presets[config.ChaosSetting].Blessings), - 3 - ) - ChaosControl.PopulateMinLength( - ChaosControl.EligibleCurses, - ChaosControl.GetEligibleTraits(ChaosControl.Presets[config.ChaosSetting].Curses), - 3 - ) end function ChaosControl.UpdateOfferings() --Inject eligible blessings/curses into table of Chaos' offerings in LootData + DebugPrint({Text = "Chaos preset: "..ChaosControl.config.ChaosSetting}) ModUtil.Table.MergeKeyed(LootData, { TrialUpgrade = { PermanentTraits = ChaosControl.EligibleBlessings, TemporaryTraits = ChaosControl.EligibleCurses, } }) - DebugPrint({Text = "Chaos Offerings: "..config.ChaosSetting}) + DebugPrint({Text = "Updated Chaos offerings"}) end ModUtil.LoadOnce( function() + ChaosControl.VanillaBlessings = ModUtil.Table.Copy(LootData.TrialUpgrade.PermanentTraits) + ChaosControl.VanillaCurses = ModUtil.Table.Copy(LootData.TrialUpgrade.TemporaryTraits) ChaosControl.ReadPreset() ChaosControl.UpdateOfferings() end) From 341026a7e3a37583a2281d1999727251dd5814eb Mon Sep 17 00:00:00 2001 From: SleepSoul Date: Sun, 11 Dec 2022 21:42:51 +0000 Subject: [PATCH 76/77] fix(EnemyControl): Fix EnemyControl crash when RuleOverrides are not set for current preset Add an extra nil check, as previous one would itself cause a crash if there were no overrides at all for a preset. --- EnemyControl/EnemyControl.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/EnemyControl/EnemyControl.lua b/EnemyControl/EnemyControl.lua index 1e0508e..5de31cc 100644 --- a/EnemyControl/EnemyControl.lua +++ b/EnemyControl/EnemyControl.lua @@ -182,7 +182,7 @@ ModUtil.Path.Wrap("IsEnemyEligible", function ( baseFunc, enemyName, encounter, local Preset = EnemyControl.config.EnemySetting local EnemyRef = RCLib.DecodeEnemy(enemyName) local Overrides = {} - if EnemyControl.RuleOverrides[Preset][EnemyRef] ~= nil then + if EnemyControl.RuleOverrides[Preset] ~= nil and EnemyControl.RuleOverrides[Preset][EnemyRef] ~= nil then Overrides = EnemyControl.RuleOverrides[Preset][EnemyRef] end if Overrides.HardForce then @@ -190,3 +190,4 @@ ModUtil.Path.Wrap("IsEnemyEligible", function ( baseFunc, enemyName, encounter, end return baseFunc( enemyName, encounter, wave ) end, EnemyControl) + From ce2637270e7ffc77e6bfbd3b73397c3c8b27d92d Mon Sep 17 00:00:00 2001 From: Zyruvias Date: Sun, 15 Jan 2023 11:34:01 -0500 Subject: [PATCH 77/77] fix quick restart crashing --- QuickRestart/QuickRestart.lua | 54 +++++++---------------------------- 1 file changed, 11 insertions(+), 43 deletions(-) diff --git a/QuickRestart/QuickRestart.lua b/QuickRestart/QuickRestart.lua index f25efe2..3156832 100644 --- a/QuickRestart/QuickRestart.lua +++ b/QuickRestart/QuickRestart.lua @@ -62,7 +62,7 @@ function QuickRestart.ResetRun(triggerArgs) Kill( CurrentRun.Hero, triggerArgs ) end -OnControlPressed{ "Assist", +OnControlPressed{ "Assist Use Shout Reload", function(triggerArgs) if config.Enabled then if IsControlDown({ Name = "Assist" }) @@ -72,43 +72,8 @@ OnControlPressed{ "Assist", QuickRestart.ResetRun(triggerArgs) end end -end} - -OnControlPressed{ "Use", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end -end} - -OnControlPressed{ "Shout", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end -end} - -OnControlPressed{ "Reload", - function(triggerArgs) - if config.Enabled then - if IsControlDown({ Name = "Assist" }) - and IsControlDown({ Name = "Use" }) - and IsControlDown({ Name = "Shout" }) - and IsControlDown({ Name = "Reload" }) then - QuickRestart.ResetRun(triggerArgs) - end - end -end} + end +} OnAnyLoad{ "RoomPreRun", function ( triggerArgs ) @@ -145,12 +110,15 @@ ModUtil.Path.Context.Wrap("HandleDeath", function () if QuickRestart.KeepStartingKeepsake and GameState.QuickRestartStartingKeepsake then GameState.LastAwardTrait = GameState.QuickRestartStartingKeepsake end - end + end + + -- Set UsedQuickRestart Flag so Keepsake and InputBlock are appropriately set. + local quickDeathApplicable = config.QuickDeathEnabled and not currentRun.Cleared + if quickDeathApplicable then + QuickRestart.UsedQuickRestart = true + end - -- Set UsedQuickRestart Flag so Keepsake and InputBlock are appropriately set. - if quickDeathApplicable then - QuickRestart.UsedQuickRestart = true - end + baseFunc(argTable) end, QuickRestart) end, QuickRestart)