Merge pull request #167 from Buurazu/master

big Bag of Crafting update
This commit is contained in:
wofsauge authored and GitHub committed 2021-10-19 16:23:03 +02:00
commit adfb6d2d3b
4 files changed
+227 -47

No files matched your search

+3 -3
View File
@@ -22,7 +22,7 @@ local repCollectibles={
[24] = {"24", "Dessert", "↑ +1 Health up#Heals one heart"}, -- Dessert
[25] = {"25", "Breakfast", "↑ +1 Health up#Heals one heart"}, -- Breakfast
[26] = {"26", "Rotten Meat", "↑ +1 Health up#Heals one heart"}, -- Rotten Meat
[29] = {"29", "Moms Underwear", "↑ +1.5 Range up#↑ +0.5 Tear Height#Spawns 3-6 Blue Flies"}, -- Moms Underwear
[29] = {"29", "Mom's Underwear", "↑ +1.5 Range up#↑ +0.5 Tear Height#Spawns 3-6 Blue Flies"}, -- Mom's Underwear
[30] = {"30", "Mom's Heels", "↑ +1.5 Range up#↑ +0.5 Tear Height#Touched enemies take 12 damage"}, -- Mom's Heels
[31] = {"31", "Mom's Lipstick", "↑ +2.25 Range up#↑ +0.5 Tear Height#Spawns 1 random heart"}, -- Mom's Lipstick
[40] = {"40", "Kamikaze!", "Causes a big explosion at the player's location#Deals 185 damage"}, -- Kamikaze!
@@ -75,7 +75,7 @@ local repCollectibles={
[214] = {"214", "Anemic", "↑ +1.5 Range up#Leave creep on floor when getting hit"}, -- Anemic
[218] = {"218", "Placenta", "↑ +1 Health up#Heals 1 red heart#Regenerate health slowly"}, -- Placenta
[224] = {"224", "Cricket's Body", "↓ -20% Range down#Tears split up in 4 on hit#Splitted tears deal half damage#↑ -1 tear delay that can go below capped value"}, -- Cricket's Body
[228] = {"228", "Mom's Perfume", "Chance to shoot fearing tears#↑ -1 tear delay that can go below capped value"}, -- Moms Perfume
[228] = {"228", "Mom's Perfume", "Chance to shoot fearing tears#↑ -1 tear delay that can go below capped value"}, -- Mom's Perfume
[230] = {"230", "Abaddon", "↑ +1.5 Damage up#↑ +0.2 Speed up#Fear tears#Converts all red heart container into Black hearts#+2 Black hearts"}, -- Abaddon
[232] = {"232", "Stop Watch", "Permanent slow effect for enemies#↑ +0.3 Speed up"}, -- Stop Watch
[240] = {"240", "Experimental Treatment", "↑ Gain 4 random Stat Ups and ↓2 Stat Downs"}, -- Experimental Treatment
@@ -672,6 +672,6 @@ EID.descriptions["en_us"].horsepills={
EID.descriptions["en_us"].CraftingBagContent = "Bag:"
EID.descriptions["en_us"].CraftingRoomContent = "Room:"
EID.descriptions["en_us"].CraftingFloorContent = "Floor:"
EID.descriptions["en_us"].CraftingResults = "Results:#(Scroll: hold {{CONFIG_BoC_Toggle}} + {{ButtonY}} {{ButtonA}})"
EID.descriptions["en_us"].CraftingResults = "(Scroll: hold {{CONFIG_BoC_Toggle}} + {{ButtonY}} {{ButtonA}},#Lock: {{ButtonX}}, Refresh: {{ButtonB}})"
EID.descriptions["en_us"].CraftingBagModError = "!!! {{ColorRed}}ERROR:#{{ColorRed}}The \"Crafting result calculation\" feature currently does not work when item mods are installed!#{{ColorRed}}Please uninstall the item mods or deactivate the Bag of Crafting descriptions in the config"
+144 -40
View File
@@ -506,6 +506,11 @@ EID.bagOfCraftingFloorQuery = {}
EID.BagItems = {}
EID.icount = 0
EID.lockedResults = nil
EID.refreshNextTick = false
EID.downHeld = 0
EID.upHeld = 0
local function GetMaxCollectibleID()
local id = CollectibleType.NUM_COLLECTIBLES-1
local step = 16
@@ -564,11 +569,12 @@ function EID:handleBagOfCraftingRendering()
if detectModdedItems() or Game():GetRoom():GetFrameCount ()<2 then
return false
end
local curRoomIndex = Game():GetLevel():GetCurrentRoomIndex()
local curRoomIndex = Game():GetLevel():GetCurrentRoomDesc().SafeGridIndex
local results = {}
local roomItems = {}
local pickups = Isaac.FindByType(5, -1, -1, true, false)
if EID.bagOfCraftingCurPickupCount ~= #pickups then
for i, entity in ipairs(pickups) do
local craftingIDs = EID:getBagOfCraftingID(entity.Variant, entity.SubType)
@@ -586,34 +592,93 @@ function EID:handleBagOfCraftingRendering()
end
local itemQuery = {}
local itemCount = {}
--max 8 copies of a single item in our list, to avoid repeat recipes
for i, v in ipairs(EID.bagOfCraftingFloorQuery) do
table.insert(itemQuery, v)
if (not itemCount[v] or itemCount[v] < 8) then
table.insert(itemQuery, v)
if (not itemCount[v]) then itemCount[v] = 1
else itemCount[v] = itemCount[v] + 1 end
end
end
for i, v in ipairs(EID.BagItems) do
table.insert(itemQuery, v)
if (not itemCount[v] or itemCount[v] < 8) then
table.insert(itemQuery, v)
if (not itemCount[v]) then itemCount[v] = 1
else itemCount[v] = itemCount[v] + 1 end
end
end
-- Calculate result from pickups on floor
if #itemQuery < 8 then
return false
end
table.sort(itemQuery, function(a, b) return a > b end)
local queryString = table.concat(itemQuery,",")
if randResultCache[queryString] == nil then
local randResults = {}
for i = 0, 250 do
local newTable = {}
local tableCopy = {table.unpack(itemQuery)}
for k = 1, 8 do
local pos = math.random(1, #tableCopy)
table.insert(newTable, tableCopy[pos])
table.remove(tableCopy, pos)
end
table.sort(newTable, function(a, b) return a > b end)
randResults[table.concat(newTable,",")] = newTable
local function qualitySort(a, b)
if (pickupValues[a+1] == pickupValues[b+1]) then
return a > b
else
return pickupValues[a+1] > pickupValues[b+1]
end
end
--sort by ingredient quality
table.sort(itemQuery, qualitySort)
local queryString = table.concat(itemQuery,",")
if EID.lockedResults ~= nil then
results = randResultCache[EID.lockedResults]
elseif randResultCache[queryString] == nil or EID.refreshNextTick then
local randResults = {}
local skipRandom = false
--check every single possible recipe for our highest value pickups
--limit it in the options, since the number of total combinations quickly grows (nCr):
--12 = 495, 13 = 1287, 14 = 3003, 15 = 6435, 16 = 12870
local mostValuable = {}
--we have less items than our threshold; every single recipe will be listed
if (#itemQuery <= EID.Config["BagOfCraftingCombinationMax"]) then
mostValuable = itemQuery
skipRandom = true
else
for i=1,EID.Config["BagOfCraftingCombinationMax"] do
mostValuable[i] = itemQuery[i]
end
end
--this combination algorithm was adopted from this Java code: https://stackoverflow.com/a/16256122
--note that it will run into duplicates, for example if you have eight pennies and a key, it can't tell the difference between
--PPPPPPPK (pennies 1-7) and PPPPPPPK (pennies 2-8) and PPPPPPPK (pennies 1-4,6-8) etc..., I don't know of a way to prevent that
local function combinations(arr, length, startPos, tempResult)
local length = length or 8
local startPos = startPos or 1
local tempResult = tempResult or {}
if (length == 0) then
randResults[table.concat(tempResult,",")] = {table.unpack(tempResult)}
return
end
for i = startPos, #arr-length+1 do
tempResult[8-length+1] = arr[i]
combinations(arr,length-1,i+1,tempResult)
end
end
combinations(mostValuable)
--do random pulls for some more recipe choices
if (not skipRandom) then
for i = 0, EID.Config["BagOfCraftingRandomResults"] do
local newTable = {}
local tableCopy = {table.unpack(itemQuery)}
for k = 1, 8 do
local pos = math.random(1, #tableCopy)
table.insert(newTable, tableCopy[pos])
table.remove(tableCopy, pos)
end
table.sort(newTable, qualitySort)
randResults[table.concat(newTable,",")] = newTable
end
end
local calcResults = {}
for k, v in pairs(randResults) do
local resultID = EID:calculateBagOfCrafting(v)
@@ -623,7 +688,18 @@ function EID:handleBagOfCraftingRendering()
end
randResultCache[queryString] = calcResults
results = calcResults
EID.bagOfCraftingOffset = 0
--sort our final results by quality, then alphabetical item name
table.sort(results, function(a, b)
if (EID.itemWeightsLookup[a[2]] == EID.itemWeightsLookup[b[2]]) then
return (EID:getObjectName(5, 100, a[2]) < EID:getObjectName(5, 100, b[2]))
else
return (EID.itemWeightsLookup[a[2]] > EID.itemWeightsLookup[b[2]])
end
end)
--don't reset our location on refresh, so you can more easily refresh for lower quality recipes
if (not EID.refreshNextTick) then EID.bagOfCraftingOffset = 0 end
EID.refreshNextTick = false
else
results = randResultCache[queryString]
end
@@ -667,38 +743,66 @@ function EID:handleBagOfCraftingRendering()
EID.player:SetShootingCooldown(2)
if Input.IsActionTriggered(ButtonAction.ACTION_SHOOTDOWN, EID.player.ControllerIndex) then
EID.bagOfCraftingOffset = math.min(#results-(#results%EID.Config["BagOfCraftingResults"]), EID.bagOfCraftingOffset + EID.Config["BagOfCraftingResults"])
EID.downHeld = Isaac.GetTime()
elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTUP, EID.player.ControllerIndex) then
EID.bagOfCraftingOffset = math.max(0, EID.bagOfCraftingOffset - EID.Config["BagOfCraftingResults"])
EID.upHeld = Isaac.GetTime()
--lock the current results so you can actually do a recipe that you've scrolled down to without losing it
elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTLEFT, EID.player.ControllerIndex) then
if (EID.lockedResults == nil) then EID.lockedResults = queryString
else EID.lockedResults = nil end
--refresh the recipes
elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTRIGHT, EID.player.ControllerIndex) then
if (EID.lockedResults == nil) then EID.refreshNextTick = true end
end
--scroll pages quickly if the button is held
if Input.IsActionPressed(ButtonAction.ACTION_SHOOTDOWN, EID.player.ControllerIndex) and Isaac.GetTime() - EID.downHeld > 750 then
EID.bagOfCraftingOffset = math.min(#results-(#results%EID.Config["BagOfCraftingResults"]), EID.bagOfCraftingOffset + EID.Config["BagOfCraftingResults"])
EID.downHeld = Isaac.GetTime() - 700
elseif Input.IsActionPressed(ButtonAction.ACTION_SHOOTUP, EID.player.ControllerIndex) and Isaac.GetTime() - EID.upHeld > 750 then
EID.bagOfCraftingOffset = math.max(0, EID.bagOfCraftingOffset - EID.Config["BagOfCraftingResults"])
EID.upHeld = Isaac.GetTime() - 700
end
end
--fix bug with being allowed to go to an empty page if recipe count = multiple of page size (or if we refresh on last page)
if (EID.bagOfCraftingOffset >= #results) then EID.bagOfCraftingOffset = EID.bagOfCraftingOffset - EID.Config["BagOfCraftingResults"] end
local resultCount = 0
local skips = 0
for quality = 4, 0, -1 do -- sort by result quality
for i, v in ipairs(results) do
if EID.itemWeightsLookup[v[2]]== quality then
if skips < EID.bagOfCraftingOffset then
skips = skips + 1
if skips == EID.bagOfCraftingOffset then
customDescObj.Description = customDescObj.Description.."#{{Blank}} ...+"..skips.." more"
end
else
customDescObj.Description = customDescObj.Description.."# {{Collectible"..v[2].."}} ="
customDescObj.Description = customDescObj.Description..EID:tableToCraftingIconsMerged(v[1])
resultCount = resultCount + 1
if resultCount > EID.Config["BagOfCraftingResults"]-1 then
if #results > EID.Config["BagOfCraftingResults"] then
customDescObj.Description = customDescObj.Description.."#{{Blank}} ...+"..(#results-EID.Config["BagOfCraftingResults"]- skips).." more"
end
break
end
end
local prevItem = 0
local qualities = { [0] = "{{ColorSilver}}", "{{ColorLime}}", "{{ColorTransform}}", "{{ColorObjName}}", "{{ColorGold}}" }
local prefix = "#{{Blank}} "
if (EID.lockedResults) then
prefix = "#{{Trinket159}} "
end
--results is now sorted by quality and item name, and doesn't iterate over the entire table, instead starting at the page offset
if (EID.bagOfCraftingOffset > 0) then
customDescObj.Description = customDescObj.Description.. prefix .. "...+"..EID.bagOfCraftingOffset.." more"
end
for i=EID.bagOfCraftingOffset+1,EID.bagOfCraftingOffset+EID.Config["BagOfCraftingResults"] do
local v = results[i]
if (not v) then break end
if (not EID.Config["BagOfCraftingDisplayNames"]) then
customDescObj.Description = customDescObj.Description.."# {{Collectible"..v[2].."}} " .. " ="
--only display the item name if it's the first occurrence
else
if (prevItem ~= v[2]) then
--substring the first 18 characters of the item name so it fits on one line; is there a way to get around desc line length limits?
customDescObj.Description = customDescObj.Description.."# {{Collectible"..v[2].."}} ".. qualities[EID.itemWeightsLookup[v[2]]] ..
string.sub(EID:getObjectName(5, 100, v[2]),1,18).."#"
else
customDescObj.Description = customDescObj.Description.."#"
end
end
if resultCount > EID.Config["BagOfCraftingResults"]-1 then
break
end
customDescObj.Description = customDescObj.Description..EID:tableToCraftingIconsMerged(v[1])
prevItem = v[2]
end
if (EID.bagOfCraftingOffset + EID.Config["BagOfCraftingResults"] < #results) then
customDescObj.Description = customDescObj.Description.. prefix .. "...+"..(#results-EID.Config["BagOfCraftingResults"]-EID.bagOfCraftingOffset).." more"
end
EID:printDescription(customDescObj)
return true
end
+15 -1
View File
@@ -170,10 +170,21 @@ EID.UserConfig = {
-- Higher numbers can cause more lag!
-- Default = 7
["BagOfCraftingResults"] = 7,
-- Changes the number of ingredients for which every combination is checked
-- Higher numbers will cause lag spikes on new pickup sets!
-- Default = 12
["BagOfCraftingCombinationMax"] = 12,
-- Changes the number of random recipes calculated
-- Higher numbers will cause lag spikes on new pickup sets!
-- Default = 500
["BagOfCraftingRandomResults"] = 500,
-- Set the keybinding to toggle the scroll feature of the bag of crafting descriptions
-- look into the AB+ or Repentance documentation for the key names here: https://wofsauge.github.io/IsaacDocs/rep/enums/ButtonAction.html
-- Default = ButtonAction.ACTION_MAP
["BagOfCraftingToggleKey"] = ButtonAction.ACTION_MAP,
-- Display craftable item names, moving the recipe to a new line
-- Default = false
["BagOfCraftingDisplayNames"] = false,
---------Mouse Controls-----------
@@ -270,7 +281,10 @@ EID.DefaultConfig = {
["DisplayDiceInfo"] = true,
["DisplayBagOfCrafting"] = "always",
["BagOfCraftingResults"] = 7,
["BagOfCraftingCombinationMax"] = 12,
["BagOfCraftingRandomResults"] = 500,
["BagOfCraftingToggleKey"] = ButtonAction.ACTION_MAP,
["BagOfCraftingDisplayNames"] = false,
["SpindownDiceResults"] = 3,
["EnableMouseControls"] = false,
["ShowCursor"] = false,
@@ -280,4 +294,4 @@ EID.DefaultConfig = {
["EnableEntityDescriptions"] = true,
["ShowQuality"] = true,
["ShowObjectID"] = false,
}
}
+65 -3
View File
@@ -648,7 +648,7 @@ if MCMLoaded then
)
-- Spindown Dice results
local diceSteps = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
local diceSteps = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
MCM.AddSetting(
"EID",
"Display",
@@ -965,9 +965,71 @@ if MCMLoaded then
AnIndexOf(diceSteps, EID.Config["BagOfCraftingResults"]) - 1 .. " " .. EID.Config["BagOfCraftingResults"]
end,
OnChange = function(currentNum)
EID.Config["BagOfCraftingResults"] = diceSteps[currentNum%#diceSteps + 1]
EID.Config["BagOfCraftingResults"] = diceSteps[currentNum + 1]
end,
Info = {"Preview of items currently craftable with Bag of crafting"}
Info = {"Page size for the preview of items currently craftable with Bag of Crafting"}
}
)
-- Bag of Crafting thorough recipe checks
local combSteps = {8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18}
MCM.AddSetting(
"EID",
"Crafting",
{
Type = ModConfigMenu.OptionType.SCROLL,
CurrentSetting = function()
return AnIndexOf(combSteps, EID.Config["BagOfCraftingCombinationMax"]) - 1
end,
Display = function()
return "Thorough Calculations: $scroll" ..
AnIndexOf(combSteps, EID.Config["BagOfCraftingCombinationMax"]) - 1 .. " " .. EID.Config["BagOfCraftingCombinationMax"]
end,
OnChange = function(currentNum)
EID.Config["BagOfCraftingCombinationMax"] = combSteps[currentNum + 1]
end,
Info = {"Get every recipe for the X best components; setting this high will cause lag spikes (12 = 500 combinations, 14 = 3,000, 16 = 13,000)"}
}
)
-- Bag of Crafting random recipe checks
local calcSteps = {200, 300, 400, 500, 600, 700, 800, 900, 1000, 1100, 1200}
MCM.AddSetting(
"EID",
"Crafting",
{
Type = ModConfigMenu.OptionType.SCROLL,
CurrentSetting = function()
return AnIndexOf(calcSteps, EID.Config["BagOfCraftingRandomResults"]) - 1
end,
Display = function()
return "Random Calculations: $scroll" ..
AnIndexOf(calcSteps, EID.Config["BagOfCraftingRandomResults"]) - 1 .. " " .. EID.Config["BagOfCraftingRandomResults"]
end,
OnChange = function(currentNum)
EID.Config["BagOfCraftingRandomResults"] = calcSteps[currentNum + 1]
end,
Info = {"An additional X number of randomly chosen recipes will be checked, changing each pickup spawn/despawn or refresh"}
}
)
-- Bag of Crafting item names
MCM.AddSetting(
"EID",
"Crafting",
{
Type = ModConfigMenu.OptionType.BOOLEAN,
CurrentSetting = function()
return EID.Config["BagOfCraftingDisplayNames"]
end,
Display = function()
local onOff = "False"
if EID.Config["BagOfCraftingDisplayNames"] then
onOff = "True"
end
return "Show Item Names: " .. onOff
end,
OnChange = function(currentBool)
EID.Config["BagOfCraftingDisplayNames"] = currentBool
end,
Info = {"If on, each result takes two lines; lower your displayed results accordingly"}
}
)