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
2019-09-11 10:39:10 +00:00
local csdirectory
2019-03-25 08:23:29 +00:00
local function mkdir ( path )
local mkdir_str
if package.config : sub ( 1 , 1 ) == ' \\ ' then -- Windows
mkdir_str = ' mkdir "%s" '
else -- *nix
mkdir_str = " mkdir -p '%s' "
end
os.execute ( mkdir_str : format ( path ) ) -- HERE BE DRAGONS!!!
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 )
2019-09-12 02:51:48 +00:00
self.customseed = nil
2018-12-29 08:23:24 +00:00
end
2019-09-11 10:39:10 +00:00
function C : setPath ( path )
csdirectory = path
end
2019-09-11 23:03:27 +00:00
function C : ready ( )
return csdirectory ~= nil
end
2019-09-11 10:39:10 +00:00
function C : randomize ( )
2018-12-19 21:39:40 +00:00
resetLog ( )
2018-12-29 02:27:25 +00:00
logNotice ( ' === Cave Story Randomizer v ' .. VERSION .. ' === ' )
2019-09-11 10:39:10 +00:00
local success , dirStage = self : _mountDirectory ( csdirectory )
2018-12-19 04:06:41 +00:00
if not success then
return " Could not find \" data \" subfolder. \n \n Maybe try dropping your Cave Story \" data \" folder in directly? "
end
2019-03-24 16:16:13 +00:00
2019-09-12 02:51:48 +00:00
local seed = self : _seedRngesus ( )
2018-12-19 04:06:41 +00:00
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 ( )
2019-09-11 10:39:10 +00:00
self : _unmountDirectory ( csdirectory )
2019-09-12 02:51:48 +00:00
return self : _getStatusMessage ( seed )
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 ( )
2019-09-15 23:38:09 +00:00
local seedstring = self.customseed or tostring ( os.time ( ) )
local seed = ld.encode ( ' string ' , ' hex ' , ld.hash ( ' sha256 ' , seedstring ) )
local s1 = tonumber ( seed : sub ( - 8 , - 1 ) , 16 ) -- first 32 bits (from right)
local s2 = tonumber ( seed : sub ( - 16 , - 9 ) , 16 ) -- next 32 bits
love.math . setRandomSeed ( s1 , s2 )
2019-09-12 03:04:14 +00:00
logNotice ( ( ' Offering seed "%s" to RNGesus ' ) : format ( seedstring ) )
return seedstring
2019-03-28 05:45:22 +00:00
end
2018-12-19 04:06:41 +00:00
function C : _createTscFiles ( dirStage )
local tscFiles = { }
for _ , filename in ipairs ( TSC_FILES ) do
2019-03-25 09:26:40 +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.
2019-03-25 08:23:29 +00:00
mkdir ( sourcePath .. ' /data/Plaintext ' )
2018-12-20 05:07:01 +00:00
-- 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-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 ( ) ) )
2019-03-22 07:32:05 +00:00
-- then fill one of the first cave spots with a weapon that can break blocks
_.shuffle ( self.worldGraph : getFirstCaveSpots ( ) ) [ 1 ] : setItem ( _.shuffle ( self.itemDeck : getItemsByAttribute ( " weaponSN " ) ) [ 1 ] )
2018-12-19 04:06:41 +00:00
2019-03-22 07:32:05 +00:00
local mandatory = _.compact ( _.shuffle ( self.itemDeck : getMandatoryItems ( true ) ) )
local optional = _.compact ( _.shuffle ( self.itemDeck : getOptionalItems ( true ) ) )
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-21 10:14:56 +00:00
--assert(#self.worldGraph:getEmptyLocations() == 0, self.worldGraph:emptyString() .. "\r\n" .. self.itemDeck:unplacedString())
2019-03-20 12:37:07 +00:00
self.worldGraph : writeItems ( tscFiles )
2019-03-21 06:39:30 +00:00
self.worldGraph : logLocations ( )
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-21 08:21:30 +00:00
repeat
local item = _.pop ( itemsLeft )
local assumed = self.worldGraph : collect ( itemsLeft )
2019-03-20 12:37:07 +00:00
local fillable = _.filter ( locations , function ( k , v ) return not v : hasItem ( ) and v : canAccess ( assumed ) end )
2019-03-21 10:14:56 +00:00
if # fillable > 0 then
logDebug ( ( " Placing %s at %s " ) : format ( item.name , fillable [ 1 ] . name ) )
fillable [ 1 ] : setItem ( item )
else
logError ( ( " No available locations for %s! Items left: %d " ) : format ( item.name , # itemsLeft ) )
end
2019-03-21 08:21:30 +00:00
until # itemsLeft == 0
2019-03-19 08:56:38 +00:00
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 )
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 ' )
end
2019-09-13 05:40:26 +00:00
-- Create /data(/base)/Stage if it doesn't already exist.
mkdir ( self._writePathStage )
2018-12-29 08:23:24 +00:00
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
2019-09-12 02:51:48 +00:00
function C : _getStatusMessage ( seed )
2018-12-19 21:39:40 +00:00
local warnings , errors = countLogWarningsAndErrors ( )
local line1
if warnings == 0 and errors == 0 then
2019-09-12 03:04:14 +00:00
line1 = ( " Randomized data successfully created! \n Seed: %s " ) : format ( seed )
2018-12-19 21:39:40 +00:00
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! "
2019-09-12 02:51:48 +00:00
local status = ( " %s \n %s \n \n %s " ) : format ( line1 , line2 , line3 )
2018-12-19 21:39:40 +00:00
return status
2018-12-19 04:06:41 +00:00
end
return C