From 4bfe3a3ecc558fde225071e484c17d3f56da280e Mon Sep 17 00:00:00 2001 From: Buurazu Date: Wed, 2 Mar 2022 00:23:20 -0500 Subject: [PATCH] Co-op overhaul --- main.lua | 587 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 364 insertions(+), 223 deletions(-) diff --git a/main.lua b/main.lua index ff87edb4..dba8e23e 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,82 @@ end --------------------------------------------------------------------------- ---------------------------Printing Functions------------------------------ -local previousDesc = "" -local previousRenderPos = Vector.Zero +EID.previousDescs = {} EID.CachingDescription = false EID.CachedIcons = {} EID.CachedStrings = {} +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,newDesc in ipairs(EID.descriptionsToPrint) do + EID:printDescription(newDesc, 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 +527,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 +537,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 +732,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 +809,21 @@ 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) + sprite.Color = Color(1, 1, 1, 1, c/repDiv * colorMult[1], c/repDiv * colorMult[2], c/repDiv * colorMult[3]) EID:renderEntity(entity, sprite, entityPos) sprite.Color = Color(1, 1, 1, 1, 0, 0, 0) else if EID.Config["Indicator"] == "border" then local c = 255 - math.floor(255 * ((entity.FrameCount % 40) / 40)) - sprite.Color = Color(1, 1, 1, 1, c/repDiv, c/repDiv, c/repDiv) + sprite.Color = Color(1, 1, 1, 1, c/repDiv * colorMult[1], c/repDiv * colorMult[2], c/repDiv * colorMult[3]) elseif EID.Config["Indicator"] == "highlight" then - sprite.Color = Color(1, 1, 1, 1, 255/repDiv, 255/repDiv, 255/repDiv) + sprite.Color = Color(1, 1, 1, 1, 255/repDiv* colorMult[1], 255/repDiv* colorMult[2], 255/repDiv* colorMult[3]) end EID:renderEntity(entity, sprite, entityPos + Vector(0, 1)) EID:renderEntity(entity, sprite, entityPos + Vector(0, -1)) @@ -738,7 +840,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 +911,35 @@ 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 + -- Get our primary player, our list of all players, and a list of the primary player for each controller + -- make this run less than 60 times a second? + 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, game:GetNumPlayers() - 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 --------------------------------------------------------------------------- @@ -990,6 +1115,8 @@ local searchPartitions = EntityPartition.FAMILIAR + EntityPartition.ENEMY + Enti EID.lastDescriptionEntity = nil EID.lastDist = 0 EID.OptionChanged = false +local currentPlayer = 1 +EID.bagPlayer = nil local function onRender(t) -- Increases by 60 per second, ignores pauses @@ -997,9 +1124,14 @@ local function onRender(t) EID.OptionChanged = EID.MCM_OptionChanged EID.MCM_OptionChanged = false EID:resumeCoroutines() + ArrowSprite:Update() EID.isDisplaying = false EID:setPlayer() + EID.bagPlayer = EID.player + 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 +1140,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 +1147,7 @@ local function onRender(t) return end + -- Handle descriptions that display regardless of player position checkStartOfRunWarnings() checkPosModifiers() EID:renderMCMDummyDescription() @@ -1034,222 +1166,231 @@ 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 - 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 - 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 + EID.bagPlayer = player + 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(descsToDisplay[#descsToDisplay]) end + return + 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 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 + --inRangeEntities = { EID.lastDescriptionEntity } + table.insert(inRangeEntities, 1, EID.lastDescriptionEntity) + -- 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]) + 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)