diff --git a/eid_api.lua b/eid_api.lua
index b9a43fb4..8397a190 100644
--- a/eid_api.lua
+++ b/eid_api.lua
@@ -62,7 +62,10 @@ local dynamicSpriteCache = {} -- used to store sprite objects of collectible ico
function EID:addCollectible(id, description, itemName, language)
itemName = itemName or nil
language = language or "en_us"
- EID.descriptions[language].custom["5.100." .. id] = {id, itemName, description, EID._currentMod}
+ modName = EID._currentMod
+ -- Glitched Items exception so they don't have a mod name
+ if id > 4294960000 then modName = nil end
+ EID.descriptions[language].custom["5.100." .. id] = {id, itemName, description, modName}
end
-- Adds a description for a trinket. Optional parameters: itemName, language
@@ -708,7 +711,7 @@ function EID:renderIcon(spriteObj, posX, posY, callback, animName, animFrame)
end
if EID.CachingDescription then
- table.insert(EID.CachedIcons, {spriteObj, posX, posY, callback, animName, animFrame})
+ table.insert(EID.CachedIcons[#EID.CachedIcons], {spriteObj, posX, posY, callback, animName, animFrame})
end
spriteObj:Render(Vector(posX, posY), nullVector, nullVector)
@@ -901,7 +904,7 @@ function EID:renderString(str, position, scale, kcolor)
EID:renderInlineIcons(spriteTable, position.X + offsetX, position.Y)
EID.font:DrawStringScaledUTF8(strFiltered, position.X + offsetX, position.Y, scale.X, scale.Y, textPart[2], 0, false)
if EID.CachingDescription then
- table.insert(EID.CachedStrings, {strFiltered, position.X + offsetX, position.Y, textPart[2], textPart[4], EID.Config["Transparency"]})
+ table.insert(EID.CachedStrings[#EID.CachedStrings], {strFiltered, position.X + offsetX, position.Y, textPart[2], textPart[4], EID.Config["Transparency"]})
end
offsetX = offsetX + EID:getStrWidth(strFiltered) * scale.X
end
@@ -959,7 +962,7 @@ function EID:PlayersHaveCollectible(collectibleID)
for i = 0, game:GetNumPlayers() - 1 do
local player = Isaac.GetPlayer(i)
if player:HasCollectible(collectibleID) then
- return true
+ return true, player
end
end
return false
@@ -1407,3 +1410,20 @@ function EID:removeIgnoredEntity(entityType, entityVariant, entitySubType)
EID.IgnoredEntities[entityType.."."..entityVariant.."."..entitySubType] = nil
end
end
+
+-- Returns if this is a frame we should refresh our descriptions
+function EID:RefreshThisFrame()
+ if EID.GameRenderCount % (60 / EID.Config["RefreshRate"]) == 0 then
+ return true
+ end
+ return false
+end
+
+-- Returns true if any player is pressing the given button (you can also specify any of the input functions)
+function EID:PlayersActionPressed(button, inputFunc)
+ inputFunc = inputFunc or Input.IsActionPressed
+ for k,_ in pairs(EID.controllerIndexes) do
+ if inputFunc(button, k) then return true end
+ end
+ return false
+end
diff --git a/eid_bagofcrafting.lua b/eid_bagofcrafting.lua
index 94499797..6deee238 100644
--- a/eid_bagofcrafting.lua
+++ b/eid_bagofcrafting.lua
@@ -84,7 +84,7 @@ local pickupIDLookup = {
local function IsTaintedCain()
-- this check is necessary for tracking Bag usage since Tainted Cain's pocket bag works differently than everyone else's
- return EID.player:GetPlayerType() == 23
+ return EID.bagPlayer:GetPlayerType() == 23
end
local componentShifts = {
@@ -597,15 +597,15 @@ end)
local holdCounter = 0
local function trackBagHolding()
if not IsTaintedCain() then return end
- local isCardHold = Input.IsActionPressed(ButtonAction.ACTION_PILLCARD, EID.player.ControllerIndex)
- local animationName = EID.player:GetSprite():GetAnimation()
+ local isCardHold = Input.IsActionPressed(ButtonAction.ACTION_PILLCARD, EID.bagPlayer.ControllerIndex)
+ local animationName = EID.bagPlayer:GetSprite():GetAnimation()
if isCardHold and string.match(animationName, "PickupWalk") and #EID.BagItems>=8 then
holdCounter = holdCounter + 1
if holdCounter < 30 then
- icount = EID.player:GetCollectibleCount()
+ icount = EID.bagPlayer:GetCollectibleCount()
end
else
- if isCardHold and holdCounter >= 30 and (string.match(animationName, "Walk") and not string.match(animationName, "Pickup") or (EID.player:GetCollectibleCount() ~= icount)) then
+ if isCardHold and holdCounter >= 30 and (string.match(animationName, "Walk") and not string.match(animationName, "Pickup") or (EID.bagPlayer:GetCollectibleCount() ~= icount)) then
EID.BagItems = {}
else
holdCounter = 0
@@ -629,7 +629,7 @@ local function shiftBagContent()
end
-- only Tainted Cain's consumable slot bag can have its ingredients shifted
local function detectBagContentShift()
- if Input.IsActionTriggered(ButtonAction.ACTION_DROP, EID.player.ControllerIndex) and IsTaintedCain() then
+ if Input.IsActionTriggered(ButtonAction.ACTION_DROP, EID.bagPlayer.ControllerIndex) and IsTaintedCain() then
shiftBagContent()
end
end
@@ -712,7 +712,7 @@ local function getHotkeyString()
local hideDesc = EID:getDescriptionEntry("CraftingHideKey")
local previewDesc = EID:getDescriptionEntry("CraftingPreviewKey")
- local controllerEnabled = EID.player.ControllerIndex > 0
+ local controllerEnabled = EID.bagPlayer.ControllerIndex > 0
local hideKey = HotkeyToString[EID.Config["CraftingHideKey"]]
local hideButton = controllerEnabled and ControllerToString[EID.Config["CraftingHideButton"]]
local previewKey = HotkeyToString[EID.Config["CraftingResultKey"]]
@@ -858,7 +858,8 @@ local function RecipeCrunchCoroutine()
EID.RefreshBagTextbox = true
end
-function EID:handleBagOfCraftingRendering()
+-- Called 60 times a second so we can read input properly
+function EID:handleBagOfCraftingUpdating()
-- reset our calculated recipes when the game seed changes
local curSeed = game:GetSeeds():GetStartSeed()
if (curSeed ~= lastSeedUsed) then
@@ -875,36 +876,34 @@ function EID:handleBagOfCraftingRendering()
detectBagContentShift()
if EID.GameRenderCount % 2 == 0 then checkForPickups() end
- -- load the function we need for Show Recipes as Groups / 8 Icons
- local tableToCraftingIconsFunc = EID.tableToCraftingIconsMerged
- if EID.Config["BagOfCraftingDisplayIcons"] then tableToCraftingIconsFunc = EID.tableToCraftingIconsFull end
-
-- Check for Hide/Preview hotkeys; prevent them from triggering while in MCM
if not ModConfigMenu or not ModConfigMenu.IsVisible then
- if Input.IsButtonTriggered(EID.Config["CraftingHideKey"], 0) or Input.IsButtonTriggered(EID.Config["CraftingHideButton"], EID.player.ControllerIndex) then
+ if Input.IsButtonTriggered(EID.Config["CraftingHideKey"], 0) or Input.IsButtonTriggered(EID.Config["CraftingHideButton"], EID.bagPlayer.ControllerIndex) then
craftingIsHidden = not craftingIsHidden
end
- if Input.IsButtonTriggered(EID.Config["CraftingResultKey"], 0) or Input.IsButtonTriggered(EID.Config["CraftingResultButton"], EID.player.ControllerIndex) then
+ if Input.IsButtonTriggered(EID.Config["CraftingResultKey"], 0) or Input.IsButtonTriggered(EID.Config["CraftingResultButton"], EID.bagPlayer.ControllerIndex) then
showCraftingResult = not showCraftingResult
end
end
-
+end
+
+-- Called when needed based on EID.Config["RefreshRate"]
+function EID:handleBagOfCraftingRendering()
-- Determine if we should display anything at all
if ((EID.isHidden or craftingIsHidden) and EID.MCMCompat_isDisplayingEIDTab ~= "Crafting") or game.Challenge == Challenge.CHALLENGE_CANTRIPPED then
- return
+ return false
+ elseif not EID:RefreshThisFrame() then
+ return false
elseif EID.Config["BagOfCraftingHideInBattle"] then
if Isaac.CountBosses() > 0 or Isaac.CountEnemies() > 0 then
- return
+ return false
end
- end
- if EID.Config["DisplayBagOfCrafting"] == "never" then
+ elseif EID.Config["DisplayBagOfCrafting"] == "never" then
return false
- end
- if EID.Config["DisplayBagOfCrafting"] == "hold" and not string.find(EID.player:GetSprite():GetAnimation(), "PickupWalk") then
+ elseif EID.Config["DisplayBagOfCrafting"] == "hold" and not string.find(EID.bagPlayer:GetSprite():GetAnimation(), "PickupWalk") then
return false
- end
- if game:GetRoom():GetFrameCount() < 2 then
+ elseif game:GetRoom():GetFrameCount() < 2 then
return false
end
@@ -922,7 +921,7 @@ function EID:handleBagOfCraftingRendering()
EID:appendToDescription(descriptionObj,"#" .. backupDesc .. "#{{Collectible" .. backupResult .. "}} " ..
backupObjName .. "#{{Blank}} " .. tabText)
end
- EID:printDescription(descriptionObj)
+ EID:addDescriptionToPrint(descriptionObj)
return true
end
-- if we're in Preview Only mode, then we have nothing more to do
@@ -981,16 +980,20 @@ function EID:handleBagOfCraftingRendering()
local customDescObj = EID:getDescriptionObj(5, 100, 710)
customDescObj.Description = ""
+ -- load the function we need for Show Recipes as Groups / 8 Icons
+ local tableToCraftingIconsFunc = EID.tableToCraftingIconsMerged
+ if EID.Config["BagOfCraftingDisplayIcons"] then tableToCraftingIconsFunc = EID.tableToCraftingIconsFull end
+
-- Pickups Only / No Recipes Mode display
if EID.Config["BagOfCraftingDisplayMode"] == "Pickups Only" then
EID:appendToDescription(customDescObj, getHotkeyString())
EID:appendToDescription(customDescObj, getFloorItemsString(false, roomItems))
- EID:printDescription(customDescObj)
+ EID:addDescriptionToPrint(customDescObj)
return true
elseif EID.Config["BagOfCraftingDisplayMode"] == "No Recipes" then
if not EID.RefreshBagTextbox and prevDesc ~= "" and not EID.OptionChanged then
EID:appendToDescription(customDescObj, prevDesc)
- EID:printDescription(customDescObj)
+ EID:addDescriptionToPrint(customDescObj)
return true
end
@@ -1013,7 +1016,7 @@ function EID:handleBagOfCraftingRendering()
if (bestQuality > bagQuality) then prevDesc = prevDesc .. bestQualityDesc .. " " .. bestQuality .. "#{{Blank}} " .. tableToCraftingIconsFunc(self,mostValuableBag, true) .. "#" .. bestResult .. "#" end
EID:appendToDescription(customDescObj, prevDesc)
- EID:printDescription(customDescObj)
+ EID:addDescriptionToPrint(customDescObj)
return true
end
@@ -1064,35 +1067,35 @@ function EID:handleBagOfCraftingRendering()
end
local prevOffset = bagOfCraftingOffset
- if Input.IsActionPressed(EID.Config["BagOfCraftingToggleKey"], EID.player.ControllerIndex) then
- EID.player.ControlsCooldown = 2
- if Input.IsActionTriggered(ButtonAction.ACTION_SHOOTDOWN, EID.player.ControllerIndex) then
+ if Input.IsActionPressed(EID.Config["BagOfCraftingToggleKey"], EID.bagPlayer.ControllerIndex) then
+ EID.bagPlayer.ControlsCooldown = 2
+ if Input.IsActionTriggered(ButtonAction.ACTION_SHOOTDOWN, EID.bagPlayer.ControllerIndex) then
bagOfCraftingOffset = math.min(numResults-(numResults%EID.Config["BagOfCraftingResults"]), bagOfCraftingOffset + EID.Config["BagOfCraftingResults"])
downHeld = Isaac.GetTime()
- elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTUP, EID.player.ControllerIndex) then
+ elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTUP, EID.bagPlayer.ControllerIndex) then
bagOfCraftingOffset = math.max(0, bagOfCraftingOffset - EID.Config["BagOfCraftingResults"])
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
+ elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTLEFT, EID.bagPlayer.ControllerIndex) then
EID.RefreshBagTextbox = true
if (lockedResults == nil) then lockedResults = queryString
else lockedResults = nil end
--refresh the recipes
- elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTRIGHT, EID.player.ControllerIndex) then
+ elseif Input.IsActionTriggered(ButtonAction.ACTION_SHOOTRIGHT, EID.bagPlayer.ControllerIndex) then
if (lockedResults == nil) then
refreshNextTick = true
end
end
--scroll pages quickly if the button is held
- if Input.IsActionPressed(ButtonAction.ACTION_SHOOTDOWN, EID.player.ControllerIndex) and Isaac.GetTime() - downHeld > 750 then
+ if Input.IsActionPressed(ButtonAction.ACTION_SHOOTDOWN, EID.bagPlayer.ControllerIndex) and Isaac.GetTime() - downHeld > 750 then
bagOfCraftingOffset = math.min(numResults-(numResults%EID.Config["BagOfCraftingResults"]), bagOfCraftingOffset + EID.Config["BagOfCraftingResults"])
downHeld = Isaac.GetTime() - 700
- elseif Input.IsActionPressed(ButtonAction.ACTION_SHOOTUP, EID.player.ControllerIndex) and Isaac.GetTime() - upHeld > 750 then
+ elseif Input.IsActionPressed(ButtonAction.ACTION_SHOOTUP, EID.bagPlayer.ControllerIndex) and Isaac.GetTime() - upHeld > 750 then
bagOfCraftingOffset = math.max(0, bagOfCraftingOffset - EID.Config["BagOfCraftingResults"])
upHeld = Isaac.GetTime() - 700
end
--reset bag contents when holding Use Pill/Card
- if Input.IsActionPressed(ButtonAction.ACTION_PILLCARD, EID.player.ControllerIndex) then
+ if Input.IsActionPressed(ButtonAction.ACTION_PILLCARD, EID.bagPlayer.ControllerIndex) then
resetBagCounter = resetBagCounter + 1
if resetBagCounter > 120 then
EID.BagItems = {}
@@ -1108,7 +1111,7 @@ function EID:handleBagOfCraftingRendering()
if not EID.RefreshBagTextbox and prevDesc ~= "" and bagOfCraftingOffset == prevOffset and not EID.OptionChanged then
EID:appendToDescription(customDescObj, prevDesc)
- EID:printDescription(customDescObj)
+ EID:addDescriptionToPrint(customDescObj)
return true
end
@@ -1173,6 +1176,6 @@ function EID:handleBagOfCraftingRendering()
end
EID:appendToDescription(customDescObj, prevDesc)
- EID:printDescription(customDescObj)
+ EID:addDescriptionToPrint(customDescObj)
return true
end
diff --git a/eid_config.lua b/eid_config.lua
index 3254e510..8e2fdbf1 100644
--- a/eid_config.lua
+++ b/eid_config.lua
@@ -76,6 +76,11 @@ EID.UserConfig = {
-- Hide the descriptions when in battle
-- Default = false
["HideInBattle"] = false,
+ -- How many times a second that EID checks for what objects are close to the player
+ -- Lowering this number can help if you experience frame drops while descriptions are on-screen
+ -- Please use a factor of 60! (1, 2, 3, 4, 5, 6, 10, 12, 15, 20, 30, 60)
+ -- Default: 30
+ ["RefreshRate"] = 30,
-- Toggle obstruction based hiding, when the player has flight
-- Default = true
["DisableObstructionOnFlight"] = true,
@@ -83,6 +88,19 @@ EID.UserConfig = {
-- Current warnings are: Achievements are locked, outdated game version, and potentially having inaccurate modded Bag of Crafting recipes
-- Default = false
["DisableStartOfRunWarnings"] = false,
+
+ ---------- Co-op / Multiple Descriptions -----------
+
+ -- Allow P2/P3/P4 to display descriptions when in Co-op mode
+ -- Default = true
+ ["CoopDescriptions"] = true,
+ -- Allow the paired player of certain characters in Repentance (Esau, Tainted Forgotten's bone pile) to display descriptions
+ -- Default = false
+ ["PairedPlayerDescriptions"] = false,
+ -- Display a description for every object within range of you, instead of just the closest
+ -- Default = false
+ ["DisplayAllNearby"] = false,
+ -- Any descriptions displayed beyond the first one will use the "Local" display mode
---------- Display Modes -----------
@@ -209,8 +227,8 @@ EID.UserConfig = {
-- Note: The --luadebug launch option is required for more detailed glitched item descriptions
-- This option allows mods to have access to your files and should be turned on at your own risk!
-- Without --luadebug, you still can see the effect the item will have on your Hearts, and what stats it might modify
- -- Default = true
- ["DisplayGlitchedItemInfo"] = true,
+ -- Default = false
+ ["DisplayGlitchedItemInfo"] = false,
---------- Sacrifice Room ----------
@@ -304,7 +322,7 @@ EID.UserConfig = {
["CraftingResultButton"] = -1,
- ---------- Mouse Controls ------------
+ ---------- Save Game Options ------------
-- Savegame which should be used for savegame specific features (for example: Item Collection page progress)
-- Possible values: 0,1,2,3
@@ -398,8 +416,12 @@ EID.DefaultConfig = {
["HideButton"] = -1,
["InitiallyHidden"] = false,
["HideInBattle"] = false,
+ ["RefreshRate"] = 30,
["DisableObstructionOnFlight"] = true,
["DisableStartOfRunWarnings"] = false,
+ ["CoopDescriptions"] = true,
+ ["PairedPlayerDescriptions"] = false,
+ ["DisplayAllNearby"] = false,
["DisableOnCurse"] = true,
["DisableOnAltPath"] = true,
["DisableOnAprilFoolsChallenge"] = true,
@@ -427,7 +449,7 @@ EID.DefaultConfig = {
["DisplayPillInfoOptions?"] = true,
["DisplayObstructedPillInfo"] = false,
["ShowUnidentifiedPillDescriptions"] = false,
- ["DisplayGlitchedItemInfo"] = true,
+ ["DisplayGlitchedItemInfo"] = false,
["DisplaySacrificeInfo"] = true,
["DisplayDiceInfo"] = true,
["DisplayBagOfCrafting"] = "always",
diff --git a/eid_modifiers.lua b/eid_modifiers.lua
index 1c1659ae..d7159b08 100644
--- a/eid_modifiers.lua
+++ b/eid_modifiers.lua
@@ -65,6 +65,8 @@ local function TabCallback(descObj)
EID.inModifierPreview = true
local descEntry = EID:getDescriptionObj(5, 100, EID.TabPreviewID)
EID.inModifierPreview = false
+ descEntry.Entity = descObj.Entity
+ EID.TabPreviewID = 0
return descEntry
end
@@ -498,9 +500,10 @@ local function EIDConditionsAB(descObj)
end
EID:addDescriptionModifier("EID Afterbirth+", EIDConditionsAB, nil)
--- should this be done differently so that mods can add tab previews?
+-- 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(descObj)
- if EID.player and Input.IsActionPressed(ButtonAction.ACTION_MAP, EID.player.ControllerIndex) and not EID.inModifierPreview then return true end
+ if EID:PlayersActionPressed(ButtonAction.ACTION_MAP) and not EID.inModifierPreview then return true end
+ EID.TabPreviewID = 0
return false
end
diff --git a/eid_tmtrainer.lua b/eid_tmtrainer.lua
index 54155d65..38924879 100644
--- a/eid_tmtrainer.lua
+++ b/eid_tmtrainer.lua
@@ -75,7 +75,10 @@ local function parseEffectLine(raw)
if (i ~= #words) then name = name .. " " end
end
- replacements[1] = "{{Collectible" .. Isaac.GetItemIdByName(name) .. "}} " .. name
+ replacements[1] = name
+ if (Isaac.GetItemIdByName(name) > 0) then
+ replacements[1] = "{{Collectible" .. Isaac.GetItemIdByName(name) .. "}} " .. name
+ end
elseif effectType == "convert_entities" then
replacements[1] = "{{ColorGray}}" .. entityToName(words[6], true) .. "{{CR}}"
replacements[2] = "{{ColorGray}}" .. entityToName(words[8])
diff --git a/main.lua b/main.lua
index ff87edb4..48353c44 100644
--- a/main.lua
+++ b/main.lua
@@ -12,7 +12,12 @@ EID.Config.Version = "3.2" -- note: changing this will reset everyone's settings
EID.ModVersion = "4.21"
EID.DefaultConfig.Version = EID.Config.Version
EID.isHidden = false
-EID.player = nil
+EID.player = nil -- The primary Player Entity of Player 1
+EID.players = {} -- Both Player Entities of Player 1 if applicable (includes Esau, T.Forgotten)
+EID.coopMainPlayers = {} -- The primary Player Entity for each controller being used
+EID.coopAllPlayers = {} -- Every Player Entity (includes Esau, T.Forgotten)
+EID.controllerIndexes = {} -- A table to map each controller index to their player number for coloring indicators
+EID.isMultiplayer = false -- Used to color P1's highlight/outline indicators (single player just uses white)
-- general variables
EID.PositionModifiers = {}
@@ -33,6 +38,7 @@ EID.CollectedItems = {}
EID.IgnoredEntities = {}
local pathsChecked = {}
local altPathItemChecked = {}
+local alwaysUseLocalMode = false -- set to true after drawing a non-local mode description this frame
EID.GameUpdateCount = 0
EID.GameRenderCount = 0
@@ -434,20 +440,87 @@ end
---------------------------------------------------------------------------
---------------------------Printing Functions------------------------------
-local previousDesc = ""
-local previousRenderPos = Vector.Zero
+EID.previousDescs = {}
EID.CachingDescription = false
EID.CachedIcons = {}
EID.CachedStrings = {}
+EID.CachedIndicators = {}
+EID.CachedRenderPoses = {}
+EID.descriptionsToPrint = {}
+EID.entitiesToPrint = {}
-function EID:printDescription(desc)
+
+function EID:addDescriptionToPrint(desc)
+ if desc.Entity and EID.entitiesToPrint[GetPtrHash(desc.Entity)] then return end
+ table.insert(EID.descriptionsToPrint, desc)
+ if desc.Entity then EID.entitiesToPrint[GetPtrHash(desc.Entity)] = true end
+end
+
+function EID:printDescriptions(useCached)
+ EID.CachingDescription = false
+ if not useCached then
+ -- Test if we should print our cached descriptions or not
+ -- Did an option change, or it's a different number of descriptions?
+ if #EID.descriptionsToPrint ~= #EID.previousDescs or EID.OptionChanged or EID.MCM_OptionChanged then
+ EID:printNewDescriptions()
+ return
+ end
+ -- Do the description entities match, and the description texts match?
+ for i,newDesc in ipairs(EID.descriptionsToPrint) do
+ local oldDesc = EID.previousDescs[i]
+ if newDesc.Description ~= oldDesc.Description or (newDesc.Entity and oldDesc.Entity and GetPtrHash(newDesc.Entity) ~= GetPtrHash(oldDesc.Entity)) then
+ EID:printNewDescriptions()
+ return
+ end
+ end
+ end
+
+ -- Print our cached descriptions
+ for i,indicator in ipairs(EID.CachedIndicators) do
+ EID:renderIndicator(indicator[1], indicator[2])
+ end
+ for i,oldDesc in ipairs(EID.previousDescs) do
+ EID:printDescription(oldDesc, i)
+ end
+end
+
+function EID:printNewDescriptions()
+ EID.CachingDescription = true
+ EID.CachedIcons = {}
+ EID.CachedStrings = {}
+ EID.CachedRenderPoses = {}
+ EID.previousDescs = {}
+
+ for i,newDesc in ipairs(EID.descriptionsToPrint) do
+ if newDesc.Description == "UnidentifiedPill" then
+ if EID:renderUnidentifiedPill(newDesc.Entity) ~= false then
+ table.insert(EID.previousDescs, newDesc)
+ end
+ elseif newDesc.Description == "QuestionMark" then
+ if EID:renderQuestionMark(newDesc.Entity) ~= false then
+ table.insert(EID.previousDescs, newDesc)
+ end
+ elseif EID:printDescription(newDesc) ~= false then
+ table.insert(EID.previousDescs, newDesc)
+ end
+ end
+ EID.CachingDescription = false
+end
+
+function EID:printDescription(desc, cachedID)
+ EID:PositionLocalMode(desc.Entity)
+ -- Do not print this description if it has to be drawn in the top-left and we've already drawn a top-left desc this frame
+ if EID.CurrentScaleType == "Size" then
+ if alwaysUseLocalMode then return false
+ else alwaysUseLocalMode = true end
+ end
local renderPos = EID:getTextPosition()
- EID.CachingDescription = false
- if previousDesc == desc.Description and not EID.OptionChanged and not EID.MCM_OptionChanged then
- local localModeDiff = renderPos - previousRenderPos
+ --print(cachedID)
+ if cachedID then
+ local localModeDiff = renderPos - EID.CachedRenderPoses[cachedID]
-- Drawing our currently saved description
- for _,icon in ipairs(EID.CachedIcons) do
+ for _,icon in ipairs(EID.CachedIcons[cachedID]) do
-- set inline icon to specific frame
if icon[6] >= 0 then
icon[1]:SetFrame(icon[5], icon[6])
@@ -459,7 +532,7 @@ function EID:printDescription(desc)
end
EID:renderIcon(icon[1], icon[2] + localModeDiff.X, icon[3] + localModeDiff.Y, icon[4])
end
- for i,str in ipairs(EID.CachedStrings) do
+ for _,str in ipairs(EID.CachedStrings[cachedID]) do
-- check for func (Rainbow, Blink, Fade), reset the color's Alpha to EID.Config["Transparency"] if func
if str[5] then
str[4].Alpha = str[6]
@@ -469,12 +542,9 @@ function EID:printDescription(desc)
end
return
else
- -- Drawing a new description
- EID.CachingDescription = true
- EID.CachedIcons = {}
- EID.CachedStrings = {}
- previousDesc = desc.Description
- previousRenderPos = Vector(renderPos.X, renderPos.Y)
+ table.insert(EID.CachedIcons, {})
+ table.insert(EID.CachedStrings, {})
+ table.insert(EID.CachedRenderPoses, Vector(renderPos.X, renderPos.Y))
end
local textScale = Vector(EID.Scale, EID.Scale)
@@ -667,13 +737,49 @@ end
---------------------------------------------------------------------------
---------------------------Handle Rendering--------------------------------
-function EID:renderQuestionMark()
+function EID:renderQuestionMark(entity)
+ EID:PositionLocalMode(entity)
+ if EID.CurrentScaleType == "Size" then
+ if alwaysUseLocalMode then return false
+ else alwaysUseLocalMode = true end
+ end
EID.IconSprite:Play("CurseOfBlind")
local pos = EID:getTextPosition()
+ if EID.CachingDescription then
+ table.insert(EID.CachedStrings, {})
+ table.insert(EID.CachedIcons, {})
+ table.insert(EID.CachedRenderPoses, Vector(pos.X, pos.Y))
+ end
EID:renderIcon(EID.IconSprite, pos.X + 5 * EID.Scale, pos.Y + 5 * EID.Scale, nil, "CurseOfBlind", 0)
end
-function EID:renderIndicator(entity)
+function EID:renderUnidentifiedPill(entity)
+ EID:PositionLocalMode(entity)
+ if EID.CurrentScaleType == "Size" then
+ if alwaysUseLocalMode then return false
+ else alwaysUseLocalMode = true end
+ end
+ local pillColor = entity.SubType
+ if pillColor >= 2049 then
+ pillColor = pillColor - 2048
+ end
+ local pos = EID:getTextPosition()
+ if EID.CachingDescription then
+ table.insert(EID.CachedStrings, {})
+ table.insert(EID.CachedIcons, {})
+ table.insert(EID.CachedRenderPoses, Vector(pos.X, pos.Y))
+ end
+ EID:renderString(
+ "{{Pill"..pillColor.."}} "..EID:getDescriptionEntry("unidentifiedPill"),
+ pos,
+ Vector(EID.Scale, EID.Scale),
+ EID:getErrorColor()
+ )
+end
+
+-- RGB colors for each player's highlights (Red, Blue, Green, Yellow)
+local playerRGB = { {1,0.6,0.6}, {0.5,0.75,1}, {0.5,1,0.75}, {0.9, 0.9, 0.5} }
+function EID:renderIndicator(entity, playerNum)
if EID.Config["Indicator"] == "none" then
return
end
@@ -708,20 +814,25 @@ function EID:renderIndicator(entity)
-- Don't apply sprite.Color changes to Effects (Dice Floors, Card Reading Portals), use Arrow instead
if EID.Config["Indicator"] == "arrow" or entity.Type == 1000 then
- ArrowSprite:Update()
- ArrowSprite:Render(arrowPos, nullVector, nullVector)
+ ArrowSprite:RenderLayer(playerNum-1, arrowPos, nullVector, nullVector)
else
+ local colorMult = {1,1,1}
+ if EID.isMultiplayer then colorMult = playerRGB[playerNum] end
if EID.Config["Indicator"] == "blink" then
local c = 255 - math.floor(255 * ((entity.FrameCount % 40) / 40))
- sprite.Color = Color(1, 1, 1, 1, c/repDiv, c/repDiv, c/repDiv)
+ local r, g, b = math.floor(c*colorMult[1]), math.floor(c*colorMult[2]), math.floor(c*colorMult[3])
+ sprite.Color = Color(1, 1, 1, 1, r/repDiv, g/repDiv, b/repDiv)
EID:renderEntity(entity, sprite, entityPos)
sprite.Color = Color(1, 1, 1, 1, 0, 0, 0)
else
- if EID.Config["Indicator"] == "border" then
+ if EID.Config["Indicator"] == "highlight" then
local c = 255 - math.floor(255 * ((entity.FrameCount % 40) / 40))
- sprite.Color = Color(1, 1, 1, 1, c/repDiv, c/repDiv, c/repDiv)
- elseif EID.Config["Indicator"] == "highlight" then
- sprite.Color = Color(1, 1, 1, 1, 255/repDiv, 255/repDiv, 255/repDiv)
+ local r, g, b = math.floor(c*colorMult[1]), math.floor(c*colorMult[2]), math.floor(c*colorMult[3])
+ sprite.Color = Color(1, 1, 1, 1, r/repDiv, g/repDiv, b/repDiv)
+ elseif EID.Config["Indicator"] == "border" then
+ local c = 255
+ local r, g, b = math.floor(c*colorMult[1]), math.floor(c*colorMult[2]), math.floor(c*colorMult[3])
+ sprite.Color = Color(1, 1, 1, 1, r/repDiv, g/repDiv, b/repDiv)
end
EID:renderEntity(entity, sprite, entityPos + Vector(0, 1))
EID:renderEntity(entity, sprite, entityPos + Vector(0, -1))
@@ -738,7 +849,7 @@ end
function EID:PositionLocalMode(entity)
-- don't use Local Mode for descriptions without an entity (or dice floors)
- if EID.Config["DisplayMode"] == "local" and entity and entity.Variant ~= EffectVariant.DICE_FLOOR then
+ if (EID.Config["DisplayMode"] == "local" or alwaysUseLocalMode) and entity and entity.Variant ~= EffectVariant.DICE_FLOOR then
EID.Scale = EID.Config["LocalModeSize"]
EID.CurrentScaleType = "LocalModeSize"
local textBoxWidth = EID.Config["LocalModeCentered"] and tonumber(EID.Config["TextboxWidth"])/2 * EID.Scale or -30
@@ -809,12 +920,50 @@ function EID:handleHoverHUD()
end
function EID:setPlayer()
- local p = Isaac.GetPlayer(0)
- if REPENTANCE and p:GetPlayerType() == PlayerType.PLAYER_THEFORGOTTEN_B then
- EID.player = p:GetOtherTwin() or p
- else
- EID.player = p
+ local numPlayers = game:GetNumPlayers()
+ -- Old simple setPlayer, to reduce runtime in single player
+ if numPlayers == 1 or not EID.Config["CoopDescriptions"] then
+ local p = Isaac.GetPlayer(0)
+ if REPENTANCE and p:GetPlayerType() == PlayerType.PLAYER_THEFORGOTTEN_B then
+ EID.player = p:GetOtherTwin() or p
+ else
+ EID.player = p
+ end
+ EID.players = { EID.player, REPENTANCE and EID.player:GetOtherTwin() }
+ EID.coopMainPlayers = { EID.player }
+ EID.coopAllPlayers = EID.players
+ EID.controllerIndexes[p.ControllerIndex] = 1
+ return
end
+
+ -- Get our primary player, our list of all players, and a list of the primary player for each controller
+ EID.coopAllPlayers = {} -- all player characters in order
+ EID.coopMainPlayers = {} -- the primary player character from each controller
+ EID.controllerIndexes = {} -- simple table to map each controller index to P1/P2/P3/P4 (since index 0 could be P2)
+ local currentPlayerNum = 1
+ for i = 0, numPlayers - 1 do
+ local player = Isaac.GetPlayer(i)
+ -- Don't include player entities with a parent (Strawman / Soul of Jacob and Esau)
+ if player.Parent == nil then
+ local newIndex = not EID.controllerIndexes[player.ControllerIndex]
+ -- Tainted Soul is treated as the primary player for Tainted Forgotten, so swap them
+ if REPENTANCE and (player:GetPlayerType() == PlayerType.PLAYER_THEFORGOTTEN_B
+ or player:GetPlayerType() == PlayerType.PLAYER_THESOUL_B) then
+ player = player:GetOtherTwin() or player
+ end
+
+ table.insert(EID.coopAllPlayers, player)
+ if newIndex then
+ table.insert(EID.coopMainPlayers, player)
+ EID.controllerIndexes[player.ControllerIndex] = currentPlayerNum
+ currentPlayerNum = currentPlayerNum + 1
+ end
+ end
+ end
+ EID.player = EID.coopAllPlayers[1]
+ -- second entry will be nil if no twin or AB+ (REPENTANCE = nil)
+ EID.players = { EID.player, REPENTANCE and EID.player:GetOtherTwin() }
+ EID.isMultiplayer = #EID.coopMainPlayers > 1
end
---------------------------------------------------------------------------
@@ -853,7 +1002,7 @@ function EID:onGameUpdate()
end
-- Remove Crane Game item data if it's giving the prize out
- if REPENTANCE and EID.GameUpdateCount % 15 == 0 then
+ if REPENTANCE and EID.GameUpdateCount % 10 == 0 then
for _, crane in ipairs(Isaac.FindByType(6, 16, -1, true, false)) do
if EID.CraneItemType[tostring(crane.InitSeed)] then
if crane:GetSprite():IsPlaying("Prize") then
@@ -990,6 +1139,7 @@ local searchPartitions = EntityPartition.FAMILIAR + EntityPartition.ENEMY + Enti
EID.lastDescriptionEntity = nil
EID.lastDist = 0
EID.OptionChanged = false
+EID.bagPlayer = nil
local function onRender(t)
-- Increases by 60 per second, ignores pauses
@@ -997,9 +1147,20 @@ local function onRender(t)
EID.OptionChanged = EID.MCM_OptionChanged
EID.MCM_OptionChanged = false
EID:resumeCoroutines()
+ ArrowSprite:Update()
+ EID:setPlayer()
+ if REPENTANCE then
+ local hasBag, bagPlayer = EID:PlayersHaveCollectible(710)
+ if hasBag then
+ EID.bagPlayer = bagPlayer
+ EID:handleBagOfCraftingUpdating()
+ end
+ end
EID.isDisplaying = false
- EID:setPlayer()
+ EID.descriptionsToPrint = {}
+ EID.entitiesToPrint = {}
+ alwaysUseLocalMode = false
-- Do not check our hotkeys while a tab that can modify the hotkey is open
if EID.MCMCompat_isDisplayingEIDTab ~= "Visuals" then
-- scale key must be handled before resetting to non-local mode
@@ -1008,7 +1169,6 @@ local function onRender(t)
EID.isHidden = not EID.isHidden
end
end
- EID:PositionLocalMode() -- default to non-local mode to fix MCM / Bag errors
EID.TabPreviewID = 0
-- If MCM is open, don't show anything unless we're in a tab labeled as "Visuals" or "Crafting"
@@ -1016,6 +1176,7 @@ local function onRender(t)
return
end
+ -- Handle descriptions that display regardless of player position
checkStartOfRunWarnings()
checkPosModifiers()
EID:renderMCMDummyDescription()
@@ -1034,222 +1195,240 @@ local function onRender(t)
return
end
- if REPENTANCE and EID.player:HasCollectible(710) then
- local success = EID:handleBagOfCraftingRendering()
- -- If the Bag of Crafting did rendering, don't display any other description
- if success then
- return
- end
+ -- This is not a frame we should check for new descriptions; just print our cached ones
+ if not EID:RefreshThisFrame() then
+ EID:printDescriptions(true)
+ return
end
- -- If we're in the Crafting options tab, the only rendering we want to happen is the Bag of Crafting
- if EID.MCMCompat_isDisplayingEIDTab == "Crafting" then return end
- EID.lastDescriptionEntity = nil
- EID.lastDist = 10000
- local searchGroups = {}
- local sourcePos = EID.player.Position
+ EID.CachedIndicators = {}
if EID.Config["EnableMouseControls"] then
local hudDescription = EID:handleHoverHUD()
if hudDescription then
- EID:printDescription(hudDescription)
- return
+ EID:addDescriptionToPrint(hudDescription)
end
end
- table.insert(searchGroups, Isaac.FindInRadius(sourcePos, tonumber(EID.Config["MaxDistance"])*40, searchPartitions))
- for k,_ in pairs(EID.effectList) do
- table.insert(searchGroups, Isaac.FindByType(EntityType.ENTITY_EFFECT, k, -1, true, false))
+ -- Decide how many player entities we're checking
+ -- Primary P1 only, P1 + Esau, primary all controllers, or all controllers + Esaus
+ local playerSearch = { EID.player }
+ if EID.Config["CoopDescriptions"] then
+ if EID.Config["PairedPlayerDescriptions"] then playerSearch = EID.coopAllPlayers
+ else playerSearch = EID.coopMainPlayers end
+ elseif EID.Config["PairedPlayerDescriptions"] then
+ playerSearch = EID.players
end
- for _, entitySearch in ipairs(searchGroups) do
- for i, entity in ipairs(entitySearch) do
- if EID:hasDescription(entity) and entity.FrameCount > 0 then
- local diff = entity.Position:__sub(sourcePos)
- -- break ties with the render offset (for Mega Chest double collectibles)
- if diff:Length() == EID.lastDist and entity:GetData()["EID_RenderOffset"] then
- diff = diff + entity:GetData()["EID_RenderOffset"]
+ -- Check for descriptions to print per player
+ for i,player in ipairs(playerSearch) do
+ local displayedDesc = false
+
+ -- Check if this player has the Bag of Crafting
+ local craftingSuccess = false
+ if REPENTANCE and player:HasCollectible(710) then
+ craftingSuccess = EID:handleBagOfCraftingRendering()
+ if craftingSuccess then
+ displayedDesc = true
+ end
+ end
+ -- If we're in the Crafting options tab, the only rendering we want to happen is the Bag of Crafting preview
+ if EID.MCMCompat_isDisplayingEIDTab == "Crafting" then
+ if craftingSuccess then
+ EID:printDescription(EID.descriptionsToPrint[#EID.descriptionsToPrint])
+ return
+ end
+ if i == #playerSearch then return end
+ end
+
+ if not displayedDesc or EID.Config["DisplayAllNearby"] then
+ -- Searching for the closest describable entity to this player
+ EID.lastDescriptionEntity = nil
+ EID.lastDist = 10000
+ local searchGroups = {}
+ local sourcePos = player.Position
+ local inRangeEntities = {}
+
+ table.insert(searchGroups, Isaac.FindInRadius(sourcePos, tonumber(EID.Config["MaxDistance"])*40, searchPartitions))
+ for k,_ in pairs(EID.effectList) do
+ table.insert(searchGroups, Isaac.FindByType(EntityType.ENTITY_EFFECT, k, -1, true, false))
+ end
+
+ for _, entitySearch in ipairs(searchGroups) do
+ for i, entity in ipairs(entitySearch) do
+ if EID:hasDescription(entity) and entity.FrameCount > 0 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)
+ if diff:Length() == EID.lastDist and entity:GetData()["EID_RenderOffset"] then
+ diff = diff + entity:GetData()["EID_RenderOffset"]
+ end
+ if diff:Length() < EID.lastDist then
+ EID.lastDescriptionEntity = entity
+ EID.lastDist = diff:Length()
+ end
+ end
end
- if diff:Length() < EID.lastDist then
- EID.lastDescriptionEntity = entity
- EID.lastDist = diff:Length()
+ end
+
+ if EID.Config["DisplayAllNearby"] then
+ -- make the closest entity the first to get drawn
+ table.insert(inRangeEntities, 1, EID.lastDescriptionEntity)
+ else
+ -- we only want to describe the closest entity
+ inRangeEntities = { EID.lastDescriptionEntity }
+ end
+ -- Only display the indicator for the primary (closest / crafting) description
+ if EID.lastDescriptionEntity and not displayedCrafting then
+ EID:renderIndicator(EID.lastDescriptionEntity, EID.controllerIndexes[player.ControllerIndex] or 1)
+ table.insert(EID.CachedIndicators, {EID.lastDescriptionEntity, EID.controllerIndexes[player.ControllerIndex]})
+ end
+
+ for _,closest in ipairs(inRangeEntities) do
+
+ --Handle GetData Entities (specific)
+ if EID.Config["EnableEntityDescriptions"] and EID:getEntityData(closest, "EID_Description") then
+ local desc = EID:getEntityData(closest, "EID_Description")
+ local origDesc = EID:getDescriptionObjByEntity(closest)
+ if type(desc) == "table" then
+ origDesc.Description = desc.Description or origDesc.Description
+ origDesc.Name = desc.Name or origDesc.Name
+ origDesc.Transformation = desc.Transformation or origDesc.Transformation
+ else
+ origDesc.Description = desc
+ end
+ EID:addDescriptionToPrint(origDesc)
+
+ -- Handle Glitched Items
+ elseif closest.Type == 5 and closest.Variant == 100 and closest.SubType > 4294960000 then
+ local glitchedObj = EID:getDescriptionObj(closest.Type, closest.Variant, closest.SubType, closest)
+ local glitchedDesc = EID:getXMLDescription(closest.Type, closest.Variant, closest.SubType)
+
+ -- force the default glitchy description if option is off
+ if not EID.Config["DisplayGlitchedItemInfo"] then
+ glitchedObj.Description = glitchedDesc
+ -- grab the Item Config info if eid_tmtrainer.lua hasn't taken care of it, and it hasn't been done before
+ elseif not debug and glitchedObj.Description == glitchedDesc then
+ EID:addCollectible(closest.SubType, EID:CheckGlitchedItemConfig(closest.SubType) .. glitchedDesc)
+ glitchedObj = EID:getDescriptionObj(closest.Type, closest.Variant, closest.SubType, closest)
+ end
+
+ EID:addDescriptionToPrint(glitchedObj)
+ -- Handle Dice Room Floor
+ elseif closest.Type == 1000 and closest.Variant == 76 then
+ EID:addDescriptionToPrint(EID:getDescriptionObj(closest.Type, closest.Variant, closest.SubType+1, closest))
+ -- Handle Card Reading Portals
+ elseif closest.Type == 1000 and closest.Variant == 161 and closest.SubType <= 2 then
+ local subtypeToCard = {18, 5, 19}
+ -- Reuse the descriptions of The Emperor/Stars/Moon, so no localization needed
+ local descriptionObj = EID:getDescriptionObj(5, 300, subtypeToCard[closest.SubType+1], closest)
+ -- Card Reading's name
+ descriptionObj.Name = EID:getObjectName(5, 100, 660)
+ EID:addDescriptionToPrint(descriptionObj)
+ -- Handle Crane Game
+ elseif closest.Type == 6 and closest.Variant == 16 then
+ if EID.CraneItemType[tostring(closest.InitSeed)] then
+ if EID:getEntityData(closest, "EID_DontHide") ~= true then
+ if (EID:hasCurseBlind() and EID.Config["DisableOnCurse"]) or (game.Challenge == Challenge.CHALLENGE_APRILS_FOOL and EID.Config["DisableOnAprilFoolsChallenge"]) then
+ EID:addDescriptionToPrint({ Description = "QuestionMark", Entity = closest})
+ end
+ end
+ local collectibleID = EID.CraneItemType[tostring(closest.InitSeed)]
+ local descriptionObj = EID:getDescriptionObj(5, 100, collectibleID, closest)
+
+ EID:addDescriptionToPrint(descriptionObj)
+ end
+
+ elseif closest.Variant == 110 then
+ --Handle Broken Shovel
+ local descriptionObj = EID:getDescriptionObj(5, 100, 550, closest)
+ EID:addDescriptionToPrint(descriptionObj)
+
+ elseif closest.Variant == PickupVariant.PICKUP_TRINKET then
+ --Handle Trinkets
+ local descriptionObj = EID:getDescriptionObjByEntity(closest)
+ EID:addDescriptionToPrint(descriptionObj)
+
+ elseif closest.Variant == PickupVariant.PICKUP_COLLECTIBLE then
+ --Handle Collectibles
+ if EID:getEntityData(closest, "EID_DontHide") ~= true then
+ if (EID:hasCurseBlind() and not closest:ToPickup().Touched and EID.Config["DisableOnCurse"] and not isDeathCertRoom) or (EID.Config["DisableOnAltPath"] and not closest:ToPickup().Touched and EID:IsAltChoice(closest)) or (game.Challenge == Challenge.CHALLENGE_APRILS_FOOL and EID.Config["DisableOnAprilFoolsChallenge"]) then
+ EID:addDescriptionToPrint({ Description = "QuestionMark", Entity = closest})
+ end
+ end
+ local descriptionObj = EID:getDescriptionObjByEntity(closest)
+ EID:addDescriptionToPrint(descriptionObj)
+
+ elseif closest.Variant == PickupVariant.PICKUP_TAROTCARD then
+ --Handle Cards & Runes
+ if (not EID.Config["DisplayObstructedCardInfo"] or not EID.Config["DisplayObstructedSoulstoneInfo"]) and closest.FrameCount < 3 then
+ -- small delay when having obstruction enabled & entering the room to prevent spoilers
+ --return
+ end
+ if EID:getEntityData(closest, "EID_DontHide") ~= true then
+ local isSoulstone = closest.SubType >= 81 and closest.SubType <= 97
+ local hideinShop = closest:ToPickup():IsShopItem() and ((not isSoulstone and not EID.Config["DisplayCardInfoShop"]) or (isSoulstone and not EID.Config["DisplaySoulstoneInfoShop"]))
+ local isOptionsSpawn = REPENTANCE and not EID.Config["DisplayCardInfoOptions?"] and closest:ToPickup().OptionsPickupIndex > 0
+ local obstructed = ((not isSoulstone and not EID.Config["DisplayObstructedCardInfo"]) or
+ (not EID.Config["DisplayObstructedSoulstoneInfo"] and isSoulstone)) and
+ (not pathsChecked[closest.InitSeed] and not attemptPathfind(closest))
+ if isOptionsSpawn or hideinShop or obstructed or (REPENTANCE and game.Challenge == Challenge.CHALLENGE_CANTRIPPED) then
+ EID:addDescriptionToPrint({ Description = "QuestionMark", Entity = closest})
+ end
+ end
+ local descriptionObj = EID:getDescriptionObjByEntity(closest)
+ EID:addDescriptionToPrint(descriptionObj)
+
+ elseif closest.Variant == PickupVariant.PICKUP_PILL then
+ --Handle Pills
+ if not EID.Config["DisplayObstructedPillInfo"] and closest.FrameCount < 3 then
+ -- small delay when having obstruction enabled & entering the room to prevent spoilers
+ --return
+ end
+ if EID:getEntityData(closest, "EID_DontHide") ~= true then
+ local hideinShop = closest:ToPickup():IsShopItem() and not EID.Config["DisplayPillInfoShop"]
+ local isOptionsSpawn = REPENTANCE and not EID.Config["DisplayPillInfoOptions?"] and closest:ToPickup().OptionsPickupIndex > 0
+ local obstructed = not EID.Config["DisplayObstructedPillInfo"] and
+ (not pathsChecked[closest.InitSeed] and not attemptPathfind(closest))
+ if isOptionsSpawn or hideinShop or obstructed then
+ EID:addDescriptionToPrint({ Description = "QuestionMark", Entity = closest})
+ end
+ end
+
+ local pillColor = closest.SubType
+ local pool = game:GetItemPool()
+ local identified = pool:IsPillIdentified(pillColor)
+ if REPENTANCE and pillColor % PillColor.PILL_GIANT_FLAG == PillColor.PILL_GOLD then identified = true end
+ if (identified or EID.Config["ShowUnidentifiedPillDescriptions"]) then
+ local descEntry = EID:getDescriptionObj(closest.Type, closest.Variant, pillColor, closest)
+ EID:addDescriptionToPrint(descEntry)
+ else
+ EID:addDescriptionToPrint({ Description = "UnidentifiedPill", Entity = closest})
+ end
+ elseif EID.Config["EnableEntityDescriptions"] then
+ --Handle Entities (omni)
+ local descriptionEntry = EID:getDescriptionObjByEntity(closest)
+ if descriptionEntry~=nil then
+ EID:addDescriptionToPrint(descriptionEntry)
+ end
end
end
end
end
-
-
- local closest = EID.lastDescriptionEntity
- -- if no entity in range, display Sacrifice Room information
- if EID.lastDist / 40 > tonumber(EID.Config["MaxDistance"]) then
+ -- if no entities to display, display Sacrifice Room information
+ -- it will be last priority for the main spot if DisplayAllNearby is on
+ if #EID.descriptionsToPrint == 0 or EID.Config["DisplayAllNearby"] then
if game:GetRoom():GetType() == RoomType.ROOM_SACRIFICE and EID.Config["DisplaySacrificeInfo"] then
local curRoomIndex = game:GetLevel():GetCurrentRoomIndex()
local curCounter = EID.sacrificeCounter[curRoomIndex] or 1
local sacrificeDesc = EID:getDescriptionObj(-999, -1, curCounter)
sacrificeDesc.Name = sacrificeDesc.Name.." ("..curCounter.."/12)"
- EID:printDescription(sacrificeDesc)
- end
- return
- end
-
- if closest == nil then
- return
- end
-
- EID.isDisplaying = true
-
- --Handle Indicators
- EID:renderIndicator(closest)
-
- --Position the description under the entity in Local Mode
- EID:PositionLocalMode(closest)
-
- --Handle GetData Entities (specific)
- if EID.Config["EnableEntityDescriptions"] and EID:getEntityData(closest, "EID_Description") then
- local desc = EID:getEntityData(closest, "EID_Description")
- local origDesc = EID:getDescriptionObjByEntity(closest)
- if type(desc) == "table" then
- origDesc.Description = desc.Description or origDesc.Description
- origDesc.Name = desc.Name or origDesc.Name
- origDesc.Transformation = desc.Transformation or origDesc.Transformation
- else
- origDesc.Description = desc
- end
- EID:printDescription(origDesc)
- return
- end
-
- -- Handle Glitched Items
- if closest.Type == 5 and closest.Variant == 100 and closest.SubType > 4294960000 then
- local glitchedObj = EID:getDescriptionObj(closest.Type, closest.Variant, closest.SubType)
- local glitchedDesc = EID:getXMLDescription(closest.Type, closest.Variant, closest.SubType)
-
- -- force the default glitchy description if option is off
- if not EID.Config["DisplayGlitchedItemInfo"] then
- glitchedObj.Description = glitchedDesc
- -- grab the Item Config info if eid_tmtrainer.lua hasn't taken care of it, and it hasn't been done before
- elseif not debug and glitchedObj.Description == glitchedDesc then
- EID:addCollectible(closest.SubType, EID:CheckGlitchedItemConfig(closest.SubType) .. glitchedDesc)
- glitchedObj = EID:getDescriptionObj(closest.Type, closest.Variant, closest.SubType)
- end
-
- EID:printDescription(glitchedObj)
- return
- -- Handle Dice Room Floor
- elseif closest.Type == 1000 and closest.Variant == 76 then
- EID:printDescription(EID:getDescriptionObj(closest.Type, closest.Variant, closest.SubType+1, closest))
- return
- -- Handle Card Reading Portals
- elseif closest.Type == 1000 and closest.Variant == 161 and closest.SubType <= 2 then
- local subtypeToCard = {18, 5, 19}
- -- Reuse the descriptions of The Emperor/Stars/Moon, so no localization needed
- local descriptionObj = EID:getDescriptionObj(5, 300, subtypeToCard[closest.SubType+1], closest)
- -- Card Reading's name
- descriptionObj.Name = EID:getObjectName(5, 100, 660)
- EID:printDescription(descriptionObj)
- return
- -- Handle Crane Game
- elseif closest.Type == 6 and closest.Variant == 16 then
- if EID.CraneItemType[tostring(closest.InitSeed)] then
- if EID:getEntityData(closest, "EID_DontHide") ~= true then
- if (EID:hasCurseBlind() and EID.Config["DisableOnCurse"]) or (game.Challenge == Challenge.CHALLENGE_APRILS_FOOL and EID.Config["DisableOnAprilFoolsChallenge"]) then
- EID:renderQuestionMark()
- return
- end
- end
- local collectibleID = EID.CraneItemType[tostring(closest.InitSeed)]
- local descriptionObj = EID:getDescriptionObj(5, 100, collectibleID, closest)
-
- EID:printDescription(descriptionObj)
- return
+ EID:addDescriptionToPrint(sacrificeDesc)
end
end
- if closest.Variant == 110 then
- --Handle Broken Shovel
- local descriptionObj = EID:getDescriptionObj(5, 100, 550, closest)
- EID:printDescription(descriptionObj)
- elseif closest.Variant == PickupVariant.PICKUP_TRINKET then
- --Handle Trinkets
- local descriptionObj = EID:getDescriptionObjByEntity(closest)
- EID:printDescription(descriptionObj)
-
- elseif closest.Variant == PickupVariant.PICKUP_COLLECTIBLE then
- --Handle Collectibles
- if EID:getEntityData(closest, "EID_DontHide") ~= true then
- if (EID:hasCurseBlind() and not closest:ToPickup().Touched and EID.Config["DisableOnCurse"] and not isDeathCertRoom) or (EID.Config["DisableOnAltPath"] and not closest:ToPickup().Touched and EID:IsAltChoice(closest)) or (game.Challenge == Challenge.CHALLENGE_APRILS_FOOL and EID.Config["DisableOnAprilFoolsChallenge"]) then
- EID:renderQuestionMark()
- return
- end
- end
- local descriptionObj = EID:getDescriptionObjByEntity(closest)
- EID:printDescription(descriptionObj)
-
- elseif closest.Variant == PickupVariant.PICKUP_TAROTCARD then
- --Handle Cards & Runes
- if (not EID.Config["DisplayObstructedCardInfo"] or not EID.Config["DisplayObstructedSoulstoneInfo"]) and closest.FrameCount < 3 then
- -- small delay when having obstruction enabled & entering the room to prevent spoilers
- return
- end
- if EID:getEntityData(closest, "EID_DontHide") ~= true then
- local isSoulstone = closest.SubType >= 81 and closest.SubType <= 97
- local hideinShop = closest:ToPickup():IsShopItem() and ((not isSoulstone and not EID.Config["DisplayCardInfoShop"]) or (isSoulstone and not EID.Config["DisplaySoulstoneInfoShop"]))
- local isOptionsSpawn = REPENTANCE and not EID.Config["DisplayCardInfoOptions?"] and closest:ToPickup().OptionsPickupIndex > 0
- local obstructed = ((not isSoulstone and not EID.Config["DisplayObstructedCardInfo"]) or
- (not EID.Config["DisplayObstructedSoulstoneInfo"] and isSoulstone)) and
- (not pathsChecked[closest.InitSeed] and not attemptPathfind(closest))
- if isOptionsSpawn or hideinShop or obstructed or (REPENTANCE and game.Challenge == Challenge.CHALLENGE_CANTRIPPED) then
- EID:renderQuestionMark()
- return
- end
- end
- local descriptionObj = EID:getDescriptionObjByEntity(closest)
- EID:printDescription(descriptionObj)
-
- elseif closest.Variant == PickupVariant.PICKUP_PILL then
- --Handle Pills
- if not EID.Config["DisplayObstructedPillInfo"] and closest.FrameCount < 3 then
- -- small delay when having obstruction enabled & entering the room to prevent spoilers
- return
- end
- if EID:getEntityData(closest, "EID_DontHide") ~= true then
- local hideinShop = closest:ToPickup():IsShopItem() and not EID.Config["DisplayPillInfoShop"]
- local isOptionsSpawn = REPENTANCE and not EID.Config["DisplayPillInfoOptions?"] and closest:ToPickup().OptionsPickupIndex > 0
- local obstructed = not EID.Config["DisplayObstructedPillInfo"] and
- (not pathsChecked[closest.InitSeed] and not attemptPathfind(closest))
- if isOptionsSpawn or hideinShop or obstructed then
- EID:renderQuestionMark()
- return
- end
- end
-
- local pillColor = closest.SubType
- local pool = game:GetItemPool()
- local identified = pool:IsPillIdentified(pillColor)
- if REPENTANCE and pillColor % PillColor.PILL_GIANT_FLAG == PillColor.PILL_GOLD then identified = true end
- if (identified or EID.Config["ShowUnidentifiedPillDescriptions"]) then
- local descEntry = EID:getDescriptionObj(closest.Type, closest.Variant, pillColor, closest)
- EID:printDescription(descEntry)
- else
- if pillColor >= 2049 then
- pillColor = pillColor - 2048
- end
- EID:renderString(
- "{{Pill"..pillColor.."}} "..EID:getDescriptionEntry("unidentifiedPill"),
- EID:getTextPosition(),
- Vector(EID.Scale, EID.Scale),
- EID:getErrorColor()
- )
- end
- elseif EID.Config["EnableEntityDescriptions"] then
- --Handle Entities (omni)
- local descriptionEntry = EID:getDescriptionObjByEntity(closest)
- if descriptionEntry~=nil then
- EID:printDescription(descriptionEntry)
- return
- end
- end
+ EID:printDescriptions()
end
EID:AddCallback(ModCallbacks.MC_POST_RENDER, onRender)
diff --git a/mod_config_menu.lua b/mod_config_menu.lua
index ade5bc9c..a059b268 100644
--- a/mod_config_menu.lua
+++ b/mod_config_menu.lua
@@ -356,7 +356,9 @@ if MCMLoaded then
-- maxDistance
local distances = {1, 1.5, 2, 3, 4, 5, 6, 7, 8, 9, 10}
EID:AddScrollSetting("General", "MaxDistance", "Max Distance", distances, {label = " Grids",
- infoText = "Distance to the object until descriptions are displayed."})
+ infoText = "Distance to the object until descriptions are displayed"})
+ EID:AddBooleanSetting("General", "DisplayAllNearby", "Display All Objects In Range",
+ { infoText = "Descriptions besides the closest one will be shown using the Local display mode" })
--------Reset to default
MCM.AddSpace("EID", "General")
@@ -383,6 +385,15 @@ if MCMLoaded then
---------------------------------------------------------------------------
---------------------------------Display-----------------------------------
+ local refreshRateSteps = {1, 2, 3, 4, 6, 10, 12, 15, 20, 30, 60}
+ EID:AddScrollSetting("Display", "RefreshRate", "Description Refresh Rate", refreshRateSteps,
+ {infoText = "How many times per second that EID updates what description to show"})
+ EID:AddBooleanSetting("Display", "CoopDescriptions", "Co-op Player Descriptions",
+ {infoText = "Allow Players 2, 3, and 4 to display descriptions in Co-op"})
+ EID:AddBooleanSetting("Display", "PairedPlayerDescriptions", "Paired Player Descriptions", {repOnly = true,
+ infoText = "Allow paired characters like Esau and Tainted Forgotten's bone pile to display descriptions"})
+ MCM.AddSpace("EID", "Display")
+
-- Simple toggles of what descriptions the user wants displayed
EID:AddBooleanSetting("Display", "DisplayItemInfo", "Collectible Infos", {displayingTab = ""})
EID:AddBooleanSetting("Display", "DisplayTrinketInfo", "Trinket Infos")
@@ -406,7 +417,7 @@ if MCMLoaded then
EID:AddBooleanSetting("Display", "SpindownDiceDisplayID", "Display IDs",
{repOnly = true, infoText = "Display IDs for Spindown Dice results"})
-- Display names for Spindown Dice results
- EID:AddBooleanSetting("Display", "SpindownDiceDisplayName", "Display names",
+ EID:AddBooleanSetting("Display", "SpindownDiceDisplayName", "Display Names",
{repOnly = true, infoText = "Display names for Spindown Dice results"})
-- Spindown Dice skip locked items
EID:AddBooleanSetting("Display", "SpindownDiceSkipLocked", "Skip Locked Items",
diff --git a/resources/gfx/eid_transform_icons.anm2 b/resources/gfx/eid_transform_icons.anm2
index 4c435e58..3e312250 100644
--- a/resources/gfx/eid_transform_icons.anm2
+++ b/resources/gfx/eid_transform_icons.anm2
@@ -6,6 +6,9 @@
+
+
+
@@ -217,6 +220,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resources/gfx/eid_transform_icons.png b/resources/gfx/eid_transform_icons.png
index 4de15951..e6282c12 100644
Binary files a/resources/gfx/eid_transform_icons.png and b/resources/gfx/eid_transform_icons.png differ