Merge pull request #80 from duncathan/smallfeatures

adds music shuffle
This commit is contained in:
duncathan salt 2020-03-03 01:22:13 -06:00 committed by GitHub
commit 03afa18131
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
58 changed files with 389 additions and 123 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -1 +1 @@
0-FSS\S0-_pqd_idlSSSS_hqg0-FSS\T0-_pqd_idlSSST_hqg0-FSS\U0-_pqd_idlSSSU_hqg0-FSS\V0-_pqd_idlSSSV_hqg0-FSS\W0-_pqd_idlSSSW_hqg0-FSS\X0-_nh|0-_iomTXVU]SUSS_idlSSSV0-_iomTXXS]SUST_hqg0-FSS\Y0-_nh|_p|eSSSU_zdlSSSU_sul_idlSSST_hqg0-FSTSS0-_sul_idrSSST_wudSS[T]SS\X]STXT]SSTV0-FSTTS0-_sul_irpSSTY_idrSSSV0-_zdlSSVS0-_iomTXVW]STTT0-_wudSS[W]SS\X]SSTS]SSSU0-FSTTT0-_wudSS[X]SS\X]SSTS]SSSU0-FSUSS0-_fqsSUSS]SVUS]SSSS0-_ioPTXVU_ioNTXVV0-_zdlSSSU_idlSSSV_iomTXXS]SUST_hqg0-FSUST0-_gqsSUST0-_fpsSSSZ]SSTW]SSST0-_fpsSSS[]SSTW]SSST0-_fpsSSS\]SSTW]SSST0-_fpsSSTS]SSTW]SSST0-_fpsSSTT]SSTW]SSST0-_fpsSSTU]SSTW]SSST0-_fpsSSTV]SSTW]SSST_hqg0-FSUWS0-FSUWT0-_gqsSUWT_dqsSUWS]SSTS]SSSV_hqg0-FSUWU0-FSUWV0-_gqsSUWV_dqsSUWU]SSTS]SSSV_hqg0-FSUWW0-FSUWX0-_gqsSUWX_dqsSUWW]SSTS]SSSV_hqg0-FSUXS0-FSUXT0-_gqsSUXT_dqsSUXS]SSTS]SSST_hqg0-FSUXU0-#SUXV0-_gqsSUXV_dqsSUXU]SSTS]SSSV_hqg0-FSUXW0-FSUXX0-_gqsSUXX_dqsSUXW]SSTS]SSST_hqg0-FSUXY0-FSUXZ0-_gqsSUXZ_dqsSUXY]SSTS]SSSV_hqg0-FSU\S0-_gqsSU\S_vrxSSTU0-_fpsSTTZ]SSS\]SSU[0-_fpsSTTZ]SSTS]SSU[0-_fpsSTTZ]SSTT]SSU[0-_hqg0-FSU\T0-_gqsSU\T_vrxSSTU0-_fpsSS\X]SSSV]SSU[0-_fpsSS\X]SSSW]SSU[0-_fpsSS\X]SSSX]SSU[0-_fpsSS\X]SSSY]SSU[0-_hqg0-FSU\U0-_gqsSU\U_vrxSSTU0-_fpsSSSS]SSSS]SSU[0-_fpsSSSS]SSSS]SSU[0-_fpsSSSS]SSSS]SSU[0-_hqg0-FSVSS0-_gqsSVSS_gqdSVS\_gqdSVTT_gqdSVTZ0-_irqSVST]SSTY0-_fpxSSSZ_evoSSSS0-_fpsSSUS]SSTT]SSST0-_fpsSSUS]SSTU]SSST0-_erdSTSS0-_fqsTSST]SVVS]SSSS0-_fqsTSSU]SVVS]SSSU0-_vrxSSTU0-_hqg0-FSVST0-FSVSU0-_prySST[]SSTS_hqg0-FSV\S0-_sul_iomTXXT]SSST_ioNTXXT0-_vrxSSUU_fqsSV\S]SSUT]SSSS_hyhSWSS0-FSWSS0-_hyhSSVX0-FS\SS0-_gqsS\SS_hqg0-FS\ST0-_gqsS\ST_hqg0-FTSSS0-_erdSXSS_fpxSSSS_ioPTXXS_hqg0-FTSST0-FTSSU
>;Taaja>;m~rmt~<7E>adgmwrzaaaamvu>;Taajb>;m~rmt~<7E>adgmwrzaaabmvu>;Taajc>;m~rmt~<7E>adgmwrzaaacmvu>;Taajd>;m~rmt~<7E>adgmwrzaaadmvu>;Taaje>;m~rmt~<7E>adgmwrzaaaemvu>;Taajf>;m|v<>>;mw}{bfdckacaamwrzaaad>;mw}{bffakacabmvu>;Taajg>;m|v確~郭aaacm<63>zaaacmzmwrzaaabmvu>;Tabaa>;mzmwr€aaabm<62>raaibkaajfkabfbkaabd>;Tabba>;mzmw€~aabgmwr€aaad>;m<>zaada>;mw}{bfdekabbb>;m<>raaiekaajfkaabakaaac>;Tabbb>;m<>raaifkaajfkaabakaaac>;Tacaa>;mt∥caakadcakaaaa>;mw}^bfdcmw}\bfdd>;m<>zaaacmwrzaaadmw}{bffakacabmvu>;Tacab>;mu∥cab>;mt~∥aahkaabekaaab>;mt~∥aaikaabekaaab>;mt~∥aajkaabekaaab>;mt~∥abakaabekaaab>;mt~∥abbkaabekaaab>;mt~∥abckaabekaaab>;mt~∥abdkaabekaaabmvu>;Tacea>;Taceb>;mu∥cebmr∥ceakaabakaaadmvu>;Tacec>;Taced>;mu∥cedmr∥ceckaabakaaadmvu>;Tacee>;Tacef>;mu∥cefmr∥ceekaabakaaadmvu>;Tacfa>;Tacfb>;mu∥cfbmr∥cfakaa1akaaabmvu>;Tacfc>;Tacfd>;mu∥cfdmr∥cfckaabakaaadmvu>;Tacfe>;Tacff>;mu∥cffmr∥cfekaabakaaabmvu>;Tacfg>;Tacfh>;mu∥cfhmr∥cfgkaabakaaadmvu>;Tacja>;mu∥cjamо<6D>abc>;mt~∥bbhkaaajkaaci>;mt~∥bbhkaabakaaci>;mt~∥bbhkaabbkaaci>;mvu>;Tacjb>;mu∥cjbmо<6D>abc>;mt~∥ajfkaaadkaaci>;mt~∥ajfkaaaekaaci>;mt~∥ajfkaaafkaaci>;mt~∥ajfkaaagkaaci>;mvu>;Tacjc>;mu∥cjcmо<6D>abc>;mt~∥aaakaaaakaaci>;mt~∥aaakaaaakaaci>;mt~∥aaakaaaakaaci>;mvu>;Tadaa>;mu∥daamuradajmuradbbmuradbh>;mw€adabkaabg>;mt~<7E>aahmsмaaaa>;mt~∥acakaabbkaaab>;mt~∥acakaabckaaab>;ms€rabaa>;mtaabkaddakaaaa>;mtaackaddakaaac>;mо<6D>abc>;mvu>;Tadab>;Tadac>;m~€㌢abikaabamvu>;Tadja>;mzmw}{bffbkaaabmw}\bffb>;mо<6D>accmt∥djakaacbkaaaamv<6D>aeaa>;Taeaa>;mv<6D>aadf>;Tajaa>;mu∥jaamvu>;Tajab>;mu∥jabmvu>;Tbaaa>;ms€rafaamt~<7E>aaamw}^bffamvu>;Tbaab>;Tbaac

