creates a unique sharecode for any given combination of settings

This commit is contained in:
duncathan 2020-02-28 03:20:05 -06:00
parent 45fc1300fc
commit ee3c595077
5 changed files with 332 additions and 13 deletions

260
src/lib/bit.lua Normal file
View file

@ -0,0 +1,260 @@
--[[---------------
LuaBit v0.4
-------------------
a bitwise operation lib for lua.
http://luaforge.net/projects/bit/
How to use:
-------------------
bit.bnot(n) -- bitwise not (~n)
bit.band(m, n) -- bitwise and (m & n)
bit.bor(m, n) -- bitwise or (m | n)
bit.bxor(m, n) -- bitwise xor (m ^ n)
bit.brshift(n, bits) -- right shift (n >> bits)
bit.blshift(n, bits) -- left shift (n << bits)
bit.blogic_rshift(n, bits) -- logic right shift(zero fill >>>)
Please note that bit.brshift and bit.blshift only support number within
32 bits.
2 utility functions are provided too:
bit.tobits(n) -- convert n into a bit table(which is a 1/0 sequence)
-- high bits first
bit.tonumb(bit_tbl) -- convert a bit table into a number
-------------------
Under the MIT license.
copyright(c) 2006~2007 hanzhao (abrash_han@hotmail.com)
--]]---------------
do
------------------------
-- bit lib implementions
local function check_int(n)
-- checking not float
if(n - math.floor(n) > 0) then
error("trying to use bitwise operation on non-integer!")
end
end
local function to_bits(n)
check_int(n)
if(n < 0) then
-- negative
return to_bits(bit.bnot(math.abs(n)) + 1)
end
-- to bits table
local tbl = {}
local cnt = 1
while (n > 0) do
local last = math.mod(n,2)
if(last == 1) then
tbl[cnt] = 1
else
tbl[cnt] = 0
end
n = (n-last)/2
cnt = cnt + 1
end
return tbl
end
local function tbl_to_number(tbl)
local n = table.getn(tbl)
local rslt = 0
local power = 1
for i = 1, n do
rslt = rslt + tbl[i]*power
power = power*2
end
return rslt
end
local function expand(tbl_m, tbl_n)
local big = {}
local small = {}
if(table.getn(tbl_m) > table.getn(tbl_n)) then
big = tbl_m
small = tbl_n
else
big = tbl_n
small = tbl_m
end
-- expand small
for i = table.getn(small) + 1, table.getn(big) do
small[i] = 0
end
end
local function bit_or(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if(tbl_m[i]== 0 and tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl_to_number(tbl)
end
local function bit_and(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if(tbl_m[i]== 0 or tbl_n[i] == 0) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl_to_number(tbl)
end
local function bit_not(n)
local tbl = to_bits(n)
local size = math.max(table.getn(tbl), 32)
for i = 1, size do
if(tbl[i] == 1) then
tbl[i] = 0
else
tbl[i] = 1
end
end
return tbl_to_number(tbl)
end
local function bit_xor(m, n)
local tbl_m = to_bits(m)
local tbl_n = to_bits(n)
expand(tbl_m, tbl_n)
local tbl = {}
local rslt = math.max(table.getn(tbl_m), table.getn(tbl_n))
for i = 1, rslt do
if(tbl_m[i] ~= tbl_n[i]) then
tbl[i] = 1
else
tbl[i] = 0
end
end
--table.foreach(tbl, print)
return tbl_to_number(tbl)
end
local function bit_rshift(n, bits)
check_int(n)
local high_bit = 0
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
high_bit = 2147483648 -- 0x80000000
end
for i=1, bits do
n = n/2
n = bit_or(math.floor(n), high_bit)
end
return math.floor(n)
end
-- logic rightshift assures zero filling shift
local function bit_logic_rshift(n, bits)
check_int(n)
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
end
for i=1, bits do
n = n/2
end
return math.floor(n)
end
local function bit_lshift(n, bits)
check_int(n)
if(n < 0) then
-- negative
n = bit_not(math.abs(n)) + 1
end
for i=1, bits do
n = n*2
end
return bit_and(n, 4294967295) -- 0xFFFFFFFF
end
local function bit_xor2(m, n)
local rhs = bit_or(bit_not(m), bit_not(n))
local lhs = bit_or(m, n)
local rslt = bit_and(lhs, rhs)
return rslt
end
--------------------
-- bit lib interface
bit = {
-- bit operations
bnot = bit_not,
band = bit_and,
bor = bit_or,
bxor = bit_xor,
brshift = bit_rshift,
blshift = bit_lshift,
bxor2 = bit_xor2,
blogic_rshift = bit_logic_rshift,
-- utility func
tobits = to_bits,
tonumb = tbl_to_number,
}
end
--[[
for i = 1, 100 do
for j = 1, 100 do
if(bit.bxor(i, j) ~= bit.bxor2(i, j)) then
error("bit.xor failed.")
end
end
end
--]]

View file

@ -12,6 +12,7 @@ ld = love.data
U = require 'util' U = require 'util'
require 'log' require 'log'
require 'lib.bit'
local random = require 'randomizer' local random = require 'randomizer'
local settings = require 'settings' local settings = require 'settings'

View file

@ -35,6 +35,7 @@ function C:new()
self.customseed = nil self.customseed = nil
self.puppy = false self.puppy = false
self.obj = "" self.obj = ""
self.sharecode = ""
end end
function C:setPath(path) function C:setPath(path)
@ -53,7 +54,11 @@ function C:randomize()
return "Could not find \"data\" subfolder.\n\nMaybe try dropping your Cave Story \"data\" folder in directly?" return "Could not find \"data\" subfolder.\n\nMaybe try dropping your Cave Story \"data\" folder in directly?"
end end
self:_logSettings()
local seed = self:_seedRngesus() local seed = self:_seedRngesus()
self:_updateSharecode(seed)
local tscFiles = self:_createTscFiles(dirStage) local tscFiles = self:_createTscFiles(dirStage)
-- self:_writePlaintext(tscFiles) -- self:_writePlaintext(tscFiles)
self:_shuffleItems(tscFiles) self:_shuffleItems(tscFiles)
@ -64,7 +69,7 @@ function C:randomize()
self:_updateSettings() self:_updateSettings()
return self:_getStatusMessage(seed) return self:_getStatusMessage(seed, self.sharecode)
end end
function C:_mountDirectory(path) function C:_mountDirectory(path)
@ -257,17 +262,57 @@ function C:_unmountDirectory(path)
assert(lf.unmount(path)) assert(lf.unmount(path))
end end
function C:_logSettings()
local obj = "Best Ending"
if self.obj == "objBadEnd" then
obj = "Bad Ending"
elseif self.obj == "objNormalEnd" then
obj = "Normal Ending"
elseif self.obj == "objAllBosses" then
obj = "All Bosses"
end
local puppy = self.puppy and ", Puppysanity" or ""
logNotice(("Game settings: %s"):format(obj .. puppy))
end
function C:_updateSettings() function C:_updateSettings()
Settings.settings.puppy = self.puppy Settings.settings.puppy = self.puppy
Settings.settings.obj = self.obj Settings.settings.obj = self.obj
Settings:update() Settings:update()
end end
function C:_getStatusMessage(seed) function C:_updateSharecode(seed)
local settings = 0 -- 0b00000000
-- 0bXXXXXPOO
-- P: single bit used for puppysanity
-- O: two bits used for objective
-- X: unused
if self.obj == "objBadEnd" then
settings = bit.bor(settings, 1)
elseif self.obj == "objNormalEnd" then
settings = bit.bor(settings, 2)
elseif self.obj == "objAllBosses" then
settings = bit.bor(settings, 3)
end
if self.puppy then settings = bit.bor(settings, 4) end
if #seed < 20 then
seed = seed .. (" "):rep(20-#seed)
end
local packed = love.data.pack("data", "sB", seed, settings)
self.sharecode = love.data.encode("string", "base64", packed)
logNotice(("Sharecode: %s"):format(self.sharecode))
end
function C:_getStatusMessage(seed, sharecode)
local warnings, errors = countLogWarningsAndErrors() local warnings, errors = countLogWarningsAndErrors()
local line1 local line1
if warnings == 0 and errors == 0 then if warnings == 0 and errors == 0 then
line1 = ("Randomized data successfully created!\nSeed: %s"):format(seed) line1 = ("Randomized data successfully created!\nSeed: %s\nSharecode: %s"):format(seed, sharecode)
elseif warnings ~= 0 and errors == 0 then elseif warnings ~= 0 and errors == 0 then
line1 = ("Randomized data was created with %d warning(s)."):format(warnings) line1 = ("Randomized data was created with %d warning(s)."):format(warnings)
else else
@ -275,7 +320,7 @@ function C:_getStatusMessage(seed)
end end
local line2 = "Next overwrite the files in your copy of Cave Story with the versions in the newly created \"data\" folder. Don't forget to save a backup of the originals!" local line2 = "Next overwrite the files in your copy of Cave Story with the versions in the newly created \"data\" folder. Don't forget to save a backup of the originals!"
local line3 = "Then play and have a fun!" local line3 = "Then play and have a fun!"
local status = ("%s\n%s\n\n%s"):format(line1, line2, line3) local status = ("%s\n%s\n%s"):format(line1, line2, line3)
return status return status
end end

View file

@ -12,19 +12,27 @@ layout:setTheme(require 'lib.luigi.theme.dark')
settings:setTheme(require 'lib.luigi.theme.dark') settings:setTheme(require 'lib.luigi.theme.dark')
function C:setup() function C:setup()
settings.puppy.value = Settings.settings.puppy self:loadSettings(Settings.settings.puppy, Settings.settings.obj)
local obj = Settings.settings.obj
if obj == "objBadEnd" then background = lg.newImage('assets/background.png')
self:draw()
layout:show()
end
function C:loadSettings(puppy, obj, seed)
settings.puppy.value = puppy
if obj == "objBadEnd" or obj == 1 then
settings.bad.value = true settings.bad.value = true
settings.norm.value = false settings.norm.value = false
settings.boss.value = false settings.boss.value = false
settings.best.value = false settings.best.value = false
elseif obj == "objNormalEnd" then elseif obj == "objNormalEnd" or obj == 2 then
settings.bad.value = false settings.bad.value = false
settings.norm.value = true settings.norm.value = true
settings.boss.value = false settings.boss.value = false
settings.best.value = false settings.best.value = false
elseif obj == "objAllBosses" then elseif obj == "objAllBosses" or obj == 3 then
settings.bad.value = false settings.bad.value = false
settings.norm.value = false settings.norm.value = false
settings.boss.value = true settings.boss.value = true
@ -36,9 +44,7 @@ function C:setup()
settings.best.value = true settings.best.value = true
end end
background = lg.newImage('assets/background.png') settings.customseed.value = seed or ""
self:draw()
layout:show()
end end
layout.version.text = 'Cave Story Randomizer [Open Mode] v' .. VERSION layout.version.text = 'Cave Story Randomizer [Open Mode] v' .. VERSION
@ -81,6 +87,13 @@ settings.closeButton:onPress(function()
layout:show() layout:show()
end) end)
settings.customseed:onChange(function()
if #settings.customseed.value > 20 then
settings.customseed.value = settings.customseed.value:sub(1, 20)
end
settings.seedcount.text = ("%s/20"):format(#settings.customseed.value)
end)
function C:draw() function C:draw()
lg.draw(background, 0, 0) lg.draw(background, 0, 0)
--layout:show() --layout:show()

View file

@ -5,7 +5,7 @@ return { style = 'dialog',
{ type = 'label', text = 'Seed' }, { type = 'label', text = 'Seed' },
{ {
{ type = 'radio', group = 'seed', text = 'Use random seed', value = true }, { type = 'radio', group = 'seed', text = 'Use random seed', value = true },
{ flow = 'y', { type = 'radio', group = 'seed', text = 'Use custom seed', id = 'seedselect'}, { type = 'text', id = 'customseed', width = 150 }, {height = false} } { flow = 'y', { type = 'radio', group = 'seed', text = 'Use custom seed', id = 'seedselect'}, {{ type = 'text', id = 'customseed', width = 200}, flow = 'x', { type = 'label', id = 'seedcount' }}, {height = false} }
}, },
{ type = 'label', text = 'Randomization Options' }, { type = 'label', text = 'Randomization Options' },
{ type = 'check', value = false, id = 'puppy', text = "Puppysanity"}, { type = 'check', value = false, id = 'puppy', text = "Puppysanity"},