diff --git a/src/lib/bit.lua b/src/lib/bit.lua new file mode 100644 index 0000000..d9fd4ce --- /dev/null +++ b/src/lib/bit.lua @@ -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 +--]] + + + + + + + + + + + + + diff --git a/src/main.lua b/src/main.lua index 9ec3029..f2ecb68 100644 --- a/src/main.lua +++ b/src/main.lua @@ -12,6 +12,7 @@ ld = love.data U = require 'util' require 'log' +require 'lib.bit' local random = require 'randomizer' local settings = require 'settings' diff --git a/src/randomizer.lua b/src/randomizer.lua index 35cf103..464a408 100644 --- a/src/randomizer.lua +++ b/src/randomizer.lua @@ -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 diff --git a/src/ui/draw.lua b/src/ui/draw.lua index a5413d1..ba41d25 100644 --- a/src/ui/draw.lua +++ b/src/ui/draw.lua @@ -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() diff --git a/src/ui/settings.lua b/src/ui/settings.lua index 5c4409b..41444ef 100644 --- a/src/ui/settings.lua +++ b/src/ui/settings.lua @@ -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"},