mirror of
https://github.com/cave-story-randomizer/cave-story-randomizer
synced 2024-12-25 14:37:05 +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'
|
||||
|
||||
require 'log'
|
||||
require 'lib.bit'
|
||||
|
||||
local random = require 'randomizer'
|
||||
local settings = require 'settings'
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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"},
|
||||
|
|
Loading…
Reference in a new issue