View file

@ -1,14 +1,14 @@
#0090
<MNA<FAI0000<END
<MNA<CMU0036<FAI0000<END
#0091
<MNA<FAI0001<END
<MNA<CMU0036<FAI0001<END
#0092
<MNA<FAI0002<END
<MNA<CMU0036<FAI0002<END
#0093
<MNA<FAI0003<END
<MNA<CMU0036<FAI0003<END
#0094
<MNA<FAI0004<END
<MNA<CMU0036<FAI0004<END
#0095
<KEY

243
src/database/music.lua Normal file
View file

@ -0,0 +1,243 @@
local song = Class:extend()
function song:new(name, id, jingle, beta)
self.name = name
self.id = id
self.jingle = jingle or false
self.beta = beta or false
end
local songs = {
xxxx = song("XXXX", "0000", true),
mischievousRobot = song("Mischievous Robot", "0001"),
safety = song("Safety", "0002"),
gameOver = song("Game Over", "0003", true),
gravity = song("Gravity", "0004"),
onToGrasstown = song("On to Grasstown", "0005"),
meltdown = song("Meltdown 2", "0006"),
eyesOfFlame = song("Eyes of Flame", "0007"),
gestation = song("Gestation", "0008"),
mimigaTown = song("Mimiga Town", "0009"),
getItem = song("Get Item!", "0010", true),
balrogsTheme = song("Balrog's Theme", "0011"),
cemetary = song("Cemetary", "0012"),
plant = song("Plant", "0013"),
pulse = song("Pulse", "0014"),
victory = song("Victory!", "0015", true),
getLifeCapsule = song("Get Life Capsule!", "0016", true),
tyrant = song("Tyrant", "0017"),
run = song("Run!", "0018"),
jenka1 = song("Jenka 1", "0019"),
labyrinthFight = song("Labyrinth Fight", "0020"),
access = song("Access", "0021"),
oppression = song("Oppression", "0022"),
geothermal = song("Geothermal", "0023"),
caveStory = song("Cave Story", "0024"),
moonsong = song("Moonsong", "0025"),
herosEnd = song("Hero's End", "0026"),
scorchingBack = song("Scorching Back", "0027"),
quiet = song("Quiet", "0028"),
finalCave = song("Final Cave", "0029"),
balcony = song("Balcony", "0030"),
charge = song("Charge", "0031"),
lastBattle = song("Last Battle", "0032"),
theWayBackHome = song("The Way Back Home", "0033"),
zombie = song("Zombie", "0034"),
breakDown = song("Break Down", "0035"),
runningHell = song("Running Hell", "0036"),
jenka2 = song("Jenka 2", "0037"),
livingWaterway = song("Living Waterway", "0038"),
sealChamber = song("Seal Chamber", "0039"),
torokosTheme = song("Toroko's Theme", "0040"),
white = song('"White"', "0041"),
windFortress = song("Wind Fortress", "0042", false, true),
halloween2 = song("Halloween 2", "0043", false, true),
peopleOfTheRoot = song("People of the Root", "0044", false, true),
pierWalk = song("Pier Walk", "0045", false, true),
snoopyCake = song("Snoopy Cake", "0046", false, true)
}
local cue = Class:extend()
function cue:new(map, events, default)
self.map = map
self.events = events
self.songid = default
self.default = default
end
local cues = {
cue("Pens1", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0002"),
cue("Pens1", {"0095", "0098"}, "0014"),
cue("Eggs", {"0090", "0091", "0092", "0093", "0094", "0095", "0099", "0503"}, "0001"),
cue("Eggs", {"0600"}, "0004"),
cue("EggX", {"0095"}, "0014"),
cue("EggR", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
cue("Weed", {"0090", "0091", "0092", "0093", "0094", "0098", "0099"}, "0005"),
cue("Santa", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("Santa", {"0099"}, "0028"),
cue("Chako", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("Chako", {"0099"}, "0028"),
cue("MazeI", {"0090", "0091", "0092", "0093", "0094", "0400", "0601"}, "0019"),
cue("Sand", {"0090", "0091", "0092", "0093", "0094", "0099", "0210", "0601"}, "0006"),
cue("Sand", {"0202"}, "0007"),
cue("Mimi", {"0090", "0091", "0092", "0093", "0094", "0302"}, "0009"),
cue("Mimi", {"0095", "0096", "0097", "0098", "0099"}, "0028"),
cue("Cave", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
cue("Start", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
cue("Barr", {"0090", "0091", "0092", "0093", "0094", "0402", "1001"}, "0008"),
cue("Barr", {"1000"}, "0011"),
cue("Barr", {"1000"}, "0004"),
cue("Pool", {"0090", "0091", "0092", "0093", "0094"}, "0009"),
cue("Pool", {"0095", "0096", "0097", "0098", "0099", "0410"}, "0028"),
cue("Cemet", {"0090", "0091", "0092", "0093", "0094"}, "0012"),
cue("Plant", {"0090", "0091", "0092", "0093", "0094"}, "0013"),
cue("Plant", {"0095", "0096", "0097", "0098", "0099"}, "0028"),
cue("Shelt", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0008"),
cue("Comu", {"0090", "0091", "0092", "0093", "0094"}, "0009"),
cue("Comu", {"0095", "0096", "0097", "0098", "0099"}, "0028"),
cue("Cthu", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
cue("Malco", {"0090", "0091", "0092", "0093", "0094", "0203"}, "0008"),
cue("Malco", {"0200", "0204"}, "0004"),
cue("Malco", {"0200"}, "0011"),
cue("Frog", {"0090", "0091", "0092", "0093", "0094", "1000"}, "0008"),
cue("Frog", {"0202"}, "0011"),
cue("Frog", {"0202"}, "0007"),
cue("Curly", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("Curly", {"0300"}, "0004"),
cue("WeedB", {"0302"}, "0004"),
cue("Stream", {"0090", "0091", "0092", "0093", "0094"}, "0018"),
cue("Jenka1", {"0090", "0091", "0092", "0093", "0094"}, "0019"),
cue("Gard", {"0502"}, "0017"),
cue("Gard", {"0502"}, "0018"),
cue("Gard", {"0502"}, "0004"),
cue("Jenka2", {"0090", "0091", "0092", "0093", "0095", "0200"}, "0019"),
cue("Jenka2", {"0094"}, "0011"),
cue("SandE", {"0090", "0091", "0092", "0093", "0094", "0600"}, "0006"),
cue("SandE", {"0600"}, "0011"),
cue("MazeH", {"0090", "0091", "0092", "0093", "0094"}, "0019"),
cue("MazeW", {"0090", "0091", "0092", "0093", "0094", "1000"}, "0037"),
cue("MazeW", {"0302"}, "0007"),
cue("MazeO", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("MazeD", {"0400"}, "0004"),
cue("MazeA", {"0090", "0091", "0092", "0093", "0094", "0301"}, "0008"),
cue("MazeB", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0008"),
cue("MazeS", {"0090", "0091", "0092", "0093", "0094", "0310", "0600"}, "0008"),
cue("MazeS", {"0321"}, "0011"),
cue("MazeS", {"0321"}, "0004"),
cue("MazeM", {"0090", "0091", "0092", "0093", "0094", "0301"}, "0020"),
cue("Drain", {"0090", "0091", "0092", "0093", "0094", "0150"}, "0008"),
cue("Drain", {"0095", "0096", "0097"}, "0023"),
cue("Almond", {"0090", "0091", "0092", "0093", "0094", "0361"}, "0023"),
cue("Almond", {"0452", "0500"}, "0022"),
cue("River", {"0090", "0091", "0092", "0093", "0094", "0095"}, "0038"),
cue("Eggs2", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0027"),
cue("Cthu2", {"0090", "0091", "0092", "0093", "0094"}, "0008"),
cue("EggR2", {"0090", "0091", "0092", "0093", "0094", "1000"}, "0008"),
cue("EggR2", {"0304"}, "0004"),
cue("EggX2", {"0090", "0091", "0092", "0093", "0095"}, "0014"),
cue("Oside", {"0090", "0091", "0092", "0093", "0094"}, "0025"),
cue("Oside", {"0402"}, "0026"),
cue("Itoh", {"0090", "0091", "0092", "0093", "0094", "0095"}, "0008"),
cue("Cent", {"0090", "0091", "0092", "0093", "0094"}, "0024"),
cue("Jail1", {"0090", "0091", "0092", "0093", "0094", "0220"}, "0008"),
cue("Momo", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("lounge", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("Jail2", {"0090", "0091", "0092", "0093", "0094", "0099"}, "0008"),
cue("Blcny1", {"0090", "0091", "0092", "0093", "0094"}, "0030"),
cue("Priso1", {"0090", "0091", "0092", "0093", "0094"}, "0029"),
cue("Ring1", {"0090", "0091", "0092", "0093", "0094", "0097", "0098", "0402"}, "0030"),
cue("Ring1", {"0502", "0503"}, "0007"),
cue("Ring1", {"0099"}, "0018"),
cue("Ring2", {"0090", "0091", "0092", "0093", "0094", "0098", "0420"}, "0030"),
cue("Ring2", {"0502"}, "0007"),
cue("Ring2", {"0410"}, "0031"),
cue("Prefa1", {"0090", "0091", "0092", "0093", "0094"}, "0030"),
cue("Priso2", {"0090", "0091", "0092", "0093", "0094", "0250"}, "0029"),
cue("Priso2", {"0241"}, "0004"),
cue("Ring3", {"0502"}, "0034"),
cue("Ring3", {"0502"}, "0032"),
cue("Ring3", {"0600"}, "0018"),
cue("Little", {"0090", "0091", "0092", "0093", "0094"}, "0002"),
cue("Blcny2", {"0090", "0091", "0092", "0093", "0094"}, "0018"),
cue("Blcny2", {"0400"}, "0035"),
cue("Pixel", {"0090", "0091", "0092", "0093", "0094", "0253"}, "0014"),
cue("Hell1", {"0090", "0091", "0092", "0094", "0096"}, "0036"),
cue("Hell2", {"0090", "0091", "0092", "0093", "0094"}, "0036"),
cue("Hell3", {"0090", "0091", "0092", "0093", "0094"}, "0036"),
cue("Hell3", {"0300"}, "0007"),
cue("Mapi", {"0420"}, "0004"),
cue("Ballo1", {"0095"}, "0039"),
cue("Ballo1", {"0500"}, "0004"),
cue("Ballo1", {"0900"}, "0007"),
cue("Ballo1", {"1000"}, "0032"),
cue("Pole", {"0090", "0091", "0092", "0093", "0094", "0095"}, "0008"),
cue("Ballo2", {"0500"}, "0034")
}
local music = Class:extend()
function music:new()
self.betaEnabled = false
self.songs = songs
self.cues = cues
self.flavor = "Shuffle"
end
local _isValid = function(key, song, self)
return not song.jingle and (not song.beta or self.betaEnabled)
end
function music:getShuffledSongs()
return _.shuffle(_.filter(self.songs, _isValid, self))
end
function music:getCues()
return self.cues
end
function music:shuffleMusic(tscFiles)
if self.flavor == "Shuffle" then self:_shuffle(tscFiles) end
if self.flavor == "Random" then self:_random(tscFiles) end
if self.flavor == "Chaos" then self:_chaos(tscFiles) end
end
-- SHUFFLE songs: every cue with a given song becomes the same, new song
function music:_shuffle(tscFiles)
local shuffled = self:getShuffledSongs()
local idmap = _.map(self.songs, function (k,v)
-- don't remap any invalid songs
if not _isValid(k,v,self) then return v.id, v.id end
return v.id, _.pop(shuffled).id
end)
for k,cue in pairs(self.cues) do
cue.songid = idmap[cue.songid]
end
self:writeCues(tscFiles)
end
-- RANDOMIZE songs: any cue can play any song
function music:_random(tscFiles)
for k,cue in pairs(self.cues) do
cue.songid = self:getShuffledSongs()[1].id
end
self:writeCues(tscFiles)
end
-- CHAOTICALLY RANDOMIZE songs: nearly any <CMU can play any song
function music:_chaos(tscFiles)
for k,cue in pairs(self.cues) do
for k,event in ipairs(cue.events) do
tscFiles[cue.map]:placeSongAtCue(self:getShuffledSongs()[1].id, event, cue.map, cue.default)
end
end
end
function music:writeCues(tscFiles)
for k,cue in pairs(self.cues) do
for k,event in ipairs(cue.events) do
tscFiles[cue.map]:placeSongAtCue(cue.songid, event, cue.map, cue.default)
end
end
end
return music

View file

@ -1,6 +1,7 @@
local Items = require 'database.items'
local TscFile = require 'tsc_file'
local WorldGraph = require 'database.world_graph'
local Music = require 'database.music'
local C = Class:extend()
@ -14,6 +15,12 @@ do
end
end
end
for key, cue in pairs(Music():getCues()) do
local filename = cue.map
if not _.contains(TSC_FILES, filename) then
table.insert(TSC_FILES, filename)
end
end
end
local csdirectory
@ -32,11 +39,14 @@ function C:new()
self._isCaveStoryPlus = false
self.itemDeck = Items()
self.worldGraph = WorldGraph(self.itemDeck)
self.music = Music()
self.customseed = nil
self.puppy = false
self.obj = ""
self.sharecode = ""
self.mychar = ""
self.shuffleMusic = false
end
function C:setPath(path)
@ -61,8 +71,10 @@ function C:randomize()
self:_updateSharecode(seed)
local tscFiles = self:_createTscFiles(dirStage)
-- self:_writePlaintext(tscFiles)
self:_shuffleItems(tscFiles)
if self.shuffleMusic then self.music:shuffleMusic(tscFiles) end
self:_writeModifiedData(tscFiles)
self:_writePlaintext(tscFiles)
self:_writeLog()
@ -311,6 +323,9 @@ function C:_updateSettings()
Settings.settings.spawn = self.worldGraph.spawn
Settings.settings.seqbreaks = self.worldGraph.seqbreak
Settings.settings.dboosts = _.map(self.worldGraph.dboosts, function(k,v) return v.enabled end)
Settings.settings.musicShuffle = self.shuffleMusic
Settings.settings.musicBeta = self.music.betaEnabled
Settings.settings.musicFlavor = self.music.flavor
Settings:update()
end

View file

@ -31,7 +31,10 @@ function C:getDefaults()
sisters = true,
plantation = true,
rocket = true
}
},
musicShuffle = false,
musicBeta = false,
musicFlavor = "Shuffle"
}
end
@ -48,18 +51,21 @@ function C:serialize()
return line .. "}"
end
local line = "return {\r\n "
line = line .. ("csdirectory = [[%s]],\r\n "):format(self.settings.csdirectory or "")
line = line .. ("puppy = %s,\r\n "):format(self.settings.puppy)
line = line .. ("obj = %q,\r\n "):format(self.settings.obj or "")
line = line .. ("mychar = %q,\r\n "):format(self.settings.mychar or "")
line = line .. ("spawn = %q,\r\n "):format(self.settings.spawn or "")
local line = "return {\r\n"
local tab = " "
line = line .. tab .. ("csdirectory = [[%s]],\r\n"):format(self.settings.csdirectory or "")
line = line .. tab .. ("puppy = %s,\r\n"):format(self.settings.puppy)
line = line .. tab .. ("obj = %q,\r\n"):format(self.settings.obj or "")
line = line .. tab .. ("mychar = %q,\r\n"):format(self.settings.mychar or "")
line = line .. tab .. ("spawn = %q,\r\n"):format(self.settings.spawn or "")
local dboost = dboosts()
line = line .. ("seqbreaks = %s,\r\n "):format(self.settings.seqbreaks)
line = line .. ("dboosts = %s,\r\n "):format(dboost)
line = line .. tab .. ("seqbreaks = %s,\r\n"):format(self.settings.seqbreaks)
line = line .. tab .. ("dboosts = %s,\r\n"):format(dboost)
line = line .. tab .. ("musicShuffle = %s,\r\n"):format(self.settings.musicShuffle)
line = line .. tab .. ("musicBeta = %s,\r\n"):format(self.settings.musicBeta)
line = line .. tab .. ("musicFlavor = %q\r\n"):format(self.settings.musicFlavor)
return line .. "\r\n}"
return line .. "}"
end
function C:getSettings()

View file

@ -19,20 +19,6 @@ function C:new(path)
assert(file:close())
assert(file:release())
-- Determine set of items which can be replaced later.
--[[
self._unreplaced = {}
self._mapName = path:match("^.+/(.+)$")
for k, v in pairs(ITEM_DATA) do repeat
if (v.map .. '.tsc') ~= self._mapName then
break -- continue
end
local item = _.clone(v)
table.insert(self._unreplaced, item)
until true end
self._unreplaced = _.shuffle(self._unreplaced)
]]
end
function C:hasUnreplacedItems()
@ -48,103 +34,51 @@ function C:placeItemAtLocation(item, location)
end
end
function C:replaceItem(replacement)
assert(self:hasUnreplacedItems())
local key = self._unreplaced[#self._unreplaced].key
self:replaceSpecificItem(key, replacement)
function C:placeSongAtCue(songid, event, map, originalid)
local wasChanged
self._text, wasChanged = self:_stringReplace(self._text, "<CMU" .. originalid, "<CMU" .. songid, event, {"<CMU0015", "<CMU0000"})
if not wasChanged then
local template = "Unable to replace [%s] event #%s's music cue with %q."
logWarning(template:format(map, event, songid))
end
end
function C:replaceSpecificItem(originalKey, replacement)
-- Fetch item with key matching originalKey.
local original
for index, item in ipairs(self._unreplaced) do
if item.key == originalKey then
original = item
table.remove(self._unreplaced, index)
break
function C:_stringReplace(text, needle, replacement, label, overrides)
overrides = overrides or {}
local pStart, pEnd = self:_getLabelPositionRange(label)
local i, o = -1, -1
while(o <= i) do
o = nil
i = text:find(needle, pStart)
if i == nil then
logDebug(('Unable to replace "%s" with "%s"'):format(needle, replacement))
return text, false
elseif i > pEnd then
-- This is totally normal and can be ignored.
logDebug(('Found "%s", but was outside of label.'):format(needle, replacement))
return text, false
end
end
assert(original, 'No unreplaced item with key: ' .. originalKey)
-- Log change
local template = "[%s] %s -> %s"
logNotice(template:format(self._mapName, original.name, replacement.name))
-- Replace before.
if original.replaceBefore then
for needle, replacement in pairs(original.replaceBefore) do
local wasChanged
self._text, wasChanged = self:_stringReplace(self._text, needle, replacement, original.label)
-- Log error if replace was not optional.
if wasChanged == false then
local wasOptional = false
for _, pattern in ipairs(OPTIONAL_REPLACES) do
if needle:find(pattern, 1, true) then
wasOptional = true
break
end
end
if wasOptional == false then
local template = 'Unable to replace [%s] "%s" with "%s".'
logError(template:format(original.map, needle, replacement))
-- find the earliest occurence of an override
for k,v in ipairs(overrides) do
local over = text:find(v, pStart)
if over ~= nil then
if o ~= nil then
o = math.min(o, over)
else
o = over
end
end
end
-- no overrides found
if o == nil then break end
pStart = o+1
end
-- Replace attributes.
self:_replaceAttribute(original, replacement, 'command')
self:_replaceAttribute(original, replacement, 'getText')
self:_replaceAttribute(original, replacement, 'displayCmd')
self:_replaceAttribute(original, replacement, 'music')
end
function C:_replaceAttribute(original, replacement, attribute)
local originalTexts = original[attribute]
if originalTexts == nil or originalTexts == '' then
return
elseif type(originalTexts) == 'string' then
originalTexts = {originalTexts}
end
local replaceText = replacement[attribute] or ''
if type(replaceText) == 'table' then
replaceText = replaceText[1]
end
-- Fix: After collecting Curly's Panties or Chako's Rouge, music would go silent.
if attribute == 'music' and replaceText == '' then
replaceText = "<CMU0010"
end
-- Loop through each possible original value until we successfully replace one.
for _, originalText in ipairs(originalTexts) do repeat
if originalText == "" then
break -- continue
end
local changed
self._text, changed = self:_stringReplace(self._text, originalText, replaceText, original.label)
if changed then
return
end
until true end
local logMethod = (attribute == 'command') and logError or logWarning
local template = 'Unable to replace original "%s" for [%s] %s.'
logMethod(template:format(attribute, original.map, original.name))
end
function C:_stringReplace(text, needle, replacement, label)
local pStart, pEnd = self:_getLabelPositionRange(label)
local i = text:find(needle, pStart)
if i == nil then
logNotice(('Unable to replace "%s" with "%s"'):format(needle, replacement))
return text, false
elseif i > pEnd then
-- This is totally normal and can be ignored.
logNotice(('Found "%s", but was outside of label.'):format(needle, replacement))
return text, false
end
local len = needle:len()
local j = i + len - 1
assert((i % 1 == 0) and (i > 0) and (i <= j), tostring(i))
@ -170,7 +104,7 @@ function C:_getLabelPositionRange(label)
assert(type(label) == 'string')
assert(#label == 4)
assert(tonumber(label) >= 1)
assert(tonumber(label) >= 0)
assert(tonumber(label) <= 9999)
local i = 1

View file

@ -7,6 +7,7 @@ local C = Class:extend()
local layout = Luigi(require 'ui.main')
local settings = Luigi(require 'ui.settings')
local sequence = Luigi(require 'ui.sequence')
local music = Luigi(require 'ui.music')
local style = require 'ui.style'
local theme = require 'lib.luigi.theme.dark'
@ -14,10 +15,12 @@ local theme = require 'lib.luigi.theme.dark'
layout:setStyle(style)
settings:setStyle(style)
sequence:setStyle(style)
music:setStyle(style)
layout:setTheme(theme)
settings:setTheme(theme)
sequence:setTheme(theme)
music:setTheme(theme)
function C:setup()
self:loadPuppy(Settings.settings.puppy)
@ -25,6 +28,7 @@ function C:setup()
self:loadMyChar(Settings.settings.mychar)
self:loadSpawn(Settings.settings.spawn)
self:loadSeqSettings(Settings.settings.seqbreaks, Settings.settings.dboosts)
self:loadMusicSettings(Settings.settings.musicShuffle, Settings.settings.musicBeta, Settings.settings.musicFlavor)
background = lg.newImage('assets/background.png')
self:draw()
@ -100,12 +104,28 @@ function C:loadSeqSettings(breaks, seq)
end
end
function C:loadMusicSettings(shuffle, beta, flavor)
settings.music.value = shuffle
music.beta.value = beta
if flavor == "Shuffle" then music.shuffle.value = true end
if flavor == "Random" then music.random.value = true end
if flavor == "Chaos" then music.random.value = true end
end
layout.version.text = 'Cave Story Randomizer [Open Mode] v' .. VERSION
layout.author.text = 'by shru and duncathan'
layout.twitter.text = '(@shruuu and @duncathan_salt)'
layout.footer.text = 'Original randomizer:\r\nshru.itch.io/cave-story-randomizer'
music.panel.text = [[Shuffle: remap every song to a new song. For example, all instances of Mischievous Robot become Pulse. Songs may remap to themselves.
Random: remap every cue to a new song. For example, entering the Egg Corridor by any means plays Meltdown 2.
Chaos: remap every <CMU to a new song. For example, teleporting to the Egg Corridor plays Charge, but entering Egg Corridor from Cthulhu's Abode plays Run!
Beta music: include Wind Fortress, Halloween 2, People of the Root, Pier Walk, and Snoopy Cake in the potential songs. Only compatible with the included Doukutsu.exe - no other platforms.]]
layout.go:onPress(function()
Randomizer:new()
@ -129,6 +149,12 @@ layout.go:onPress(function()
Randomizer.worldGraph.dboosts.plantation.enabled = sequence.plantation.value
Randomizer.worldGraph.dboosts.rocket.enabled = sequence.rocket.value
Randomizer.shuffleMusic = settings.music.value
Randomizer.music.betaEnabled = music.beta.value
if music.shuffle.value then Randomizer.music.flavor = "Shuffle" end
if music.random.value then Randomizer.music.flavor = "Random" end
if music.chaos.value then Randomizer.music.flavor = "Chaos" end
C:setStatus(Randomizer:randomize())
layout.sharecode.text = "Copy Sharecode"
@ -153,6 +179,11 @@ settings.seqButton:onPress(function()
settings:hide()
end)
settings.musicButton:onPress(function()
music:show()
settings:hide()
end)
sequence.allOn:onPress(function()
Screen:loadSeqSettings(nil, _.map(Settings.settings.dboosts, function(k,v) return true end))
end)
@ -166,6 +197,11 @@ sequence.close:onPress(function()
settings:show()
end)
music.close:onPress(function()
music:hide()
settings:show()
end)
layout.sharecode:onPress(function()
if Randomizer.sharecode ~= "" then
love.system.setClipboardText(Randomizer.sharecode)

28
src/ui/music.lua Normal file
View file

@ -0,0 +1,28 @@
return { style = 'dialog',
{ style = 'dialogHead', text = 'Music Settings' },
{ style = 'dialogBody', padding = 24, flow = 'x',
{
{ type = 'label', text = 'Type of randomization' },
{ type = 'radio', group = 'music', text = 'Shuffle', id = 'shuffle', minheight = 27 },
{ type = 'radio', group = 'music', text = 'Random', id = 'random', minheight = 27 },
{ type = 'radio', group = 'music', text = 'Chaos', id = 'chaos', minheight = 27 },
{ type = 'label', text = 'Other settings' },
{ type = 'check', id = 'beta', value = false, text = 'Enable beta music', minheight = 27 },
},
{
{
align = 'top center',
wrap = true,
id = 'panel',
width = 275,
scroll = true
},
{ height = 10 },
{ type = 'label', text = '[scroll for more]', align = 'center', width = 275, minheight = 27 }
}
},
{ style = 'dialogFoot',
{},
{ style = 'dialogButton', id = 'close', text = 'Close' }
}
}

View file

@ -16,12 +16,16 @@ return { style = 'dialog',
},
{ type = 'label', text = 'Randomization Options', minheight = 32 },
{ type = 'check', value = false, id = 'puppy', text = "Puppysanity", minheight = 27 },
{ flow = 'x',
{ flow = 'x', height = 32,
{ type = 'check', value = false, id = 'seqbreak', text = "Sequence breaks", minheight = 32, width = 170},
{ type = 'button', style = 'dialogButton', text = "Modify", id = 'seqButton', width = 70, align = 'center' },
{ width = false }
},
{ height = 'auto' },
{ flow = 'x', height = 32,
{ type = 'check', value = false, id = 'music', text = "Randomize music", minheight = 32, width = 170},
{ type = 'button', style = 'dialogButton', text = "Modify", id = 'musicButton', width = 70, align = 'center' },
{ width = false }
},
},
{
{ type = 'label', text = 'Objective', minheight = 32 },