0 | 1 | 2 | 3 | 4 | |
A5 | |||||
A6 | |||||
A7 | |||||
B4 | |||||
B5 | |||||
B6 | |||||
B7 | |||||
B Star | |||||
C1 | |||||
C2 | |||||
C3 | |||||
C4 | |||||
C5 | |||||
C6 | |||||
C7 | |||||
C Star |
-- nothing to do if messages are not available for current world if not talPlayerMessagesAvailableForCurrentWorld(worldGlobals.worldInfo) then return endExit program if player is done with the current world.
local function RandomFomStringChars(str, multiplier) local len = #str local sum = 0 local offset = 32 for i = 1, len do sum = (string.byte(str, i) - offset)*multiplier end return sum endThis function creates an add-in seed which is supposed to be based on the world name. However, this calls
sum =
not sum = sum +
, and so it in fact only uses the last character in the world name, in this case the world number.
local function ShowPaintBucketIfNecessary() -- paint buckets can onlys be unlocked if enough messages are unlocked if talGetUnlockedPlayerMessagesCount(worldGlobals.worldInfo) < 5 then -- paint item was not shown return false endExit function if player doesn't have paint unlocked.
-- talosProgress : CTalosProgress local talosProgress = nexGetTalosProgress(worldGlobals.worldInfo) local randomSeed = talosProgress:GetCodeValue("PaintItemSeed") if randomSeed == -1 then randomSeed = mthRoundF(mthRndF()*8909478) talosProgress:SetCode("PaintItemSeed", randomSeed) end local worldName = string.match(worldGlobals.worldInfo:GetWorldFileName(), "([^/]+)%.wld$") if randomSeed < 0 then randomSeed = -randomSeed end local multiplier = randomSeed % 8 if multiplier == 0 then multiplier = 1 endThis generates a random integer from 0 to 8909478, then from it creates a multiplier from 1-7. Note that a multiplier of 1 will occur with probability 1/4.
local randomIndex = randomSeed + RandomFomStringChars(worldName, multiplier) if randomIndex < 0 then randomIndex = -randomIndex endNow, the random integer from 0 to 8909478 is increased by the world name string times the multiplier. This means that the spawn set on A6 differs from A7, but not from B6.
local randomPaintItemIndex = 1 + randomIndex % #worldGlobals.paintItems worldGlobals.paintItems[randomPaintItemIndex]:Show() -- paint item was shown return true endThen, we convert the random number to a paint spawn. However, if there are 4 paint spawns (
#worldGlobals.paintItems
), then problems will arise because 4 and 8 are not coprime. For example, if we're on A3, then we calculate n + (n%8)*3
, which will be 0 or 4 mod 8. The exception is if 8 | n and the multiplier is set to 1, in which case it will be 7 mod 8. Regardless, this is not an even distribution of values 0-7, and it is this behavior that causes uneven spawns.
RunAsync(function() -- wait for all other scripts/items to boot Wait(Delay(0.00001)) -- nothing to do if there are no paint items in the world if worldGlobals.paintItems == nil then return end -- show paint bucket if necessary at start if ShowPaintBucketIfNecessary() then -- since paint item was shown, there is no need to handle message unlocking -- events and check if paint item should be shown again return end -- wait for message unlocking event until paint bucket is shown while true do Wait(CustomEvent("PlayerMessageUnlocked")) if ShowPaintBucketIfNecessary() then break end end end)This code just checks a couple of edge cases and only displays paint if needed.
So let's take a look at the arithmetic:
RNG % 8 | Multiplier | World Number: |
Paint index = RNG + Multiplier * World Number |
Paint spawn = Paint index % 4 |