BoC: Added basic search function

This commit is contained in:
Eric authored and wofsauge committed 2024-05-31 21:42:03 +02:00
1 parent 78052f2e5c
commit de1d96a103
2 files changed
+305 -5

No files matched your search

+37 -5
View File
@@ -1,5 +1,7 @@
local game = Game() local game = Game()
require("eid_bagofcrafting_search")
--these aren't local so that they can be saved and reloaded, or cleared in the Mod Config Menu --these aren't local so that they can be saved and reloaded, or cleared in the Mod Config Menu
EID.BoC = {} EID.BoC = {}
EID.BoC.CurrentPickupCount = -1 EID.BoC.CurrentPickupCount = -1
@@ -235,6 +237,9 @@ local recheckPickups = false
local customRNGSeed = 0x77777770 local customRNGSeed = 0x77777770
local customRNGShift = {0,0,0} local customRNGShift = {0,0,0}
local lastSearchValue = "";
local lastSearchInputEnabled = false;
-- Use local RNG functions to possibly reduce processing time a little bit -- Use local RNG functions to possibly reduce processing time a little bit
local function RNGNext() local function RNGNext()
local num = customRNGSeed local num = customRNGSeed
@@ -930,6 +935,15 @@ function EID:handleBagOfCraftingUpdating()
end end
end end
local currentSearchValue = EID:BoCSGetSearchValue()
local currentSearchInputEnabled = EID:BoCSGetSearchInputEnabled()
EID:BoCSHandleInput()
if currentSearchValue ~= lastSearchValue or currentSearchInputEnabled ~= lastSearchInputEnabled then
refreshNextTick = true
end
lastSearchValue = currentSearchValue
lastSearchInputEnabled = currentSearchInputEnabled
-- Check for Hold Tab key inputs -- Check for Hold Tab key inputs
if displayingRecipeList and Input.IsActionPressed(EID.Config["BagOfCraftingToggleKey"], EID.bagPlayer.ControllerIndex) then if displayingRecipeList and Input.IsActionPressed(EID.Config["BagOfCraftingToggleKey"], EID.bagPlayer.ControllerIndex) then
EID.TabDescThisFrame = true EID.TabDescThisFrame = true
@@ -1176,6 +1190,11 @@ function EID:handleBagOfCraftingRendering(ignoreRefreshRate)
prevDesc = "" prevDesc = ""
EID.RefreshBagTextbox = false EID.RefreshBagTextbox = false
local searchLine = EID:BoCSGetSearchLine()
if searchLine ~= nil then
prevDesc = prevDesc .. searchLine
end
prevDesc = prevDesc .. getHotkeyString() prevDesc = prevDesc .. getHotkeyString()
prevDesc = prevDesc .. getFloorItemsString(true, roomItems) prevDesc = prevDesc .. getFloorItemsString(true, roomItems)
if (EID.Config["BagOfCraftingShowControls"]) then if (EID.Config["BagOfCraftingShowControls"]) then
@@ -1191,7 +1210,6 @@ function EID:handleBagOfCraftingRendering(ignoreRefreshRate)
prefix = "#{{Trinket159}} " prefix = "#{{Trinket159}} "
end end
local filteredRecipesList = {} local filteredRecipesList = {}
local filteredNumResults = 0 local filteredNumResults = 0
-- If we aren't Tainted Cain, we should filter out recipes that don't use everything in our bag -- If we aren't Tainted Cain, we should filter out recipes that don't use everything in our bag
@@ -1199,15 +1217,28 @@ function EID:handleBagOfCraftingRendering(ignoreRefreshRate)
for _,id in ipairs(sortedIDs) do for _,id in ipairs(sortedIDs) do
filteredRecipesList[id] = {} filteredRecipesList[id] = {}
for _, v in ipairs(currentRecipesList[id]) do for _, v in ipairs(currentRecipesList[id]) do
if (EID:bagContainsCount(v[1]) == #bagItems) then local itemName = EID:getObjectName(5, 100, v[2]);
local searchValid = not EID:BoCSGetSearchEnabled() or EID:BoCSCheckItemName(itemName)
if (searchValid and EID:bagContainsCount(v[1]) == #bagItems) then
table.insert(filteredRecipesList[id], v) table.insert(filteredRecipesList[id], v)
filteredNumResults = filteredNumResults + 1 filteredNumResults = filteredNumResults + 1
end end
end end
end end
else else
filteredRecipesList = currentRecipesList for _,id in ipairs(sortedIDs) do
filteredNumResults = numResults filteredRecipesList[id] = {}
for _, v in ipairs(currentRecipesList[id]) do
local itemName = EID:getObjectName(5, 100, v[2]);
local searchValid = not EID:BoCSGetSearchEnabled() or EID:BoCSCheckItemName(itemName)
if (searchValid) then
table.insert(filteredRecipesList[id], v)
filteredNumResults = filteredNumResults + 1
end
end
end
end end
-- Keeping the offset doesn't work at all with non-Tainted-Cain bag-filtered results; -- Keeping the offset doesn't work at all with non-Tainted-Cain bag-filtered results;
@@ -1229,6 +1260,7 @@ function EID:handleBagOfCraftingRendering(ignoreRefreshRate)
if (curOffset > bagOfCraftingOffset+EID.Config["BagOfCraftingResults"]) then break end if (curOffset > bagOfCraftingOffset+EID.Config["BagOfCraftingResults"]) then break end
if not v then break end if not v then break end
if (curOffset > bagOfCraftingOffset) then if (curOffset > bagOfCraftingOffset) then
local itemName = EID:getObjectName(5, 100, v[2]);
if not EID.Config["BagOfCraftingDisplayNames"] then if not EID.Config["BagOfCraftingDisplayNames"] then
prevDesc = prevDesc .."#{{Collectible"..v[2].."}} " prevDesc = prevDesc .."#{{Collectible"..v[2].."}} "
--color the equals sign with the item quality, so the order of the list can make sense --color the equals sign with the item quality, so the order of the list can make sense
@@ -1238,7 +1270,7 @@ function EID:handleBagOfCraftingRendering(ignoreRefreshRate)
if prevItem ~= v[2] then 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? --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?
prevDesc = prevDesc .."#{{Collectible"..v[2].."}} ".. qualities[CraftingItemQualities[v[2]]] .. prevDesc = prevDesc .."#{{Collectible"..v[2].."}} ".. qualities[CraftingItemQualities[v[2]]] ..
string.sub(EID:getObjectName(5, 100, v[2]),1,18).."#" string.sub(itemName,1,18).."#"
else else
prevDesc = prevDesc .."#" prevDesc = prevDesc .."#"
end end
+268
View File
@@ -0,0 +1,268 @@
local indexCharMapping = " ??????'????,-./0123456789?;?????abcdefghijklmnopqrstuvwxyz"
local searchValue = ""
local searchInputEnabled = false
local lastBackspaceTrigger = 0
local backspaceStep = 0
--- Returns the current search query
-- @return string
function EID:BoCSGetSearchValue()
return searchValue
end
--- Enables or disables the search input respectively
-- @param newState boolean
function EID:BoCSSetSearchInputEnabled(newState)
searchInputEnabled = newState
if newState then
EID:AddCallback(ModCallbacks.MC_INPUT_ACTION, EID.BoCSBlockInputAction)
else
EID:RemoveCallback(ModCallbacks.MC_INPUT_ACTION, EID.BoCSBlockInputAction)
end
end
--- Returns if the search input is enabled
-- @returns boolean
function EID:BoCSGetSearchInputEnabled()
return searchInputEnabled
end
--- Gets if the filtering is currently enabled
function EID:BoCSGetSearchEnabled()
return true
end
--- Returns true if the item name is matched
-- @returns boolean
function EID:BoCSCheckItemName(itemName)
if searchValue == "" then
return true
end
return string.find(string.lower(itemName), string.lower(searchValue))
end
--- Handles any input done related to Bag of Crafting search
function EID:BoCSHandleInput()
if Game():IsPaused() then
return
end
if EID.bagPlayer == nil then
return
end
if Input.IsButtonTriggered(Keyboard.KEY_ENTER, EID.bagPlayer.ControllerIndex, true) then
EID:BoCSSetSearchInputEnabled(not searchInputEnabled)
return
end
if searchInputEnabled then
local newValue = searchValue
local index = 0
local hasLetterInput = false
-- Keep in mind that this only works because the respective
-- enum values are integers
for i=Keyboard.KEY_SPACE,Keyboard.KEY_Z do
index = index + 1
if Input.IsButtonTriggered(i, EID.bagPlayer.ControllerIndex, true) then
local toAppend = string.upper(indexCharMapping:sub(index, index))
newValue = newValue .. toAppend
hasLetterInput = true
-- We only handle one input at a time
break
end
if hasLetterInput then
return
end
end
if Input.IsButtonTriggered(Keyboard.KEY_BACKSPACE, EID.bagPlayer.ControllerIndex, true) then
lastBackspaceTrigger = 0
backspaceStep = 0
return
end
local currentFrame = Game():GetFrameCount()
-- 30 Frames = 1 second
-- 15 = 500ms
if Input.IsButtonPressed(Keyboard.KEY_BACKSPACE, EID.bagPlayer.ControllerIndex, true) and (
backspaceStep == 0
or
(backspaceStep == 1 and currentFrame - lastBackspaceTrigger > 15) -- we delay the second deletion a little bit more
or
(backspaceStep >= 2 and currentFrame - lastBackspaceTrigger > 3)
) then
local isControlPressed = (
Input.IsButtonPressed(Keyboard.KEY_LEFT_CONTROL, EID.bagPlayer.ControllerIndex, true)
or
Input.IsButtonPressed(Keyboard.KEY_RIGHT_CONTROL, EID.bagPlayer.ControllerIndex, true)
)
if isControlPressed then
newValue = ""
else
newValue = searchValue:sub(1, -2)
end
lastBackspaceTrigger = currentFrame
backspaceStep = backspaceStep + 1
end
searchValue = newValue
end
end
--- Returns the line that should be displayed inside of the menu
function EID:BoCSGetSearchLine()
if not searchInputEnabled and (searchValue == nil or searchValue == "") then
return nil
end
local result = ""
if searchInputEnabled then
result = "{{ColorGreen}}"
end
result = result .. "Search: " .. searchValue .. "#"
return result
end
---
-- This is a workaround to block every single Input call that could potentially be
-- used from another mod.
-- We override the Input functions with our own stubs to prevent other mods from
-- doing actions while the user is performing a search.
-- This does NOT block the main game from performing actions like toggling fullscreen
-- or toggling the pause!--
-- Since this could be useful for multiple reasons, this should probably be put
-- inside the main.lua instead of leaving this here
-- Hooking multiple times WILL impact the performance!--
-- I've also added a third parameter called "force"
-- This way, we are still able to force the real function to be called instead
-- of our modified code--
-- This code should not impact the performance in a huge way
-- since the first thing we do is check if the searchInput is enabled anyway
-- We can allow losing a bit of performance when searching
function EID:BoCSHookInput()
local oldInputIsButtonTriggered = Input.IsButtonTriggered
local oldInputIsButtonPressed = Input.IsButtonPressed
local oldInputIsActionTriggered = Input.IsActionTriggered
local oldInputIsActionPressed = Input.IsActionPressed
Input.IsButtonTriggered = function(key, controller, force)
if force or not searchInputEnabled then
return oldInputIsButtonTriggered(key, controller)
end
local bagPlayer = nil
if EID.isRepentance then
local hasBag, player = EID:PlayersHaveCollectible(710)
if hasBag then
bagPlayer = player
end
end
if bagPlayer == nil or controller ~= bagPlayer.ControllerIndex then
return oldInputIsButtonTriggered(key, controller)
end
return false
end
Input.IsButtonPressed = function(key, controller, force)
if force or not searchInputEnabled then
return oldInputIsButtonPressed(key, controller)
end
local bagPlayer = nil
if EID.isRepentance then
local hasBag, player = EID:PlayersHaveCollectible(710)
if hasBag then
bagPlayer = player
end
end
if bagPlayer == nil or controller ~= bagPlayer.ControllerIndex then
return oldInputIsButtonPressed(key, controller)
end
return false
end
Input.IsActionTriggered = function(key, controller, force)
if force or not searchInputEnabled then
return oldInputIsActionTriggered(key, controller)
end
local bagPlayer = nil
if EID.isRepentance then
local hasBag, player = EID:PlayersHaveCollectible(710)
if hasBag then
bagPlayer = player
end
end
if bagPlayer == nil or controller ~= bagPlayer.ControllerIndex then
return oldInputIsActionTriggered(key, controller)
end
return false
end
Input.IsActionPressed = function(key, controller, force)
if force or not searchInputEnabled then
return oldInputIsActionPressed(key, controller)
end
local bagPlayer = nil
if EID.isRepentance then
local hasBag, player = EID:PlayersHaveCollectible(710)
if hasBag then
bagPlayer = player
end
end
if bagPlayer == nil or controller ~= bagPlayer.ControllerIndex then
return oldInputIsActionPressed(key, controller)
end
return false
end
Isaac.DebugString("Hooked Input")
end
--- Blocks all input actions triggered by the games bindings itself
function EID:BoCSBlockInputAction(entity, inputHook, buttonAction)
local bagPlayer = nil
if EID.isRepentance then
local hasBag, player = EID:PlayersHaveCollectible(710)
if hasBag then
bagPlayer = player
end
end
if bagPlayer == nil then
return nil
end
if inputHook == InputHook.GET_ACTION_VALUE then
return 0
end
if inputHook == InputHook.IS_ACTION_PRESSED or inputHook == InputHook.IS_ACTION_TRIGGERED then
return false
end
return nil
end
EID:BoCSHookInput()