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'
require 'log'
require 'lib.bit'
local random = require 'randomizer'
local settings = require 'settings'

View file

@ -35,6 +35,7 @@ function C:new()
self.customseed = nil
self.puppy = false
self.obj = ""
self.sharecode = ""
end
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?"
end
self:_logSettings()
local seed = self:_seedRngesus()
self:_updateSharecode(seed)
local tscFiles = self:_createTscFiles(dirStage)
-- self:_writePlaintext(tscFiles)
self:_shuffleItems(tscFiles)
@ -64,7 +69,7 @@ function C:randomize()
self:_updateSettings()
return self:_getStatusMessage(seed)
return self:_getStatusMessage(seed, self.sharecode)
end
function C:_mountDirectory(path)
@ -257,17 +262,57 @@ function C:_unmountDirectory(path)
assert(lf.unmount(path))
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()
Settings.settings.puppy = self.puppy
Settings.settings.obj = self.obj
Settings:update()
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 line1
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
line1 = ("Randomized data was created with %d warning(s)."):format(warnings)
else
@ -275,7 +320,7 @@ function C:_getStatusMessage(seed)
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 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
end

View file

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

View file

@ -5,7 +5,7 @@ return { style = 'dialog',
{ type = 'label', text = 'Seed' },
{
{ 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 = 'check', value = false, id = 'puppy', text = "Puppysanity"},