Merge pull request #483 from Buurazu/master

D Infinity, moving predictors to new file
This commit is contained in:
Buurazu authored and GitHub committed 2022-06-20 05:09:04 -04:00
commit 30e5b44591
6 files changed
+348 -272

No files matched your search

+1
View File
@@ -884,6 +884,7 @@ EID.descriptions[languageCode].MCM = {
DemoObjectText = "This text is in english#A very cool and long description to simulate linebreaks caused by EID textbox width!#{{Collectible4}} This is also a cool line#{{Heart}} This line loves you#{{AngelDevilChance}} This line can be your angel or your devil#\1 {{Damage}} +1 Stat Change up",
}
EID.descriptions[languageCode].HoldMapTitle = "Item Reminder"
-- the Hold Map description will predict the abilities of items with a header like "Item Name Result"
EID.descriptions[languageCode].HoldMapHeader = " Result"
+1 -81
View File
@@ -1051,71 +1051,6 @@ function EID:CheckGlitchedItemConfig(id)
return attributes
end
-- Void stat prediction helper functions (called from eid_modifiers when needed)
-- Didn't seem right to put them in main or eid_modifiers
local numVoidable = 0
local numRunable = 0
local function GetTwoIncreases(rng, tbl)
local statTable = {1,2,3,4,5,6}
-- perform 5 random swaps of our stat table
for i = 6, 2, -1 do
rng = EID:RNGNext(rng, 5, 9, 7)
local result = (rng % i) + 1
local temp = statTable[i]
statTable[i] = statTable[result]
statTable[result] = temp
end
-- the first two entries in the stat table get increased
tbl[statTable[1]] = tbl[statTable[1]] + 1
tbl[statTable[2]] = tbl[statTable[2]] + 1
return rng
end
-- Count the number of absorbable pedestals in the room
-- Returns a table of active items that will be absorbed
function EID:VoidRoomCheck()
numVoidable = 0
numRunable = 0
EID.VoidOptionIndexes = {}
local activesAbsorbed = {}
for _, entity in ipairs(Isaac.FindByType(5, 100, -1, true, false)) do
local pickup = entity:ToPickup()
-- Count this pedestal if it's not an active (or this is Black Rune), not a shop item, and (in Repentance) the first of its option index
-- TEST IF VOID ALWAYS ABSORBS THE FIRST OF ITS OPTION INDEX
if entity.SubType > 0 and not pickup:IsShopItem() and
(not REPENTANCE or pickup.OptionsPickupIndex == 0 or EID.VoidOptionIndexes[pickup.OptionsPickupIndex] == nil) then
numRunable = numRunable + 1
if REPENTANCE then EID.VoidOptionIndexes[pickup.OptionsPickupIndex] = entity.SubType end
if (EID.itemConfig:GetCollectible(entity.SubType).Type ~= ItemType.ITEM_ACTIVE) then
numVoidable = numVoidable + 1
else
table.insert(activesAbsorbed, entity.SubType)
end
end
end
return activesAbsorbed
end
-- Determine what stats will be increased after 1 absorption, the whole room's absorption, and whole room + a purchased item above your head
function EID:VoidRNGCheck(player, isRune)
local increases = {0, 0, 0, 0, 0, 0}
local startRNG = (isRune and player:GetCardRNG(Card.RUNE_BLACK):GetSeed()) or player:GetCollectibleRNG(CollectibleType.COLLECTIBLE_VOID):GetSeed()
local count = (isRune and numRunable) or numVoidable
local eidTable = (isRune and EID.BlackRuneStatIncreases) or EID.VoidStatIncreases
-- in Repentance, an additional RNG call is done before the 5 for stat ups when using Void
if REPENTANCE and not isRune then startRNG = EID:RNGNext(startRNG, 5, 9, 7) end
for i = 1, count do
startRNG = GetTwoIncreases(startRNG, increases)
if i == 1 then eidTable[3] = {table.unpack(increases)} end
end
eidTable[1] = {table.unpack(increases)}
-- do an extra check for what you'd get if you Void with a shop item above your head
GetTwoIncreases(startRNG, increases)
eidTable[2] = {table.unpack(increases)}
-- if there were no absorbable pedestals, the "single increase" stats are the same as the "one extra" stats
if count == 0 then eidTable[3] = {table.unpack(increases)} end
end
-- Converts a given CollectibleID into the respective Spindown dice result
function EID:getSpindownResult(collectibleID)
if collectibleID <= 0 or collectibleID > 4294960000 then return 0 end
@@ -1708,19 +1643,4 @@ end
-- Adds an EID Icon to an Object
function EID:AddIconToObject(eType, eVariant, eSubType, iconName)
EID.ObjectIcon[eType.."."..eVariant.."."..eSubType] = EID.InlineIcons[iconName]
end
-- reimplementation of RNG Seed shift feature used by the game. Mostly used for RNG Prediction features
-- Default RNG shift values for many things are 5, 9, 7
function EID:RNGNext(rngNum, shift1, shift2, shift3)
rngNum = rngNum ~ ((rngNum >> (shift1 or 5)) & 4294967295)
rngNum = rngNum ~ ((rngNum << (shift2 or 9)) & 4294967295)
rngNum = rngNum ~ ((rngNum >> (shift3 or 7)) & 4294967295)
return rngNum >> 0;
end
-- General RNG functions for RNG predicting
function EID:SeedToFloat(seed)
local multi = 2.3283061589829401E-10;
return seed * multi;
end
end
+39 -183
View File
@@ -4,17 +4,21 @@ local currentRoom
local blacklist
local holdMapDesc
local currentPlayer
-- Rainbow Worm's trinket IDs it grants, in order
local rainbowWormEffects = { [0] = 9, 11, 65, 27, 10, 12, 26, 66, 96, 144 }
-- Mysterious Paper does not play well with displaying Error 404's effect
local mysteriousPaperBlacklist = { [23] = true, [48] = true }
EID.InsideItemReminder = false
-- Simple function to help with adding properly formatted sections to the desc
local function append(icon, title, newDesc)
holdMapDesc = holdMapDesc .. (icon or "{{Blank}}") .. " {{ColorEIDObjName}}" .. title .. "#" .. newDesc .. "#"
end
-- Simple function to quickly get an item's RNG seed. We have no use for the RNG object itself because every other function it can do will advance the item's RNG, altering the game state
local function getSeed(id, variant)
if variant == nil or variant == 100 then return currentPlayer:GetCollectibleRNG(id):GetSeed()
elseif variant == 350 then return currentPlayer:GetTrinketRNG(id):GetSeed()
elseif variant == 300 then return currentPlayer:GetCardRNG(id):GetSeed()
elseif variant == 70 then return currentPlayer:GetPillRNG(id):GetSeed() end
end
-- Helper function to easily add an item's stock description to the desc
-- Don't use if you need to customize the desc! (Like Sanguine Bond's result highlighting)
-- extraIcon is for when we want a special icon before the line (like {{Dice Bag Icon}} {{Dice Item Icon}})
@@ -30,166 +34,14 @@ local function addObjectDesc(type, variant, subtype, extraIcon)
end
end
-- Banned items: Metronome, Plan C, Glowing Hour Glass, Breath of Life, Clicker, R Key
-- Reduced chance: Death Certificate (15%), Genesis (25%)
-- I saw something in the code about rerolling Forget Me Now but not sure what the criteria was
local metronomeBlacklist = {[488] = 1, [475] = 1, [422] = 1} --AB+ can choose Clicker lol rip
if REPENTANCE then metronomeBlacklist = {[488] = 1, [475] = 1, [422] = 1, [326] = 1, [482] = 1, [636] = 1, [628] = 0.85, [622] = 0.75 } end
-- Should we account for Car Battery? (Both if we have it, and if it grants it which immediately uses it again)
function EID:MetronomePrediction()
local itemConfig = Isaac.GetItemConfig()
local numCollectibles = EID:GetMaxCollectibleID()
local metronomeRNG = currentPlayer:GetCollectibleRNG(CollectibleType.COLLECTIBLE_METRONOME):GetSeed()
local rerollChance = 0
if REPENTANCE then
metronomeRNG = EID:RNGNext(metronomeRNG)
rerollChance = metronomeRNG
end
local attempts = 15
while attempts > 0 do
attempts = attempts - 1
metronomeRNG = EID:RNGNext(metronomeRNG)
local sel = metronomeRNG % numCollectibles + 1
if itemConfig:GetCollectible(sel) ~= nil then
if metronomeBlacklist[sel] then
-- A few items have a reroll chance in Repentance
if metronomeBlacklist[sel] < 1 then
rerollChance = EID:RNGNext(rerollChance, 0x02, 0x0F, 0x11)
local rerollFloat = EID:SeedToFloat(rerollChance)
if rerollFloat < 1 - metronomeBlacklist[sel] then
-- We succeeded the reroll chance
return "{{Collectible" .. sel .. "}} {{ColorGray}}" .. EID:getObjectName(5,100,sel) .. "#" .. EID:getDescriptionObj(5,100,sel).Description
end
end
else
return "{{Collectible" .. sel .. "}} {{ColorGray}}" .. EID:getObjectName(5,100,sel) .. "#" .. EID:getDescriptionObj(5,100,sel).Description
end
end
end
-- as the code puts it, [warn] No Collectible found for Metronome!
return "None"
end
-- Teleport! Destination Prediction --
local function teleport1Prediction()
if REPENTANCE and currentPlayer:GetSprite():GetAnimation() == "TeleportUp" then return end
local level = game:GetLevel()
local currentRoomIndex = level:GetCurrentRoomDesc().SafeGridIndex
local possibleRooms = {}
for i = 0, 168 do
local room = level:GetRoomByIdx(i)
-- Currently, any non-red room on the grid that we aren't in is a valid choice
-- Is there more criteria to look out for?
if room.GridIndex >= 0 and room.SafeGridIndex ~= currentRoomIndex and room.Data.Type ~= 29 and (not REPENTANCE or room.Flags & 1024 ~= 1024) then
table.insert(possibleRooms, i)
end
end
local teleSeed = currentPlayer:GetCollectibleRNG(CollectibleType.COLLECTIBLE_TELEPORT):GetSeed()
teleSeed = EID:RNGNext(teleSeed, 5, 9, 7)
teleSeed = EID:RNGNext(teleSeed, 0x02, 0x0F, 0x19) -- magic disassembled numbers!
local resultRoomIndex = possibleRooms[(teleSeed % #possibleRooms) + 1]
local resultRoom = level:GetRoomByIdx(resultRoomIndex)
local resultSafeIndex = resultRoom.SafeGridIndex
local roomIcon = EID.RoomTypeToMarkup[resultRoom.Data.Type]
if resultRoom.Data.Type == 1 then roomIcon = EID.RoomShapeToMarkup[resultRoom.Data.Shape] end
local roomNames = EID:getDescriptionEntry("RoomTypeNames")
local roomName = roomNames[resultRoom.Data.Type]
-- Find our X/Y difference from the target location, if we're on the map
local d = ""
if currentRoomIndex >= 0 then
local myPos = Vector(currentRoomIndex % 13, math.floor(currentRoomIndex / 13))
local newPos = Vector(resultSafeIndex % 13, math.floor(resultSafeIndex / 13))
local diffX = math.floor(newPos.X - myPos.X)
local diffY = math.floor(newPos.Y - myPos.Y)
if diffY > 0 then d = d .. "{{ArrowGrayDown}} " .. diffY
elseif diffY < 0 then d = d .. "{{ArrowGrayUp}} " .. math.abs(diffY) end
if d ~= "" and diffX ~= 0 then d = d .. "," end
if diffX > 0 then d = d .. "{{ArrowGrayRight}} " .. diffX
elseif diffX < 0 then d = d .. "{{ArrowGrayLeft}} " .. math.abs(diffX) end
end
if d ~= "" then d = "#{{Blank}} " .. d end
append("{{Collectible44}}", EID:getObjectName(5, 100, 44) .. EID:getDescriptionEntry("HoldMapHeader"), roomIcon .. " " .. roomName .. d)
end
-- Teleport 2 Destination Prediction --
local teleport2Order = { 1,5,8,2,4,13,21,12,10,6,11,18,19,9,20,24,7,1025,666,3 }
local teleport2GreedOrder = { 1,5,2,4,10,23,8,666,3 }
local teleport2Icons = { [1025] = "{{RedRoom}}", [666] = "{{AngelDevilChance}}" }
local function teleport2Prediction()
local level = game:GetLevel()
local rooms = level:GetRooms()
--I AM ERROR Room always considered uncleared
local unclearedTypes = {[3] = true}
for i = 0, rooms.Size - 1 do
local room = rooms:Get(i)
if not room.Clear then
-- Check for Special Red Rooms, which get ordered differently than their non-red version
if REPENTANCE and room.Data.Type ~= 1 and room.Flags & 1024 == 1024 then unclearedTypes[1025] = true
else unclearedTypes[room.Data.Type] = true end
end
end
--Angel/Devil Room check (it lives off the map)
if not level:GetRoomByIdx(-1).Clear then unclearedTypes[666] = true end
local greed = game:IsGreedMode()
local roomOrder = (greed and teleport2GreedOrder) or teleport2Order
local roomNames = EID:getDescriptionEntry("RoomTypeNames")
for i,v in ipairs(roomOrder) do
if unclearedTypes[v] then
local descString = (teleport2Icons[v] or EID.RoomTypeToMarkup[v]) .. " " .. roomNames[v]
-- Tall Vertical Main Greed Room exception, because why not, attention to detail
if v == 1 and greed then descString = "{{RoomLongVertical}} " .. roomNames[v] end
append("{{Collectible419}}", EID:getObjectName(5, 100, 419) .. EID:getDescriptionEntry("HoldMapHeader"), descString)
return
end
end
end
-- Sanguine Bond Chance Display / Prediction --
--order of checking: 15% Pennies, 48% Damage, 58% Hearts, 63% Item, 65% Leviathan, 100% Nothing
local sanguineResults = { { 0.15, 3 }, { 0.48, 2 }, { 0.58, 4 }, { 0.63, 5 }, { 0.65, 6 }, { 1, 1 } }
-- this isn't even used in this file anymore, it's in main, but there isn't really a better place to put it yet
function EID:trimSanguineDesc(descObj)
local currentRoom = game:GetLevel():GetCurrentRoom()
local spikes = currentRoom:GetGridEntity(67)
if not spikes then return "" end -- don't display anything if we can't find the spikes!
local cheatResult = nil
if spikes and EID.Config["PredictionSanguineBond"] then
local spikeSeed = currentRoom:GetGridEntity(67):GetRNG():GetSeed()
spikeSeed = EID:RNGNext(spikeSeed, 5, 9, 7)
spikeSeed = EID:RNGNext(spikeSeed, 0x01, 0x05, 0x13) -- magic disassembled numbers!
local nextFloat = EID:SeedToFloat(spikeSeed)
for _,v in ipairs(sanguineResults) do
if nextFloat < v[1] then cheatResult = v[2] break end
end
end
local resultsDesc = ""
local lineCount = 0
-- separate sanguine's description by # or semicolons
for w in string.gmatch(descObj.Description, "([^#;]+)") do
-- we only care about groups that contain a percent sign
if string.find(w,"%%") then
lineCount = lineCount + 1
if cheatResult == lineCount then resultsDesc = resultsDesc .. "{{ColorBagComplete}}" end
resultsDesc = resultsDesc .. w .. "#"
end
end
return resultsDesc
end
-- Data Tables --
-- Rainbow Worm's trinket IDs it grants, in order
local rainbowWormEffects = { [0] = 9, 11, 65, 27, 10, 12, 26, 66, 96, 144 }
-- Mysterious Paper does not play well with displaying Error 404's effect
local mysteriousPaperBlacklist = { [23] = true, [48] = true }
function EID:getHoldMapDescription(player, checkingTwin)
EID.InsideItemReminder = true
-- Starting Blacklist: Recall, Hold
blacklist = { ["5.100.714"] = true, ["5.100.715"] = true, }
holdMapDesc = ""
@@ -214,24 +66,6 @@ function EID:getHoldMapDescription(player, checkingTwin)
end
end
-- Metronome result
if player:HasCollectible(CollectibleType.COLLECTIBLE_METRONOME) and EID.Config["ItemReminderShowRNGCheats"] then
blacklist["5.100.488"] = true
append("{{Collectible488}}", EID:getObjectName(5,100,488) .. EID:getDescriptionEntry("HoldMapHeader"), EID:MetronomePrediction())
end
-- Teleport! location
if player:HasCollectible(CollectibleType.COLLECTIBLE_TELEPORT) and EID.Config["ItemReminderShowRNGCheats"] then
blacklist["5.100.44"] = true
teleport1Prediction()
end
-- Teleport 2.0 location
if player:HasCollectible(CollectibleType.COLLECTIBLE_TELEPORT_2) and not EID.isMirrorRoom then
blacklist["5.100.419"] = true
teleport2Prediction()
end
-- Recently Acquired Item Descriptions
if EID.Config["ItemReminderShowRecentItem"] > 0 then
local printedItems = 0
@@ -249,9 +83,30 @@ function EID:getHoldMapDescription(player, checkingTwin)
-- Active Item Descriptions
if EID.Config["ItemReminderShowActiveDesc"] > 0 then
for i = 0, EID.Config["ItemReminderShowActiveDesc"]-1 do
-- the modulo is to convert negative IDs (glitched items) to positive IDs
local heldActive = player:GetActiveItem(i) % 4294967296
if heldActive > 0 then
addObjectDesc(5, 100, heldActive)
if heldActive > 0 and not blacklist["5.100." .. heldActive] then
-- Metronome result
if heldActive == 488 and EID.Config["ItemReminderShowRNGCheats"] then
blacklist["5.100.488"] = true
addObjectDesc(5, 100, EID:MetronomePrediction(getSeed(488)), "{{Collectible488}}")
-- Teleport! location
elseif heldActive == 44 and EID.Config["ItemReminderShowRNGCheats"] then
blacklist["5.100.44"] = true
-- The result preview changes as soon as we activate Teleport, which looks awkward, so try to not display the result while mid-teleport. Doesn't work perfectly and only in Rep
if not REPENTANCE or currentPlayer:GetSprite():GetAnimation() ~= "TeleportUp" then
append("{{Collectible44}}", EID:getObjectName(5,100,44) .. EID:getDescriptionEntry("HoldMapHeader"), EID:Teleport1Prediction(getSeed(44)))
end
-- Teleport 2.0 location
elseif heldActive == 419 and not EID.isMirrorRoom then
blacklist["5.100.419"] = true
append("{{Collectible419}}", EID:getObjectName(5,100,419) .. EID:getDescriptionEntry("HoldMapHeader"), EID:Teleport2Prediction())
elseif heldActive == 489 then
blacklist["5.100.489"] = true
addObjectDesc(5, 100, EID:CurrentDInfinity(getSeed(489), currentPlayer), "{{Collectible489}}")
else
addObjectDesc(5, 100, heldActive)
end
end
end
end
@@ -344,5 +199,6 @@ function EID:getHoldMapDescription(player, checkingTwin)
end
end
EID.InsideItemReminder = false
return holdMapDesc
end
+270
View File
@@ -0,0 +1,270 @@
-- This file is for various functions that are able to calculate the result effect that an item will have
local game = Game()
-- reimplementation of RNG:Next()
-- Default RNG shift values for many things are 5, 9, 7
function EID:RNGNext(rngNum, shift1, shift2, shift3)
rngNum = rngNum ~ ((rngNum >> (shift1 or 5)) & 4294967295)
rngNum = rngNum ~ ((rngNum << (shift2 or 9)) & 4294967295)
rngNum = rngNum ~ ((rngNum >> (shift3 or 7)) & 4294967295)
return rngNum >> 0;
end
-- Convert a seed int into a float between 0 and 1
function EID:SeedToFloat(seed)
local multi = 2.3283061589829401E-10;
return seed * multi;
end
-- D Infinity --
local dinfinityList = { [0] = 105, 166, 284, 283, 285, 406, 386 }
-- Repentance's D Infinity order: D1, D4, D6, Eternal D6, D7, D8, D10, D12, D20, D100
if REPENTANCE then dinfinityList = { [0] = 476, 284, 105, 609, 437, 406, 285, 386, 166, 283 } end
-- Returns the item ID of D Infinity's current die
-- For Repentance, the Drop button is tracked, which is probably not 100% successful, and handled elsewhere
function EID:CurrentDInfinity(rng, player)
if not REPENTANCE then
rng = EID:RNGNext(rng, 0x1, 0x9, 0x1D) -- magic disassembled numbers!
return dinfinityList[rng % 7]
else
local playerID = EID:getPlayerID(player)
return dinfinityList[EID.DInfinityState[playerID]] or 476
end
end
local justDidD = false
if REPENTANCE then
-- To help with Rep tracking, watch for D Infinity to get used and resync its current dice
function EID:WatchForDInfinity()
justDidD = true
end
EID:AddCallback(ModCallbacks.MC_PRE_USE_ITEM, EID.WatchForDInfinity, CollectibleType.COLLECTIBLE_D_INFINITY)
function EID:WatchForDice(collectibleType, rng, player)
if not justDidD or collectibleType == 489 then return end
justDidD = false
local playerID = EID:getPlayerID(player)
for i,v in ipairs(dinfinityList) do
if v == collectibleType then EID.DInfinityState[playerID] = i end
end
end
EID:AddCallback(ModCallbacks.MC_PRE_USE_ITEM, EID.WatchForDice)
end
-- Metronome --
-- Banned items: Metronome, Plan C, Glowing Hour Glass, Breath of Life, Clicker, R Key
-- Reduced chance: Death Certificate (15%), Genesis (25%)
-- I saw something in the code about rerolling Forget Me Now but not sure what the criteria was
local metronomeBlacklist = {[488] = 1, [475] = 1, [422] = 1} --AB+ can choose Clicker lol rip
if REPENTANCE then metronomeBlacklist = {[488] = 1, [475] = 1, [422] = 1, [326] = 1, [482] = 1, [636] = 1, [628] = 0.85, [622] = 0.75 } end
-- TODO: Account for Car Battery
-- (If we have it, display multiple results? Also, if the result is Car Battery, it's immediately triggered again)
function EID:MetronomePrediction(rng)
local numCollectibles = EID:GetMaxCollectibleID()
local rerollChance = 0
if REPENTANCE then --Rep advances the RNG an extra time to use for its Death Cert/Genesis reroll
rng = EID:RNGNext(rng)
rerollChance = rng
end
local attempts = 15
while attempts > 0 do
attempts = attempts - 1
rng = EID:RNGNext(rng)
local sel = rng % numCollectibles + 1
if EID.itemConfig:GetCollectible(sel) ~= nil then
if metronomeBlacklist[sel] then
-- A few items have a reroll chance in Repentance
if metronomeBlacklist[sel] < 1 then
rerollChance = EID:RNGNext(rerollChance, 0x02, 0x0F, 0x11)
local rerollFloat = EID:SeedToFloat(rerollChance)
if rerollFloat < 1 - metronomeBlacklist[sel] then
-- We succeeded the reroll chance
return sel
end
end
else
return sel
end
end
end
-- as the code puts it, [warn] No Collectible found for Metronome!
return 488
end
-- Sanguine Bond --
--order of checking: 15% Pennies, 48% Damage, 58% Hearts, 63% Item, 65% Leviathan, 100% Nothing
local sanguineResults = { { 0.15, 3 }, { 0.48, 2 }, { 0.58, 4 }, { 0.63, 5 }, { 0.65, 6 }, { 1, 1 } }
-- This function both trims the desc to just a list of chances, and highlights the next result if allowed, since those two actions are very intertwined
function EID:trimSanguineDesc(descObj)
local currentRoom = game:GetLevel():GetCurrentRoom()
local spikes = currentRoom:GetGridEntity(67)
if not spikes then return "" end -- don't display anything if we can't find the spikes!
local cheatResult = nil
if spikes and EID.Config["PredictionSanguineBond"] then
local spikeSeed = currentRoom:GetGridEntity(67):GetRNG():GetSeed()
spikeSeed = EID:RNGNext(spikeSeed, 5, 9, 7)
spikeSeed = EID:RNGNext(spikeSeed, 0x01, 0x05, 0x13) -- magic disassembled numbers!
local nextFloat = EID:SeedToFloat(spikeSeed)
for _,v in ipairs(sanguineResults) do
if nextFloat < v[1] then cheatResult = v[2] break end
end
end
local resultsDesc = ""
local lineCount = 0
-- separate sanguine's description by # or semicolons
for w in string.gmatch(descObj.Description, "([^#;]+)") do
-- we only care about groups that contain a percent sign
if string.find(w,"%%") then
lineCount = lineCount + 1
if cheatResult == lineCount then resultsDesc = resultsDesc .. "{{ColorBagComplete}}" end
resultsDesc = resultsDesc .. w .. "#"
end
end
return resultsDesc
end
-- Teleport! Destination Prediction --
-- Returns text like "{{RoomIcon}} Room Type Name#Up 3, Left 2"
function EID:Teleport1Prediction(rng)
local level = game:GetLevel()
local currentRoomIndex = level:GetCurrentRoomDesc().SafeGridIndex
local possibleRooms = {}
-- Search the 13x13 grid for valid teleport locations
-- Currently, that's any room on the grid besides the one we're in, and Red Rooms or Ultra Secret Rooms
for i = 0, 168 do
local room = level:GetRoomByIdx(i)
if room.GridIndex >= 0 and room.SafeGridIndex ~= currentRoomIndex and room.Data.Type ~= 29 and (not REPENTANCE or room.Flags & 1024 ~= 1024) then
table.insert(possibleRooms, i)
end
end
rng = EID:RNGNext(rng, 5, 9, 7)
rng = EID:RNGNext(rng, 0x02, 0x0F, 0x19) -- magic disassembled numbers!
local resultRoomIndex = possibleRooms[(rng % #possibleRooms) + 1]
local resultRoom = level:GetRoomByIdx(resultRoomIndex)
local resultSafeIndex = resultRoom.SafeGridIndex
local roomIcon = EID.RoomTypeToMarkup[resultRoom.Data.Type]
if resultRoom.Data.Type == 1 then roomIcon = EID.RoomShapeToMarkup[resultRoom.Data.Shape] end
local roomNames = EID:getDescriptionEntry("RoomTypeNames")
local roomName = roomNames[resultRoom.Data.Type]
-- Find our X/Y difference from the target location, if we're on the map
local d = ""
if currentRoomIndex >= 0 then
local myPos = Vector(currentRoomIndex % 13, math.floor(currentRoomIndex / 13))
local newPos = Vector(resultSafeIndex % 13, math.floor(resultSafeIndex / 13))
local diffX = math.floor(newPos.X - myPos.X)
local diffY = math.floor(newPos.Y - myPos.Y)
if diffY > 0 then d = d .. "{{ArrowGrayDown}} " .. diffY
elseif diffY < 0 then d = d .. "{{ArrowGrayUp}} " .. math.abs(diffY) end
if d ~= "" and diffX ~= 0 then d = d .. "," end
if diffX > 0 then d = d .. "{{ArrowGrayRight}} " .. diffX
elseif diffX < 0 then d = d .. "{{ArrowGrayLeft}} " .. math.abs(diffX) end
end
if d ~= "" then d = "#{{Blank}} " .. d end
return roomIcon .. " " .. roomName .. d
end
-- Teleport 2 Destination Prediction --
local teleport2Order = { 1,5,8,2,4,13,21,12,10,6,11,18,19,9,20,24,7,1025,666,3 }
local teleport2GreedOrder = { 1,5,2,4,10,23,8,666,3 }
local teleport2Icons = { [1025] = "{{RedRoom}}", [666] = "{{AngelDevilChance}}" }
function EID:Teleport2Prediction()
local level = game:GetLevel()
local rooms = level:GetRooms()
--I AM ERROR Room always considered uncleared
local unclearedTypes = {[3] = true}
for i = 0, rooms.Size - 1 do
local room = rooms:Get(i)
if not room.Clear then
-- Check for Special Red Rooms, which get ordered differently than their non-red version
if REPENTANCE and room.Data.Type ~= 1 and room.Flags & 1024 == 1024 then unclearedTypes[1025] = true
else unclearedTypes[room.Data.Type] = true end
end
end
--Angel/Devil Room check (it lives off the map)
if not level:GetRoomByIdx(-1).Clear then unclearedTypes[666] = true end
local greed = game:IsGreedMode()
local roomOrder = (greed and teleport2GreedOrder) or teleport2Order
local roomNames = EID:getDescriptionEntry("RoomTypeNames")
-- Return a string for the first uncleared room type that we find
for i,v in ipairs(roomOrder) do
if unclearedTypes[v] then
local descString = (teleport2Icons[v] or EID.RoomTypeToMarkup[v]) .. " " .. roomNames[v]
-- Tall Vertical Main Greed Room exception, because why not, attention to detail
if v == 1 and greed then descString = "{{RoomLongVertical}} " .. roomNames[v] end
return descString
end
end
end
-- Void Stat Increases --
local numVoidable = 0
local numRunable = 0
local function GetTwoIncreases(rng, tbl)
local statTable = {1,2,3,4,5,6}
-- perform 5 random swaps of our stat table
for i = 6, 2, -1 do
rng = EID:RNGNext(rng, 5, 9, 7)
local result = (rng % i) + 1
local temp = statTable[i]
statTable[i] = statTable[result]
statTable[result] = temp
end
-- the first two entries in the stat table get increased
tbl[statTable[1]] = tbl[statTable[1]] + 1
tbl[statTable[2]] = tbl[statTable[2]] + 1
return rng
end
-- Count the number of absorbable pedestals in the room
-- Returns a table of active items that will be absorbed
function EID:VoidRoomCheck()
numVoidable = 0
numRunable = 0
EID.VoidOptionIndexes = {}
local activesAbsorbed = {}
for _, entity in ipairs(Isaac.FindByType(5, 100, -1, true, false)) do
local pickup = entity:ToPickup()
-- Count this pedestal if it's not an active (or this is Black Rune), not a shop item, and (in Repentance) the first of its option index
-- TEST IF VOID ALWAYS ABSORBS THE FIRST OF ITS OPTION INDEX
if entity.SubType > 0 and not pickup:IsShopItem() and
(not REPENTANCE or pickup.OptionsPickupIndex == 0 or EID.VoidOptionIndexes[pickup.OptionsPickupIndex] == nil) then
numRunable = numRunable + 1
if REPENTANCE then EID.VoidOptionIndexes[pickup.OptionsPickupIndex] = entity.SubType end
if (EID.itemConfig:GetCollectible(entity.SubType).Type ~= ItemType.ITEM_ACTIVE) then
numVoidable = numVoidable + 1
else
table.insert(activesAbsorbed, entity.SubType)
end
end
end
return activesAbsorbed
end
-- Determine what stats will be increased after 1 absorption, the whole room's absorption, and whole room + a purchased item above your head
function EID:VoidRNGCheck(player, isRune)
local increases = {0, 0, 0, 0, 0, 0}
local startRNG = (isRune and player:GetCardRNG(Card.RUNE_BLACK):GetSeed()) or player:GetCollectibleRNG(CollectibleType.COLLECTIBLE_VOID):GetSeed()
local count = (isRune and numRunable) or numVoidable
local eidTable = (isRune and EID.BlackRuneStatIncreases) or EID.VoidStatIncreases
-- in Repentance, an additional RNG call is done before the 5 for stat ups when using Void
if REPENTANCE and not isRune then startRNG = EID:RNGNext(startRNG, 5, 9, 7) end
for i = 1, count do
startRNG = GetTwoIncreases(startRNG, increases)
if i == 1 then eidTable[3] = {table.unpack(increases)} end
end
eidTable[1] = {table.unpack(increases)}
-- do an extra check for what you'd get if you Void with a shop item above your head
GetTwoIncreases(startRNG, increases)
eidTable[2] = {table.unpack(increases)}
-- if there were no absorbable pedestals, the "single increase" stats are the same as the "one extra" stats
if count == 0 then eidTable[3] = {table.unpack(increases)} end
end
+3 -3
View File
@@ -5,8 +5,6 @@ EID.TabPreviewID = 0
EID.inModifierPreview = false
-- The "Hold Map Helper" needs to know if it shouldn't display because we're in a Hold Tab desc
EID.TabDescThisFrame = false
-- ...and likewise we need to know when we're in the Item Reminder (this is getting out of hand)
EID.ReminderThisFrame = false
-- 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, }
@@ -87,6 +85,7 @@ EID.BlackRuneStatIncreases = {{},{},{}}
EID.VoidOptionIndexes = {}
local function VoidCallback(descObj, isRune)
if EID.InsideItemReminder then return descObj end
-- Recheck RNG periodically (picking up passive collectibles will change the Void results without any easy trigger to track)
-- Do both Void and Rune here since they could both be requested in the same frame
if EID.GameUpdateCount >= lastVoidCheck + 30 or EID.RecheckVoid then
@@ -281,6 +280,7 @@ if REPENTANCE then
-- Handle Spindown Dice description addition
local function SpindownDiceCallback(descObj)
if EID.InsideItemReminder 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}} :")
@@ -322,7 +322,7 @@ if REPENTANCE then
if hasCarBattery then
EID:appendToDescription(descObj, " (Results with {{Collectible356}})")
end
if not EID.ReminderThisFrame and firstID ~= 0 and EID.TabPreviewID == 0 then
if firstID ~= 0 and EID.TabPreviewID == 0 then
EID.TabPreviewID = firstID
EID:appendToDescription(descObj, "#{{Blank}} ".. EID:getDescriptionEntry("FlipItemToggleInfo"))
end
+34 -5
View File
@@ -44,6 +44,8 @@ EID.ForceRefreshCache = false -- set to true to force-refresh descriptions, curr
local preHourglassStatus = {}
EID.holdTabPlayer = 0
EID.holdTabCounter = 0
EID.DInfinityState = {}
local forgottenDropTimer = 0
EID.GameUpdateCount = 0
EID.GameRenderCount = 0
@@ -107,6 +109,7 @@ require("eid_xmldata")
require("eid_api")
require("eid_modifiers")
require("eid_holdmapdesc")
require("eid_itemprediction")
-- load Repentence descriptions
if REPENTANCE then
@@ -1213,7 +1216,6 @@ local function onRender(t)
end
EID.TabPreviewID = 0
EID.TabDescThisFrame = false
EID.ReminderThisFrame = false
-- If MCM is open, don't show anything unless we're in a tab labeled as "Visuals" or "Crafting"
if ModConfigMenu and ModConfigMenu.IsVisible and ModConfigMenu.Config["Mod Config Menu"].HideHudInMenu and EID.MCMCompat_isDisplayingEIDTab ~= "Visuals" and EID.MCMCompat_isDisplayingEIDTab ~= "Crafting" then
@@ -1255,14 +1257,40 @@ local function onRender(t)
return
end
local tabHeld, playerHoldingTab = EID:PlayersActionPressed(EID.Config["BagOfCraftingToggleKey"])
-- Check for Tab (or user setting) being held, for the Item Reminder
local tabHeld, playerNumHoldingTab = EID:PlayersActionPressed(EID.Config["BagOfCraftingToggleKey"])
if tabHeld then
EID.holdTabPlayer = EID.coopMainPlayers[playerHoldingTab]
EID.holdTabPlayer = EID.coopMainPlayers[playerNumHoldingTab]
EID.holdTabCounter = EID.holdTabCounter + 1
else
EID.holdTabPlayer = nil
EID.holdTabCounter = 0
end
-- Check for Drop being pressed, for Repentance D Infinity tracking
if REPENTANCE then
local dropTriggered, playerNumPressingDrop = EID:PlayersActionPressed(ButtonAction.ACTION_DROP, Input.IsActionTriggered)
if dropTriggered then
local playerPressingDrop = EID.coopMainPlayers[playerNumPressingDrop]
local playerID = EID:getPlayerID(playerPressingDrop)
local playerType = playerPressingDrop:GetPlayerType()
-- Forgotten's drop requires a double tap to actually count for this
if (playerType == 16 or playerType == 17) then
-- technically we should track a forgotten drop timer for each player but forget it
if EID.GameUpdateCount > forgottenDropTimer then
-- the actual interval here seems to be 6.5. 7 has false positives, 6 has false negatives
forgottenDropTimer = EID.GameUpdateCount + 7
dropTriggered = false
else
forgottenDropTimer = 0
end
end
if dropTriggered and playerPressingDrop:GetActiveItem(0) == 489 then
EID.DInfinityState[playerID] = EID.DInfinityState[playerID] or 0
EID.DInfinityState[playerID] = (EID.DInfinityState[playerID] + 1) % 10
end
end
end
if EID.ForceRefreshCache then
resetDescCache()
@@ -1516,9 +1544,9 @@ local function onRender(t)
-- handle showing the Hold Map Helper description
if EID.Config["ItemReminderEnabled"] and EID.holdTabCounter >= 30 and EID.TabDescThisFrame == false and EID.holdTabPlayer ~= nil then
EID.ReminderThisFrame = true
local demoDescObj = EID:getDescriptionObj(-999, -1, 1)
demoDescObj.Name = ""
demoDescObj.Name = EID:getDescriptionEntry("HoldMapTitle")
-- check for scrolling being enabled here and append scroll controls to the title?
demoDescObj.Description = EID:getHoldMapDescription(EID.holdTabPlayer)
if (demoDescObj.Description ~= "") then EID:addDescriptionToPrint(demoDescObj, 1) end
end
@@ -1564,6 +1592,7 @@ local function OnGameStartGeneral(_,isSave)
EID:buildTransformationTables()
if not isSave then
EID.PlayerItemInteractions = {}
EID.DInfinityState = {}
end
end
EID:AddCallback(ModCallbacks.MC_POST_GAME_STARTED, OnGameStartGeneral)