Added Stat Change Predictors for Experimental Treatment and Dataminer by Pattieburger
This commit is contained in:
1 parent
d98397ec26
commit
0162ef2e35
4 files changed
+137
-7
No files matched your search
@@ -34,3 +34,4 @@ If you enjoy this mod and want to help on it, check it out on GitHub: https://gi
|
|||||||
- [adc](https://steamcommunity.com/id/whytefang/) for creating an awesome new language pack and providing ideas for new functionalities
|
- [adc](https://steamcommunity.com/id/whytefang/) for creating an awesome new language pack and providing ideas for new functionalities
|
||||||
- [CreepSore](https://github.com/CreepSore) for creating the bag of crafting search feature
|
- [CreepSore](https://github.com/CreepSore) for creating the bag of crafting search feature
|
||||||
- [Poyo](https://x.com/Poyomama02) for the great thumbnail art
|
- [Poyo](https://x.com/Poyomama02) for the great thumbnail art
|
||||||
|
- Pattieburger for Experimental Treatment and Dataminer Stat Change Predictors
|
||||||
@@ -413,9 +413,8 @@ function EID:GlyphOfBalancePrediction(player)
|
|||||||
end
|
end
|
||||||
|
|
||||||
--- Crooked Penny ---
|
--- Crooked Penny ---
|
||||||
function EID:CrookedPennyPrediction(rng, carBattery)
|
function EID:CrookedPennyPrediction(seed, carBattery)
|
||||||
local rng = EID:RNGNext(rng)
|
seed = EID:RNGNext(seed)
|
||||||
local seed = rng
|
|
||||||
-- Repentance makes a new seed by XORing the room's decoration seed
|
-- Repentance makes a new seed by XORing the room's decoration seed
|
||||||
if EID.isRepentance then
|
if EID.isRepentance then
|
||||||
local roomSeed = game:GetLevel():GetCurrentRoom():GetDecorationSeed()
|
local roomSeed = game:GetLevel():GetCurrentRoom():GetDecorationSeed()
|
||||||
@@ -430,13 +429,106 @@ function EID:CrookedPennyPrediction(rng, carBattery)
|
|||||||
else
|
else
|
||||||
result = EID:getDescriptionEntry("CrookedPennyTails")
|
result = EID:getDescriptionEntry("CrookedPennyTails")
|
||||||
-- failures advance crooked penny's RNG an additional time to give the pity penny a seed value
|
-- failures advance crooked penny's RNG an additional time to give the pity penny a seed value
|
||||||
rng = EID:RNGNext(rng)
|
seed = EID:RNGNext(seed)
|
||||||
end
|
end
|
||||||
|
|
||||||
if carBattery then return result .. "#{{Collectible356}} " .. EID:CrookedPennyPrediction(rng)
|
if carBattery then return result .. "#{{Collectible356}} " .. EID:CrookedPennyPrediction(seed)
|
||||||
else return result end
|
else return result end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Experimental Treatment Stat Changes (by Pattieburger)
|
||||||
|
-- Uses only s2,s3,s4 from the ET collectible RNG stream.
|
||||||
|
-- Outcome pattern: 4 stats UP, 2 stats DOWN, 1 stat unchanged.
|
||||||
|
|
||||||
|
-- Possible Stat change lookup table, for use with modular descriptions (7 stats total).
|
||||||
|
-- Used by Experimental Treatment and also Dataminer, without the first entry.
|
||||||
|
local statChangeLookup = {
|
||||||
|
[1] = {"RedHeart", 1.0 }, -- Health container
|
||||||
|
[2] = {"Speed", 0.2 },
|
||||||
|
[3] = REPENTANCE and {"FireRate", 0.5 } or {"Tears", 0.5 },
|
||||||
|
[4] = {"Damage", 1.0 },
|
||||||
|
[5] = {"Range", REPENTANCE and 2.5 or 0.5 },
|
||||||
|
[6] = {"ShotSpeed", 0.2 },
|
||||||
|
[7] = {"Luck", 1.0 },
|
||||||
|
}
|
||||||
|
|
||||||
|
function EID:ExperimentalTreatmentRNGCheck(player)
|
||||||
|
-- s1: current collectible RNG seed (pre-pickup)
|
||||||
|
local s1 = player:GetCollectibleRNG(CollectibleType.COLLECTIBLE_EXPERIMENTAL_TREATMENT):GetSeed()
|
||||||
|
|
||||||
|
-- simulate advances
|
||||||
|
local s2 = EID:RNGNext(s1, 5, 9, 7) -- used (NoChange)
|
||||||
|
local s3 = EID:RNGNext(s2, 5, 9, 7) -- used (Neg1)
|
||||||
|
local s4 = EID:RNGNext(s3, 5, 9, 7) -- used (Neg2)
|
||||||
|
|
||||||
|
-- NoChange: (s2 % 7) + 1
|
||||||
|
local noChange = (s2 % 7) + 1
|
||||||
|
-- Neg1: (s3 % 6) + 1, but if it collides with NoChange -> becomes Luck (7)
|
||||||
|
local neg1 = (s3 % 6) + 1
|
||||||
|
if neg1 == noChange then
|
||||||
|
neg1 = 7
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Neg2: (s4 % 5) + 1, with collision rules:
|
||||||
|
-- - if collides with neg1 -> becomes Shot speed (6)
|
||||||
|
-- - if collides with noChange -> becomes Luck (7)
|
||||||
|
local neg2 = (s4 % 5) + 1
|
||||||
|
if neg2 == neg1 then
|
||||||
|
neg2 = 6
|
||||||
|
end
|
||||||
|
if neg2 == noChange then
|
||||||
|
neg2 = 7
|
||||||
|
end
|
||||||
|
|
||||||
|
local statChanges = {}
|
||||||
|
statChanges[statChangeLookup[neg1][1]] = -statChangeLookup[neg1][2]
|
||||||
|
statChanges[statChangeLookup[neg2][1]] = -statChangeLookup[neg2][2] + (statChanges[statChangeLookup[neg2][1]] or 0) -- optionally add existing value
|
||||||
|
|
||||||
|
-- Positives are the remaining 4 stats
|
||||||
|
for stat = 1, 7 do
|
||||||
|
if stat ~= noChange and stat ~= neg1 and stat ~= neg2 then
|
||||||
|
statChanges[statChangeLookup[stat][1]] = statChangeLookup[stat][2] + (statChanges[statChangeLookup[stat][1]] or 0) -- optionally add existing value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Return stat change table
|
||||||
|
return statChanges
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Dataminer Stat Changes (by Pattieburger)
|
||||||
|
-- We "shuffle" the stat table using s1..s5 (mod6, mod5, mod4, mod3, mod2).
|
||||||
|
-- Result: position 1 = stat UP, position 2 = stat DOWN.
|
||||||
|
function EID:DataminerRNGCheck(player)
|
||||||
|
-- s0: current collectible RNG seed (pre-use)
|
||||||
|
local s0 = player:GetCollectibleRNG(CollectibleType.COLLECTIBLE_DATAMINER):GetSeed()
|
||||||
|
|
||||||
|
-- simulate s1..s5 (do NOT alter game RNG; just compute next seeds)
|
||||||
|
local s1 = EID:RNGNext(s0, 5, 9, 7)
|
||||||
|
local s2 = EID:RNGNext(s1, 5, 9, 7)
|
||||||
|
local s3 = EID:RNGNext(s2, 5, 9, 7)
|
||||||
|
local s4 = EID:RNGNext(s3, 5, 9, 7)
|
||||||
|
local s5 = EID:RNGNext(s4, 5, 9, 7)
|
||||||
|
|
||||||
|
-- stat indices:
|
||||||
|
-- 1=Speed, 2=Fire rate, 3=Damage, 4=Range, 5=Shotspeed, 6=Luck
|
||||||
|
local statTable = {1,2,3,4,5,6}
|
||||||
|
local seeds = { s1, s2, s3, s4, s5 }
|
||||||
|
|
||||||
|
for i = 6, 2, -1 do
|
||||||
|
local seed = seeds[7 - i] -- 6→1, 5→2, 4→3, 3→4, 2→5
|
||||||
|
local j = (seed % i) + 1
|
||||||
|
statTable[i], statTable[j] = statTable[j], statTable[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
local upStat = statTable[1] + 1 -- +1 to skip Health container entry
|
||||||
|
local downStat = statTable[2] + 1 -- +1 to skip Health container entry
|
||||||
|
|
||||||
|
return {
|
||||||
|
[statChangeLookup[downStat][1]] = -statChangeLookup[downStat][2],
|
||||||
|
[statChangeLookup[upStat][1]] = statChangeLookup[upStat][2],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
-- Liberty Cap, Broken Syringe, Mom's Lock (extremely similar, simple RNG modulos) --
|
-- Liberty Cap, Broken Syringe, Mom's Lock (extremely similar, simple RNG modulos) --
|
||||||
local libertyItems = {[0] = 21, 71, 120, 121}
|
local libertyItems = {[0] = 21, 71, 120, 121}
|
||||||
if EID.isRepentance then libertyItems = {[0] = 12, 71, 120, 121, 342, 398} end
|
if EID.isRepentance then libertyItems = {[0] = 12, 71, 120, 121, 342, 398} end
|
||||||
|
|||||||
@@ -141,6 +141,29 @@ local function BlackRuneCallback(descObj)
|
|||||||
return VoidCallback(descObj, true)
|
return VoidCallback(descObj, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function RandomStatIncreaseCallback(descObj)
|
||||||
|
for i = 1, #EID.coopAllPlayers do
|
||||||
|
local player = EID.coopAllPlayers[i]
|
||||||
|
-- only add description for relevant players
|
||||||
|
if not EID.InsideItemReminder or (EID.InsideItemReminder and EID.ItemReminderPlayerEntity == player) then
|
||||||
|
local playerID = player:GetPlayerType()
|
||||||
|
EID:appendToDescription(descObj, "#"..EID:GetPlayerIcon(playerID) .. " {{ColorIsaac}}"..player:GetName().."{{CR}}#")
|
||||||
|
|
||||||
|
if descObj.ObjSubType == 240 then
|
||||||
|
-- Experimental Treatment
|
||||||
|
local itemDataTable = EID:ExperimentalTreatmentRNGCheck(player)
|
||||||
|
EID:appendToDescription(descObj, EID:GenerateDescriptionFromStatTable(itemDataTable))
|
||||||
|
else
|
||||||
|
-- Dataminer
|
||||||
|
local itemDataTable = EID:DataminerRNGCheck(player)
|
||||||
|
EID:appendToDescription(descObj, EID:GenerateDescriptionFromStatTable(itemDataTable))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return descObj
|
||||||
|
end
|
||||||
|
|
||||||
-- Map each text block of Pandora's Box to the AbsoluteStage number. Second entry is the block used for Alt-Stages
|
-- Map each text block of Pandora's Box to the AbsoluteStage number. Second entry is the block used for Alt-Stages
|
||||||
local pandoraStages = {
|
local pandoraStages = {
|
||||||
[1] = { 1, 1 }, -- B1
|
[1] = { 1, 1 }, -- B1
|
||||||
@@ -1302,6 +1325,10 @@ local function EIDConditionsAB(descObj)
|
|||||||
if EID.collectiblesOwned[477] then table.insert(callbacks, VoidCallback) end
|
if EID.collectiblesOwned[477] then table.insert(callbacks, VoidCallback) end
|
||||||
if EID.collectiblesOwned["5.300.41"] then table.insert(callbacks, BlackRuneCallback) end
|
if EID.collectiblesOwned["5.300.41"] then table.insert(callbacks, BlackRuneCallback) end
|
||||||
end
|
end
|
||||||
|
if EID.Config["ItemReminderShowRNGCheats"] then
|
||||||
|
if descObj.ObjSubType == 240 then table.insert(callbacks, RandomStatIncreaseCallback) end
|
||||||
|
if descObj.ObjSubType == 481 then table.insert(callbacks, RandomStatIncreaseCallback) end
|
||||||
|
end
|
||||||
|
|
||||||
elseif descObj.ObjVariant == PickupVariant.PICKUP_TRINKET then
|
elseif descObj.ObjVariant == PickupVariant.PICKUP_TRINKET then
|
||||||
if descObj.ObjSubType == 80 then table.insert(callbacks, BlackFeatherCallback) end
|
if descObj.ObjSubType == 80 then table.insert(callbacks, BlackFeatherCallback) end
|
||||||
|
|||||||
@@ -464,14 +464,24 @@ function EID:GenerateDescription(descriptionObj)
|
|||||||
if EID.descriptions[EID:getLanguage()].DisableModularDescriptions then return nil end
|
if EID.descriptions[EID:getLanguage()].DisableModularDescriptions then return nil end
|
||||||
|
|
||||||
local itemDataTable = EID.ItemData[itemID]
|
local itemDataTable = EID.ItemData[itemID]
|
||||||
|
|
||||||
|
return EID:GenerateDescriptionFromStatTable(itemDataTable, descriptionObj, itemID)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Generates a description for an item based on its modules defined in EID.ItemData
|
||||||
|
-- Optional: A description object can be provided to apply modular data modifiers
|
||||||
|
-- Returns an empty string if no modules are defined for the item
|
||||||
|
function EID:GenerateDescriptionFromStatTable(itemDataTable, descriptionObj, itemID)
|
||||||
-- return empty string if no ItemData are defined
|
-- return empty string if no ItemData are defined
|
||||||
if not itemDataTable then
|
if not itemDataTable then
|
||||||
return ""
|
return ""
|
||||||
end
|
end
|
||||||
|
|
||||||
itemDataTable = EID:ApplyModularDataModifier(itemDataTable, descriptionObj)
|
if descriptionObj then
|
||||||
|
itemDataTable = EID:ApplyModularDataModifier(itemDataTable, descriptionObj)
|
||||||
|
end
|
||||||
|
|
||||||
local sortedModulesPositivePrio, sortedModulesNegativePrio = EID:GetSortedModularDescriptionEntries(itemDataTable, false, true, itemID)
|
local sortedModulesPositivePrio, sortedModulesNegativePrio = EID:GetSortedModularDescriptionEntries(itemDataTable, false, itemID ~= nil, itemID)
|
||||||
|
|
||||||
local description = ""
|
local description = ""
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user