Files
External-Item-Descriptions/eid_tmtrainer.lua
T
2022-01-12 04:44:32 -05:00

247 lines
8.9 KiB
Lua

--[[
This file is only loaded when the game is launched with the "--luadebug" launch option.
This allows mods to have access to your computer's files, which is necessary to read glitched item info that's printed to the game's log file.
Having --luadebug on would allow malicious mods to delete your files, execute command prompt commands, and probably much more.
USE AT YOUR OWN RISK.
]]
local game = Game()
-- glitched items start at 4294967295
local maxNumber = 4294967296
local spawnedItems = 0
local logLocation = os.getenv("SYSTEMDRIVE") .. "/Users/" .. os.getenv("USERNAME") .. "/Documents/My Games/Binding of Isaac Repentance/log.txt"
local logFound = false
local logCursor = 0
local debugLogLocation = os.getenv("SYSTEMDRIVE") .. "/Users/" .. os.getenv("USERNAME") .. "/Documents/My Games/Binding of Isaac Repentance/tmtrainerlog.txt"
local debugLog = io.open(debugLogLocation, "w+")
-- all possible attributes
--local itemConfigItemAttributes = { "AchievementID", "AddBlackHearts", "AddBombs", "AddCoins", "AddHearts", "AddKeys", "AddMaxHearts", "AddSoulHearts", "CacheFlags", "ChargeType", "ClearEffectsOnRemove", "Costume", "Description", "DevilPrice", "Discharged", "GfxFileName", "Hidden", "ID", "InitCharge", "MaxCharges", "MaxCooldown", "Name", "PassiveCache", "PersistentEffect", "Quality", "ShopPrice", "Special", "Tags", "Type" }
-- the traits here will be listed in this order
local itemConfigItemAttributes = { "AddMaxHearts", "AddHearts", "AddSoulHearts", "AddBlackHearts", "AddBombs", "AddCoins", "AddKeys", "CacheFlags" }
local cacheFlagNames = { [0] = "Damage", "Fire Delay", "Shot Speed", "Range", "Speed", "Tear Effects", "Tear Color", "Flying", "Weapon", "Familiars", "Luck", "Size", "Color", "Pickup Vision", [16] = "All Stats" }
-- cache flags: math.pow(2, i) | flags ~= 0
--currently english only
local attributeConversions = {
--chain = "Then: ",
chain = "",
active = "On use:",
pickup_collected = "When you collect a pickup:", --chance to?
enemy_kill = "On kill, chance to:",
damage_taken = "When you take damage:", --chance to?
entity_spawned = "When a {{ColorGray}}{T1}{{ColorText}} is spawned, chance to:",
tear_fire = "When you fire a tear, chance to:",
enemy_hit = "On hitting an enemy, chance to:",
room_clear = "On room clear:", --chance to?
area_damage = "Deal {1} damage in an area around you",
add_temporary_effect = "Gain {1} for the room",
convert_entities = "Convert all {{ColorGray}}{1}{{ColorText}} in the room to {{ColorGray}}{2}{{ColorText}}",
use_active_item = "Use {1}",
spawn_entity = "Spawn a {{ColorGray}}{1}{{ColorText}}",
fart = "Fart with size {1}",
AddBlackHearts = "{1} Black Heart",
AddBombs = "{1} Bomb",
AddCoins = "{1} Coin",
AddHearts = "Heals {1} Red Heart",
AddKeys = "{1} Key",
AddMaxHearts = "{1} Heart Container",
AddSoulHearts = "{1} Soul Heart",
}
--999.-1?
--1000.50: Laser Impact? It couldn't find that?
local genericEntityNames = {
["2.-1"] = "tear",
["4.-1"] = "lit Bomb",
["5.-1"] = "pickup",
["5.10"] = "Heart",
["5.20"] = "Coin",
["5.30"] = "Key",
["5.40"] = "Bomb pickup",
["5.69"] = "Grab Bag",
["5.70"] = "Pill",
["5.300"] = "Card",
["9.-1"] = "projectile",
["999.-1"] = "grid-aligned object",
["1000.0"] = "effect",
}
local lastEffectTrigger = "chain"
local function entityToName(e, plural)
-- -1 is often used as an "any of this type", so converting it to 0 can help
local eWithZero = string.gsub(e, "-1", "0")
plural = plural or false
local name = genericEntityNames[e] or EID.XMLEntityNames[e] or genericEntityNames[eWithZero] or EID.XMLEntityNames[eWithZero] or "NO NAME"
if name == "NO NAME" then
debugLog:write(e .. "\n")
debugLog:flush()
print(e)
elseif plural then
name = name .. "s"
end
if name == "NO NAME" then return e end
return name
end
local function parseEffectLine(raw)
local words = {}
for w in string.gmatch(raw,"%S+") do table.insert(words, w) end
local triggerReplacements = {}
local replacements = {}
local printEffect = true
-- words[1] = "[INFO]", words[2] = "-"
local effectTrigger = words[3]
if (string.find(effectTrigger, "entity_spawned")) then
effectTrigger = "entity_spawned"
triggerReplacements[1] = entityToName(string.sub(words[3], 16, -2))
end
-- words[4] = "->"
local effectType = words[5]
local fullEffect = ""
for i=5,#words do fullEffect = fullEffect .. words[i] .. " " end
local extraText = ""
-- switch statement for how to handle the words after the effect type
if effectType == "add_temporary_effect" or effectType == "use_active_item" then
-- remainder of line = collectible name that's granted for the room
local name = ""
for i=6,#words do
name = name .. words[i]
if (i ~= #words) then name = name .. " " end
end
replacements[1] = "{{Collectible" .. Isaac.GetItemIdByName(name) .. "}}" .. name
elseif effectType == "convert_entities" then
replacements[1] = entityToName(words[6], true)
replacements[2] = entityToName(words[8])
elseif effectType == "spawn_entity" then
replacements[1] = entityToName(words[6])
elseif effectType == "fart" then
replacements[1] = words[6]
elseif effectType == "area_damage" then
--It also has flags in words[8] but they don't seem very useful, usually all zeroes
replacements[1] = words[6]
if words[6] == "0" then printEffect = false end
end
local eidString = ""
if effectTrigger ~= lastEffectTrigger then eidString = eidString .. (attributeConversions[effectTrigger] or effectTrigger) .. "#" end
if printEffect then eidString = eidString .. (attributeConversions[effectType] or fullEffect) .. extraText .. "#" end
for k,v in ipairs(triggerReplacements) do eidString = string.gsub(eidString, "{T" .. k .. "}", v) end
for k,v in ipairs(replacements) do eidString = string.gsub(eidString, "{" .. k .. "}", v) end
if effectTrigger ~= "chain" then lastEffectTrigger = effectTrigger end
return eidString
end
local function CheckLogForItems(_)
if (not logFound or game:GetFrameCount() % 5 ~= 0 or not EID:PlayersHaveCollectible(CollectibleType.COLLECTIBLE_TMTRAINER)) then return end
local numEffects = 0
local itemScore = 0
local eidDesc = ""
local theLog = io.open(logLocation, "r")
theLog:seek("set", logCursor)
local line = theLog:read()
while line ~= nil do
if string.find(line, "initialized with") then
local count = 0
spawnedItems = spawnedItems + 1
lastEffectTrigger = "chain"
eidDesc = ""
-- find the number of effects to watch for
for s in string.gmatch(line, "%d*%.?%d+") do
numEffects = tonumber(s)
break
end
elseif numEffects > 0 then
numEffects = numEffects - 1
eidDesc = eidDesc .. parseEffectLine(line)
if numEffects == 0 then
-- Glowing Hour Glass type effects will cause the game to reload all items, check if our desc is equal to the first one
if (EID:getDescriptionObj(5, 100, maxNumber - 1).Description == eidDesc) then spawnedItems = 1 end
-- check for the item's ItemConfig attributes
local config = Isaac.GetItemConfig()
local item = config:GetCollectible(maxNumber - spawnedItems)
print(item)
debugLog:write((maxNumber - spawnedItems) .. "\n")
local attributes = ""
for k,v in ipairs(itemConfigItemAttributes) do
local attrib = item[v]
if attrib ~= 0 then
if (v == "CacheFlags") then
local flagString = "Might affect "
if attrib == CacheFlag.CACHE_ALL then
flagString = flagString .. cacheFlagNames[16]
else
for i=0, 13 do
if 2^i & attrib ~= 0 then
flagString = flagString .. cacheFlagNames[i] .. ", "
end
end
flagString = string.sub(flagString, 0, -3)
end
attributes = attributes .. flagString .. "#"
else
-- the Add Heart attributes count half a heart as 1, so divide the value in half
if string.find(v, "Hearts") then attrib = attrib / 2 end
-- the g flag removes .0 in numbers like 1.0 (caused by the hearts division)
local s = string.format("%.4g",attrib)
if attrib > 0 then s = "+" .. s end
attributes = attributes .. string.gsub(attributeConversions[v], "{1}", s)
--things like this don't localize well at all...
if attrib > 1 or attrib < -1 then attributes = attributes .. "s" end
attributes = attributes .. "#"
end
end
debugLog:write(v .. ": " .. tostring(item[v]) .. "\n")
end
eidDesc = attributes .. eidDesc
debugLog:flush()
EID:addCollectible(maxNumber - spawnedItems, eidDesc)
--print("adding " .. (maxNumber - spawnedItems))
end
end
line = theLog:read()
end
logCursor = theLog:seek()
theLog:close()
end
EID:AddCallback(ModCallbacks.MC_POST_UPDATE, CheckLogForItems)
local function GameStartTMTRAINER(_,isSave)
local theLog = io.open(logLocation, "r")
if (theLog == nil) then
print("[EID] Could not find the log file at " .. logLocation .. "; glitched item descriptions won't work!")
logFound = false
return
else
logFound = true
end
spawnedItems = 0
end
EID:AddCallback(ModCallbacks.MC_POST_GAME_STARTED, GameStartTMTRAINER)