mirror of
https://github.com/cave-story-randomizer/cave-story-randomizer
synced 2025-05-06 06:54:25 +00:00
creates a unique sharecode for any given combination of settings
This commit is contained in:
parent
45fc1300fc
commit
ee3c595077
260
src/lib/bit.lua
Normal file
260
src/lib/bit.lua
Normal 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
|
||||||
|
--]]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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"},
|
||||||
|
|
Loading…
Reference in a new issue