2019-03-19 08:56:38 +00:00
local Items = require ' database.items '
2018-12-19 04:06:41 +00:00
local TscFile = require ' tsc_file '
2019-03-19 08:56:38 +00:00
local WorldGraph = require ' database.world_graph '
2018-12-19 04:06:41 +00:00
local C = Class : extend ( )
local TSC_FILES = { }
do
2019-03-20 12:37:07 +00:00
for key , location in ipairs ( WorldGraph ( Items ( ) ) : getLocations ( ) ) do
if location.map ~= nil and location.event ~= nil then
local filename = location.map
if not _.contains ( TSC_FILES , filename ) then
table.insert ( TSC_FILES , filename )
end
2018-12-19 04:06:41 +00:00
end
end
end
2018-12-29 08:23:24 +00:00
function C : new ( )
self._isCaveStoryPlus = false
2019-03-19 08:56:38 +00:00
self.itemDeck = Items ( )
2019-03-20 12:37:07 +00:00
self.worldGraph = WorldGraph ( self.itemDeck )
2018-12-29 08:23:24 +00:00
end
2018-12-19 04:06:41 +00:00
function C : randomize ( path )
2018-12-19 21:39:40 +00:00
resetLog ( )
2018-12-29 02:27:25 +00:00
logNotice ( ' === Cave Story Randomizer v ' .. VERSION .. ' === ' )
2018-12-19 04:06:41 +00:00
local success , dirStage = self : _mountDirectory ( path )
if not success then
return " Could not find \" data \" subfolder. \n \n Maybe try dropping your Cave Story \" data \" folder in directly? "
end
self : _seedRngesus ( )
local tscFiles = self : _createTscFiles ( dirStage )
2018-12-20 05:07:01 +00:00
-- self:_writePlaintext(tscFiles)
2019-03-19 08:56:38 +00:00
self : _shuffleItems ( tscFiles )
2019-03-20 12:37:07 +00:00
self : _writeModifiedData ( tscFiles )
2018-12-29 19:28:20 +00:00
self : _writePlaintext ( tscFiles )
2018-12-19 21:39:40 +00:00
self : _writeLog ( )
2018-12-19 04:06:41 +00:00
self : _unmountDirectory ( path )
2018-12-19 21:39:40 +00:00
return self : _getStatusMessage ( )
2018-12-19 04:06:41 +00:00
end
function C : _mountDirectory ( path )
local mountPath = ' mounted-data '
assert ( lf.mount ( path , mountPath ) )
local dirStage = ' / ' .. mountPath
local items = lf.getDirectoryItems ( dirStage )
local containsData = _.contains ( items , ' data ' )
if containsData then
dirStage = dirStage .. ' /data '
end
2018-12-19 23:39:08 +00:00
-- For Cave Story+
local items = lf.getDirectoryItems ( dirStage )
local containsBase = _.contains ( items , ' base ' )
if containsBase then
dirStage = dirStage .. ' /base '
2018-12-29 08:23:24 +00:00
self._isCaveStoryPlus = true
2018-12-19 23:39:08 +00:00
end
2018-12-19 04:06:41 +00:00
local items = lf.getDirectoryItems ( dirStage )
local containsStage = _.contains ( items , ' Stage ' )
if containsStage then
dirStage = dirStage .. ' /Stage '
else
return false , ' '
end
return true , dirStage
end
function C : _seedRngesus ( )
local seed = tostring ( os.time ( ) )
math.randomseed ( seed )
logNotice ( ( ' Offering seed "%s" to RNGesus ' ) : format ( seed ) )
end
function C : _createTscFiles ( dirStage )
local tscFiles = { }
for _ , filename in ipairs ( TSC_FILES ) do
2019-03-20 12:37:07 +00:00
local path = dirStage .. ' / ' .. filename .. ' .tsc '
2018-12-19 04:06:41 +00:00
tscFiles [ filename ] = TscFile ( path )
2019-03-20 12:37:07 +00:00
tscFiles [ filename ] . mapName = filename
2018-12-19 04:06:41 +00:00
end
return tscFiles
end
2018-12-20 05:07:01 +00:00
function C : _writePlaintext ( tscFiles )
local sourcePath = lf.getSourceBaseDirectory ( )
-- Create /data/Plaintext if it doesn't already exist.
local command = ( ' mkdir "%s" ' ) : format ( sourcePath .. ' /data/Plaintext ' )
os.execute ( command ) -- HERE BE DRAGONS!!!
-- Write modified files.
for filename , tscFile in pairs ( tscFiles ) do
2019-03-20 12:37:07 +00:00
local path = sourcePath .. ' /data/Plaintext/ ' .. filename .. ' .txt '
2018-12-20 05:07:01 +00:00
tscFile : writePlaintextTo ( path )
end
end
2018-12-19 04:06:41 +00:00
function C : _shuffleItems ( tscFiles )
2019-03-20 12:37:07 +00:00
local l , i = # self.worldGraph : getLocations ( ) , # self.itemDeck : getItems ( )
2019-03-21 05:46:22 +00:00
assert ( l == i , ( " Locations: %d \r \n Items: %d " ) : format ( l , i ) )
2019-03-19 08:56:38 +00:00
-- first fill puppies
2019-03-20 12:37:07 +00:00
self : _fastFillItems ( self.itemDeck : getItemsByAttribute ( " puppy " ) , _.shuffle ( self.worldGraph : getPuppySpots ( ) ) )
2018-12-19 04:06:41 +00:00
2019-03-20 12:37:07 +00:00
local mandatory = _.compact ( _.shuffle ( self.itemDeck : getMandatoryItems ( ) ) )
local optional = _.compact ( _.shuffle ( self.itemDeck : getOptionalItems ( ) ) )
2019-03-21 05:46:22 +00:00
2019-03-19 08:56:38 +00:00
-- next fill hell chests, which cannot have mandatory items
2019-03-20 12:37:07 +00:00
self : _fastFillItems ( optional , _.shuffle ( self.worldGraph : getHellSpots ( ) ) )
2018-12-19 23:49:40 +00:00
2019-03-20 12:37:07 +00:00
self : _fillItems ( mandatory , _.shuffle ( _.reverse ( self.worldGraph : getEmptyLocations ( ) ) ) )
2019-03-19 08:56:38 +00:00
self : _fastFillItems ( optional , _.shuffle ( self.worldGraph : getEmptyLocations ( ) ) )
2019-03-10 05:41:37 +00:00
2019-03-20 12:37:07 +00:00
assert ( # self.worldGraph : getEmptyLocations ( ) == 0 , self.worldGraph : emptyString ( ) .. " \r \n " .. self.itemDeck : unplacedString ( ) )
self.worldGraph : writeItems ( tscFiles )
2019-03-19 08:56:38 +00:00
end
2018-12-19 20:15:26 +00:00
2019-03-21 05:46:22 +00:00
function C : _fillItems ( items , locations )
assert ( # items > 0 , ( " No items provided! Trying to fill %s locations. " ) : format ( # locations ) )
2019-03-20 12:37:07 +00:00
assert ( # items <= # locations , string.format ( " Trying to fill more items than there are locations! Items: %d Locations: %d " , # items , # locations ) )
2019-03-15 04:05:08 +00:00
2019-03-21 05:46:22 +00:00
local itemsLeft = _.clone ( items )
2019-03-20 12:37:07 +00:00
for key , item in ipairs ( items ) do
2019-03-19 08:56:38 +00:00
local assumed = self.worldGraph : collect ( _.remove ( itemsLeft , item ) )
2019-03-20 12:37:07 +00:00
local fillable = _.filter ( locations , function ( k , v ) return not v : hasItem ( ) and v : canAccess ( assumed ) end )
local empty = _.filter ( locations , function ( k , v ) return not v : hasItem ( ) end )
assert ( # fillable > 0 , " No available locations! " )
assert ( item ~= nil , " No item found! " )
2019-03-19 08:56:38 +00:00
fillable [ 1 ] : setItem ( item )
end
end
2019-03-15 04:05:08 +00:00
2019-03-19 08:56:38 +00:00
function C : _fastFillItems ( items , locations )
2019-03-21 05:46:22 +00:00
assert ( # items > 0 , ( " No items provided! Attempting to fast fill %s locations. " ) : format ( # locations ) )
2019-03-20 12:37:07 +00:00
for key , location in ipairs ( locations ) do
2019-03-21 05:46:22 +00:00
local item = _.pop ( items )
2019-03-20 12:37:07 +00:00
if item == nil then break end -- no items left to place, but there are still locations open
location : setItem ( item )
2019-03-10 05:41:37 +00:00
end
2018-12-19 04:06:41 +00:00
end
function C : _writeModifiedData ( tscFiles )
2018-12-29 08:23:24 +00:00
local basePath = self : _getWritePathStage ( )
2018-12-19 04:06:41 +00:00
for filename , tscFile in pairs ( tscFiles ) do
2019-03-20 12:37:07 +00:00
local path = basePath .. ' / ' .. filename .. ' .tsc '
2018-12-19 04:06:41 +00:00
tscFile : writeTo ( path )
end
end
2018-12-19 20:15:26 +00:00
function C : _copyModifiedFirstCave ( )
2018-12-29 08:23:24 +00:00
local cavePxmPath = self : _getWritePathStage ( ) .. ' /Cave.pxm '
2018-12-19 20:15:26 +00:00
local data = lf.read ( ' database/Cave.pxm ' )
assert ( data )
U.writeFile ( cavePxmPath , data )
end
2018-12-19 21:39:40 +00:00
function C : _writeLog ( )
2018-12-29 08:23:24 +00:00
local path = self : _getWritePath ( ) .. ' /log.txt '
2018-12-19 21:39:40 +00:00
local data = getLogText ( )
U.writeFile ( path , data )
print ( " \n " )
end
2018-12-29 08:23:24 +00:00
function C : _getWritePath ( )
return select ( 1 , self : _getWritePaths ( ) )
end
function C : _getWritePathStage ( )
return select ( 2 , self : _getWritePaths ( ) )
end
function C : _getWritePaths ( )
if self._writePath == nil then
local sourcePath = lf.getSourceBaseDirectory ( )
self._writePath = sourcePath .. ' /data '
self._writePathStage = ( self._isCaveStoryPlus )
and ( self._writePath .. ' /base/Stage ' )
or ( self._writePath .. ' /Stage ' )
-- Create /data(/base)/Stage if it doesn't already exist.
local command = ( ' mkdir "%s" ' ) : format ( self._writePathStage )
os.execute ( command ) -- HERE BE DRAGONS!!!
end
return self._writePath , self._writePathStage
end
2018-12-19 04:06:41 +00:00
function C : _unmountDirectory ( path )
assert ( lf.unmount ( path ) )
2018-12-19 21:39:40 +00:00
end
function C : _getStatusMessage ( )
local warnings , errors = countLogWarningsAndErrors ( )
local line1
if warnings == 0 and errors == 0 then
line1 = " Randomized data successfully created! "
elseif warnings ~= 0 and errors == 0 then
line1 = ( " Randomized data was created with %d warning(s). " ) : format ( warnings )
else
return ( " Encountered %d error(s) and %d warning(s) when randomizing data! " ) : format ( errors , warnings )
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 \n %s \n \n %s " ) : format ( line1 , line2 , line3 )
return status
2018-12-19 04:06:41 +00:00
end
return C