diff --git a/pre-edited-cs/data/Stage/Hell3.tsc b/pre-edited-cs/data/Stage/Hell3.tsc index 74358e1..f25d55e 100644 --- a/pre-edited-cs/data/Stage/Hell3.tsc +++ b/pre-edited-cs/data/Stage/Hell3.tsc @@ -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 \ No newline at end of file +>;Taaja>;m~rmt~�aadgmwrzaaaamvu>;Taajb>;m~rmt~�aadgmwrzaaabmvu>;Taajc>;m~rmt~�aadgmwrzaaacmvu>;Taajd>;m~rmt~�aadgmwrzaaadmvu>;Taaje>;m~rmt~�aadgmwrzaaaemvu>;Taajf>;m|v�>;mw}{bfdckacaamwrzaaad>;mw}{bffakacabmvu>;Taajg>;m|v�m~�saaacm�rzaaacm��zmwrzaaabmvu>;Tabaa>;m��zmwr�aaabm��raaibkaajfkabfbkaabd>;Tabba>;m��zmw�~aabgmwr�aaad>;m�rzaada>;mw}{bfdekabbb>;m��raaiekaajfkaabakaaac>;Tabbb>;m��raaifkaajfkaabakaaac>;Tacaa>;mt�acaakadcakaaaa>;mw}^bfdcmw}\bfdd>;m�rzaaacmwrzaaadmw}{bffakacabmvu>;Tacab>;mu�acab>;mt~�aaahkaabekaaab>;mt~�aaaikaabekaaab>;mt~�aaajkaabekaaab>;mt~�aabakaabekaaab>;mt~�aabbkaabekaaab>;mt~�aabckaabekaaab>;mt~�aabdkaabekaaabmvu>;Tacea>;Taceb>;mu�acebmr�aceakaabakaaadmvu>;Tacec>;Taced>;mu�acedmr�aceckaabakaaadmvu>;Tacee>;Tacef>;mu�acefmr�aceekaabakaaadmvu>;Tacfa>;Tacfb>;mu�acfbmr�acfakaa1akaaabmvu>;Tacfc>;Tacfd>;mu�acfdmr�acfckaabakaaadmvu>;Tacfe>;Tacff>;mu�acffmr�acfekaabakaaabmvu>;Tacfg>;Tacfh>;mu�acfhmr�acfgkaabakaaadmvu>;Tacja>;mu�acjam���aabc>;mt~�abbhkaaajkaaci>;mt~�abbhkaabakaaci>;mt~�abbhkaabbkaaci>;mvu>;Tacjb>;mu�acjbm���aabc>;mt~�aajfkaaadkaaci>;mt~�aajfkaaaekaaci>;mt~�aajfkaaafkaaci>;mt~�aajfkaaagkaaci>;mvu>;Tacjc>;mu�acjcm���aabc>;mt~�aaaakaaaakaaci>;mt~�aaaakaaaakaaci>;mt~�aaaakaaaakaaci>;mvu>;Tadaa>;mu�adaamuradajmuradbbmuradbh>;mw�adabkaabg>;mt~�aaahms�}aaaa>;mt~�aacakaabbkaaab>;mt~�aacakaabckaaab>;ms�rabaa>;mt�baabkaddakaaaa>;mt�baackaddakaaac>;m���aabc>;mvu>;Tadab>;Tadac>;m~��aabikaabamvu>;Tadja>;m��zmw}{bffbkaaabmw}\bffb>;m���aaccmt�adjakaacbkaaaamv�vaeaa>;Taeaa>;mv�vaadf>;Tajaa>;mu�ajaamvu>;Tajab>;mu�ajabmvu>;Tbaaa>;ms�rafaamt~�aaaamw}^bffamvu>;Tbaab>;Tbaac \ No newline at end of file diff --git a/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt b/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt index 6535f85..8d21f51 100644 --- a/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt +++ b/pre-edited-cs/data/Stage/ScriptSource/Hell3.txt @@ -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 diff --git a/src/database/music.lua b/src/database/music.lua new file mode 100644 index 0000000..1448b0a --- /dev/null +++ b/src/database/music.lua @@ -0,0 +1,227 @@ +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 +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 + +-- 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:randomize(tscFiles) + for k,cue in pairs(self.cues) do + cue.songid = self:getShuffledSongs()[1].id + end + self:writeCues(tscFiles) +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 \ No newline at end of file diff --git a/src/randomizer.lua b/src/randomizer.lua index cca714c..ab093b7 100644 --- a/src/randomizer.lua +++ b/src/randomizer.lua @@ -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,6 +39,8 @@ 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 = "" @@ -61,8 +70,10 @@ function C:randomize() self:_updateSharecode(seed) local tscFiles = self:_createTscFiles(dirStage) - -- self:_writePlaintext(tscFiles) + self:_shuffleItems(tscFiles) + self.music:randomize(tscFiles) + self:_writeModifiedData(tscFiles) self:_writePlaintext(tscFiles) self:_writeLog() diff --git a/src/tsc_file.lua b/src/tsc_file.lua index 6a56b89..2213f51 100644 --- a/src/tsc_file.lua +++ b/src/tsc_file.lua @@ -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