diff --git a/descriptions/rep/en_us.lua b/descriptions/rep/en_us.lua index bc5acf9d..ce12a559 100644 --- a/descriptions/rep/en_us.lua +++ b/descriptions/rep/en_us.lua @@ -1299,6 +1299,7 @@ EID.descriptions[languageCode].spindownError = "Item disappears" EID.descriptions[languageCode].BlankCardEffect = "Blank Card effect:" EID.descriptions[languageCode].FlipItemToggleInfo = "(Hold {{CONFIG_BoC_Toggle}} to show description)" +EID.descriptions[languageCode].GlitchedCrownToggleInfo = "(Press {{CONFIG_BoC_Toggle}} to show next description: {1})" EID.descriptions[languageCode].GlowingHourglassTransformed = "Will change back to Glowing Hourglass next floor" diff --git a/features/eid_api.lua b/features/eid_api.lua index 2cd0af82..7ab8a58a 100644 --- a/features/eid_api.lua +++ b/features/eid_api.lua @@ -2319,6 +2319,26 @@ function EID:UpdateAllPlayerLemegetonWisps() end end +-- This table holds, for each pedestal in the room, a table of item IDs that have been on that pedestal, and timestamps of when they were first and last seen +-- The Glitched Crown callback when describing the pedestal will sort by first timestamp, and delete entries with too old of a last timestamp (like after a reroll) +EID.GlitchedCrownCheck = {} +-- Watch pedestals for being a Glitched Crown style pedestal that flips between items too quickly to display descriptions for +function EID:WatchForGlitchedCrown() + if not EID.collectiblesOwned[689] then return end + local curRoomIndex = game:GetLevel():GetCurrentRoomDesc().ListIndex + EID.GlitchedCrownCheck[curRoomIndex] = EID.GlitchedCrownCheck[curRoomIndex] or {} + + for _, entity in ipairs(Isaac.FindByType(5, 100, -1, true, false)) do + -- Use InitSeed and Index to prevent any Diplopia weirdness + EID.GlitchedCrownCheck[curRoomIndex][entity.InitSeed..entity.Index] = EID.GlitchedCrownCheck[curRoomIndex][entity.InitSeed..entity.Index] or {} + -- Initialize the data about this pedestal showing its current item ID, if necessary + -- in order to sort the items displayed, while also trashing items that haven't shown up in a while, keep both "initial frame seen" and "last frame seen" + EID.GlitchedCrownCheck[curRoomIndex][entity.InitSeed..entity.Index][entity.SubType] = EID.GlitchedCrownCheck[curRoomIndex][entity.InitSeed..entity.Index][entity.SubType] or {EID.GameUpdateCount, EID.GameUpdateCount} + -- update the last frame seen for the pedestal's current collectible ID + EID.GlitchedCrownCheck[curRoomIndex][entity.InitSeed..entity.Index][entity.SubType][2] = EID.GameUpdateCount + end +end + -- Replaces Variable placeholders in string with a given value -- Example: "My {1} message" --> "My test message" function EID:ReplaceVariableStr(str, varID, newString) diff --git a/features/eid_modifiers.lua b/features/eid_modifiers.lua index d230bb04..a55cd9da 100644 --- a/features/eid_modifiers.lua +++ b/features/eid_modifiers.lua @@ -5,6 +5,11 @@ EID.TabPreviewID = 0 EID.inModifierPreview = false -- The "Item Reminder" needs to know if it shouldn't display because we're in a Hold Tab desc EID.TabDescThisFrame = false +-- Some modifiers (e.g. Glitched Crown) want to know if Tab was pressed/released, rather than held +EID.TabHeldThisFrame = false +EID.TabHeldLastFrame = false +function EID:TabPressed() return EID.TabHeldThisFrame and not EID.TabHeldLastFrame end +function EID:TabReleased() return EID.TabHeldLastFrame and not EID.TabHeldThisFrame end -- List of collectible IDs for us to check if a player owns them; feel free to add to this in mods that add description modifiers! EID.collectiblesToCheck[CollectibleType.COLLECTIBLE_VOID] = true @@ -22,6 +27,7 @@ if EID.isRepentance then EID.collectiblesToCheck[CollectibleType.COLLECTIBLE_FALSE_PHD] = true EID.collectiblesToCheck[CollectibleType.COLLECTIBLE_FLIP] = true EID.collectiblesToCheck[CollectibleType.COLLECTIBLE_GLOWING_HOUR_GLASS] = true + EID.collectiblesToCheck[CollectibleType.COLLECTIBLE_GLITCHED_CROWN] = true end EID.collectiblesOwned = {} EID.collectiblesAbsorbed = {} @@ -413,7 +419,8 @@ if EID.isRepentance then -- Handle Spindown Dice description addition local function SpindownDiceCallback(descObj) - if EID.InsideItemReminder then return descObj end + -- don't display in item reminder, or if we've already printed it earlier in the desc + if EID.InsideItemReminder or string.match(descObj.Description, "#{{Collectible723}} :") then return descObj end -- get the ID of the player that owns the Spindown Dice local playerID = (EID.collectiblesOwned[723] or EID.collectiblesAbsorbed[723]) EID:appendToDescription(descObj, "#{{Collectible723}} :") @@ -726,6 +733,64 @@ if EID.isRepentance then return descObj end + + -- Handle Glitched Crown style pedestals + local currentSelection = {} -- keep track of which description we're looking at for a given pedestal + local goingToSpindown = false + local function GlitchedCrownCallback(descObj) + if not descObj.Entity then return descObj end + local entity = descObj.Entity + local curRoomIndex = game:GetLevel():GetCurrentRoomDesc().ListIndex + + if EID.GlitchedCrownCheck[curRoomIndex] and EID.GlitchedCrownCheck[curRoomIndex][descObj.Entity.InitSeed..descObj.Entity.Index] then + -- this table has collectible ID keys that define the first frame and most recent frame that that ID has been seen on this pedestal + -- we need to filter out items that haven't been seen in a while (due to a reroll perhaps), then sort by first frame + local pedestalID = descObj.Entity.InitSeed..descObj.Entity.Index + local items = EID.GlitchedCrownCheck[curRoomIndex][pedestalID] + local sortedItems = {} + for id,frames in pairs(items) do + if EID.GameUpdateCount - frames[2] > 120 then + items[id] = nil + else + table.insert(sortedItems, {id, frames[1]}) + end + end + if #sortedItems < 5 then return descObj end + table.sort(sortedItems, function(a, b) return a[2] < b[2] end) + + currentSelection[pedestalID] = currentSelection[pedestalID] or 0 + + -- watch for Tab being pressed to advance our selection by 1 + -- when spindown dice is involved, watch for tab being released instead of pressed, it makes more sense + if goingToSpindown and EID:TabReleased() or not goingToSpindown and EID:TabPressed() then + currentSelection[pedestalID] = currentSelection[pedestalID] + 1 + if currentSelection[pedestalID] > #sortedItems then currentSelection[pedestalID] = 0 end + end + + -- display the overview description + if currentSelection[pedestalID] == 0 then + descObj = EID:getDescriptionObj(5, 100, 689, nil, false) + descObj.Description = "" + for _,item in ipairs(sortedItems) do + descObj.Description = descObj.Description .. "#{{NameC" .. item[1] .. "}}" + end + -- display a specific description + -- don't replace the desc if the pedestal's already showing the correct item + else + descObj = EID:getDescriptionObj(5, 100, sortedItems[currentSelection[pedestalID]][1]) + end + + local nextIcon = "{{Collectible689}}" + if currentSelection[pedestalID]+1 <= #sortedItems then nextIcon = "{{Collectible" .. sortedItems[currentSelection[pedestalID]+1][1] .. "}}" end + EID:appendToDescription(descObj, "#{{Blank}} ".. EID:ReplaceVariableStr(EID:getDescriptionEntry("GlitchedCrownToggleInfo"),1,nextIcon)) + + -- manually apply Flip; changing the description's item stops future callbacks to avoid infinite loops, but we want Flip still, it works fine + descObj.Entity = entity + if EID.collectiblesOwned[711] and descObj.ObjSubType ~= entity.SubType then descObj = FlipCallback(descObj) end + end + + return descObj + end -------------------------------- -- Although individual conditions/callbacks work well for mods to be able to add through the API, @@ -747,14 +812,18 @@ if EID.isRepentance then -- Using magic numbers here in case it's slightly faster, and because the callback names give context -- Check Birthright first because it overwrites the description instead of appending to it if descObj.ObjSubType == 619 then table.insert(callbacks, BirthrightCallback) end - -- Glowing Hourglass overwrites the description when used three times + if EID.collectiblesOwned[689] then table.insert(callbacks, GlitchedCrownCallback) end + -- Glowing Hourglass overwrites the description when used three times if REPENTOGON and descObj.ObjSubType == 422 then table.insert(callbacks, GlowingHourglassCallback) end if descObj.ObjSubType == 644 then table.insert(callbacks, ConsolationPrizeCallback) end if EID.collectiblesOwned[584] or descObj.ObjSubType == 584 then table.insert(callbacks, BookOfVirtuesCallback) end if EID.collectiblesOwned[711] and EID:getEntityData(descObj.Entity, "EID_FlipItemID") then table.insert(callbacks, FlipCallback) end - if EID.Config["SpindownDiceResults"] > 0 and (EID.collectiblesOwned[723] or EID.collectiblesAbsorbed[723]) and descObj.ObjSubType ~= 668 then table.insert(callbacks, SpindownDiceCallback) end + if EID.Config["SpindownDiceResults"] > 0 and (EID.collectiblesOwned[723] or EID.collectiblesAbsorbed[723]) and descObj.ObjSubType ~= 668 then + goingToSpindown = true + table.insert(callbacks, SpindownDiceCallback) + else goingToSpindown = false end -- Card / Rune Callbacks elseif descObj.ObjVariant == PickupVariant.PICKUP_TAROTCARD then @@ -820,7 +889,7 @@ EID:addDescriptionModifier("EID Afterbirth+", EIDConditionsAB, nil) -- should this be done differently so that mods can add tab previews? (tab conditions is done last, but would be done before callbacks mods add, maybe tab should be checked in EID:getDescriptionObj local function TabConditions(_) - if EID:PlayersActionPressed(EID.Config["BagOfCraftingToggleKey"]) and not EID.inModifierPreview then return true end + if EID.TabHeldThisFrame and not EID.inModifierPreview then return true end EID.TabPreviewID = 0 return false end diff --git a/main.lua b/main.lua index 5ea37697..efdb174c 100644 --- a/main.lua +++ b/main.lua @@ -281,9 +281,7 @@ if EID.isRepentance then if curFrame == lastGetItemResult[2] then if initialItemNext then lastGetItemResult[1] = selectedCollectible elseif flipItemNext and lastGetItemResult[1] then - if EID.flipItemPositions[curRoomIndex] == nil then - EID.flipItemPositions[curRoomIndex] = {} - end + EID.flipItemPositions[curRoomIndex] = EID.flipItemPositions[curRoomIndex] or {} EID.flipItemPositions[curRoomIndex][lastGetItemResult[4]] = {selectedCollectible, lastGetItemResult[3]} end end @@ -1059,12 +1057,17 @@ function EID:onGameUpdate() EID:checkPlayersForMissingItems() EID:evaluateQueuedItems() EID:evaluateHeldPill() + + EID.TabHeldLastFrame = EID.TabHeldThisFrame + EID.TabHeldThisFrame = EID:PlayersActionPressed(EID.Config["BagOfCraftingToggleKey"]) if collSpawned then collSpawned = false local curPositions = {} for _, entity in ipairs(Isaac.FindByType(5, 100, -1, true, false)) do + -- Flag pedestals as being describable on frame 0 (for Tainted Isaac / Glitched Crown type pedestals to not be non-existent for a frame) + entity:GetData()["EID_DescribeOnFirstFrame"] = true -- Fix Overlapping Pedestals if a collectible spawned this frame (needed for Mega Chest) local pos = entity.Position for _, otherPos in ipairs(curPositions) do @@ -1087,6 +1090,7 @@ function EID:onGameUpdate() end if EID.isRepentance then + EID:WatchForGlitchedCrown() EID:UpdateWildCardEffects() if EID.GameUpdateCount % 10 == 0 then -- Check wisp for adding reminder when using lemegeton @@ -1400,7 +1404,7 @@ function EID:OnRender() for _, entitySearch in ipairs(searchGroups) do for _, entity in ipairs(entitySearch) do - if EID:hasDescription(entity) and entity.FrameCount > 0 and not EID.entitiesToPrint[GetPtrHash(entity)] then + if EID:hasDescription(entity) and (entity.FrameCount > 0 or entity:GetData()["EID_DescribeOnFirstFrame"]) and not EID.entitiesToPrint[GetPtrHash(entity)] then table.insert(inRangeEntities, entity) local diff = entity.Position:__sub(sourcePos) -- break ties with the render offset (for Mega Chest double collectibles)