From 85ca484675b18225361eee387ba0f87dae3e5c8c Mon Sep 17 00:00:00 2001 From: Endaris Date: Fri, 27 Dec 2024 23:52:53 +0100 Subject: [PATCH 01/10] initial draft for SfxGroup file matching --- client/src/FileUtils.lua | 6 +-- client/src/music/SfxGroup.lua | 71 ++++++++++++++++++++++++++++++++++ client/tests/SfxGroupTests.lua | 36 +++++++++++++++++ 3 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 client/src/music/SfxGroup.lua create mode 100644 client/tests/SfxGroupTests.lua diff --git a/client/src/FileUtils.lua b/client/src/FileUtils.lua index 323a85ff..0d5aeab2 100644 --- a/client/src/FileUtils.lua +++ b/client/src/FileUtils.lua @@ -111,10 +111,10 @@ function fileUtils.readJsonFile(file) end end -local SUPPORTED_SOUND_FORMATS = {".mp3", ".ogg", ".wav", ".it", ".flac"} +fileUtils.SUPPORTED_SOUND_FORMATS = {".mp3", ".ogg", ".wav", ".it", ".flac"} --returns a source, or nil if it could not find a file function fileUtils.loadSoundFromSupportExtensions(path_and_filename, streamed) - for k, extension in ipairs(SUPPORTED_SOUND_FORMATS) do + for k, extension in ipairs(fileUtils.SUPPORTED_SOUND_FORMATS) do if love.filesystem.getInfo(path_and_filename .. extension) then return love.audio.newSource(path_and_filename .. extension, streamed and "stream" or "static") end @@ -136,7 +136,7 @@ function fileUtils.findSound(sound_name, dirs_to_check, streamed) end function fileUtils.soundFileExists(soundName, path) - for _, extension in pairs(SUPPORTED_SOUND_FORMATS) do + for _, extension in pairs(fileUtils.SUPPORTED_SOUND_FORMATS) do if love.filesystem.getInfo(path .. "/" .. soundName .. extension, "file") then return true end diff --git a/client/src/music/SfxGroup.lua b/client/src/music/SfxGroup.lua new file mode 100644 index 00000000..c08bbbd4 --- /dev/null +++ b/client/src/music/SfxGroup.lua @@ -0,0 +1,71 @@ +local class = require("common.lib.class") +local tableUtils = require("common.lib.tableUtils") +local FileUtils = require("client.src.FileUtils") + +---@class SfxGroup +---@field path string +---@field pattern string The pattern to search for as plain text; luaregex disabled +---@field separator string the separator by which alternative copies separate their index, default "" +---@field matchingFiles string[] the files that got matched during the load process of the SfxGroup +local SfxGroup = class( +function(self, path, pattern, separator) + self.path = path + self.pattern = pattern + self.separator = separator or "" +end) + +function SfxGroup.getMatchingFiles(files, pattern, separator) + local stringLen = string.len(pattern) + local matchedFiles = tableUtils.filter(files, + function(file) + local startIndex, endIndex = string.find(file, pattern, nil, true) + if not startIndex then + return false + elseif startIndex > 1 then + -- this means the name is prefixed with something else + return false + else + local goodExtension + -- this check is doubly good because it enforces lower case extensions even on windows + for i, extension in ipairs(FileUtils.SUPPORTED_SOUND_FORMATS) do + local length = extension:len() + if file:sub(-length) == extension then + goodExtension = extension + break + end + end + if not goodExtension then + return false + else + -- now check for actual exact matching: + local middlePart = file:sub(- goodExtension:len()):sub(1, stringLen) + if middlePart:len() == 0 then + -- this is just the exact pattern + file extension + return true + else + local sepLen = separator:len() + if middlePart:sub(sepLen) ~= separator then + return false + else + local numberPart = middlePart:sub(sepLen + 1) + if string.match(numberPart, "%d+") == numberPart and tonumber(numberPart) then + -- there are really only digits that form a number in the number part + return true + else + return false + end + end + end + end + end + end) + + return matchedFiles +end + +function SfxGroup:load(yields) + local files = FileUtils.getFilteredDirectoryItems(self.path, "file") + self.matchingFiles = SfxGroup.getMatchingFiles(files, self.pattern, self.separator) +end + +return SfxGroup \ No newline at end of file diff --git a/client/tests/SfxGroupTests.lua b/client/tests/SfxGroupTests.lua new file mode 100644 index 00000000..f256ce5f --- /dev/null +++ b/client/tests/SfxGroupTests.lua @@ -0,0 +1,36 @@ +local SfxGroup = require("client.src.music.SfxGroup") +local FileUtils = require("client.src.FileUtils") +local tableUtils = require("common.lib.tableUtils") + +local files = { + "chain2.ogg", + "chain3.wav", + "chain4.WAV", + "chain5.mp3", +} + +local matchingGroup1 = { + "chain2.ogg", + "chain3.wav", + "chain5.mp3", +} + +local matchingGroup2 = { + +} + + +local function testFileMatching1() + local pattern = "chain" + local separator = "" + local matchingFiles = SfxGroup.getMatchingFiles(files, pattern, separator) + + for _, matched in ipairs(matchingFiles) do + assert(tableUtils.contains(matchingGroup1, matched), "Unexpectedly matched " .. matched) + end + for _, control in ipairs(matchingFiles) do + assert(tableUtils.contains(matchingGroup1, control), "Expected the matching files to also match " .. control) + end +end + +testFileMatching1() \ No newline at end of file From ed3dd88f936d6d7fd8a495ab3f2cb7823ac2d7cb Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 00:23:11 +0100 Subject: [PATCH 02/10] add test cases and fix file matching --- client/src/music/SfxGroup.lua | 7 ++-- client/tests/SfxGroupTests.lua | 59 ++++++++++++++++++++++++++++++---- testLauncher.lua | 1 + 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/client/src/music/SfxGroup.lua b/client/src/music/SfxGroup.lua index c08bbbd4..59e78bb1 100644 --- a/client/src/music/SfxGroup.lua +++ b/client/src/music/SfxGroup.lua @@ -38,13 +38,16 @@ function SfxGroup.getMatchingFiles(files, pattern, separator) return false else -- now check for actual exact matching: - local middlePart = file:sub(- goodExtension:len()):sub(1, stringLen) + -- first cut off the matching part + local middlePart = file:sub(stringLen + 1) + -- and then the extension + middlePart = middlePart:sub(1, - goodExtension:len() - 1) if middlePart:len() == 0 then -- this is just the exact pattern + file extension return true else local sepLen = separator:len() - if middlePart:sub(sepLen) ~= separator then + if middlePart:sub(1, sepLen) ~= separator then return false else local numberPart = middlePart:sub(sepLen + 1) diff --git a/client/tests/SfxGroupTests.lua b/client/tests/SfxGroupTests.lua index f256ce5f..5bcd7c85 100644 --- a/client/tests/SfxGroupTests.lua +++ b/client/tests/SfxGroupTests.lua @@ -5,32 +5,77 @@ local tableUtils = require("common.lib.tableUtils") local files = { "chain2.ogg", "chain3.wav", + -- upper case file extension "chain4.WAV", "chain5.mp3", + -- 6e2 is a number for lua + "chain6e2.mp3", + -- 7.7 is a number for lua + "chain7.7.mp3", + -- leading 0 + "chain08.ogg", + -- prefix + "mychain9.ogg", + -- suffix + "chain10.ogg.bak", + -- string throwin + "chain08alt.ogg", + -- with separator + "chain2_2.ogg", + "chain2_3.ogg", + "chain2_04.ogg", + "chain3_2.ogg", + -- extra number before separator + "chain22_2.ogg" } local matchingGroup1 = { "chain2.ogg", "chain3.wav", "chain5.mp3", + "chain08.ogg" } local matchingGroup2 = { } - -local function testFileMatching1() - local pattern = "chain" - local separator = "" +local function testFileMatching(pattern, separator, controlGroup) local matchingFiles = SfxGroup.getMatchingFiles(files, pattern, separator) for _, matched in ipairs(matchingFiles) do assert(tableUtils.contains(matchingGroup1, matched), "Unexpectedly matched " .. matched) end - for _, control in ipairs(matchingFiles) do - assert(tableUtils.contains(matchingGroup1, control), "Expected the matching files to also match " .. control) + for _, control in ipairs(controlGroup) do + assert(tableUtils.contains(matchingFiles, control), "Expected the matching files to also match " .. control) end end -testFileMatching1() \ No newline at end of file +local function testFileMatching1() + local pattern = "chain" + local separator = "" + local expected = { + "chain2.ogg", + "chain3.wav", + "chain5.mp3", + "chain08.ogg", + } + + testFileMatching(pattern, separator, expected) +end + +local function testfileMatching2() + local pattern = "chain2" + local separator = "_" + local expected = { + "chain2.ogg", + "chain2_2.ogg", + "chain2_3.ogg", + "chain2_04.ogg", + } + + testFileMatching(pattern, separator, expected) +end + +testFileMatching1() +testfileMatching2() \ No newline at end of file diff --git a/testLauncher.lua b/testLauncher.lua index 670a0036..1c9cc4ef 100644 --- a/testLauncher.lua +++ b/testLauncher.lua @@ -31,6 +31,7 @@ function love.load() end local tests = { + "client.tests.SfxGroupTests", "client.tests.ModControllerTests", "common.tests.engine.StackRollbackReplayTests", "client.tests.QueueTests", From 348bbbb8c1c4d949ed6160dff6f6abbbd586f635 Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 14:46:24 +0100 Subject: [PATCH 03/10] move getMatchingFiles function to FileUtils --- client/src/FileUtils.lua | 53 ++++++++++++++++++++++++++++++++++ client/tests/SfxGroupTests.lua | 16 ++-------- 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/client/src/FileUtils.lua b/client/src/FileUtils.lua index 0d5aeab2..a4dff82b 100644 --- a/client/src/FileUtils.lua +++ b/client/src/FileUtils.lua @@ -1,5 +1,6 @@ local logger = require("common.lib.logger") local Replay = require("common.data.Replay") +local tableUtils = require("common.lib.tableUtils") local PREFIX_OF_IGNORED_DIRECTORIES = "__" @@ -182,4 +183,56 @@ function fileUtils.saveReplay(replay) ) end +function fileUtils.getMatchingFiles(files, pattern, extensionList, separator) + local stringLen = string.len(pattern) + local matchedFiles = tableUtils.filter(files, + function(file) + local startIndex, endIndex = string.find(file, pattern, nil, true) + if not startIndex then + return false + elseif startIndex > 1 then + -- this means the name is prefixed with something else + return false + else + local goodExtension + -- this check is doubly good because it enforces lower case extensions even on windows + for i, extension in ipairs(extensionList) do + local length = extension:len() + if file:sub(-length) == extension then + goodExtension = extension + break + end + end + if not goodExtension then + return false + else + -- now check for actual exact matching: + -- first cut off the matching part + local middlePart = file:sub(stringLen + 1) + -- and then the extension + middlePart = middlePart:sub(1, - goodExtension:len() - 1) + if middlePart:len() == 0 then + -- this is just the exact pattern + file extension + return true + else + local sepLen = separator:len() + if middlePart:sub(1, sepLen) ~= separator then + return false + else + local numberPart = middlePart:sub(sepLen + 1) + if string.match(numberPart, "%d+") == numberPart and tonumber(numberPart) then + -- there are really only digits that form a number in the number part + return true + else + return false + end + end + end + end + end + end) + + return matchedFiles +end + return fileUtils \ No newline at end of file diff --git a/client/tests/SfxGroupTests.lua b/client/tests/SfxGroupTests.lua index 5bcd7c85..cf56ccd5 100644 --- a/client/tests/SfxGroupTests.lua +++ b/client/tests/SfxGroupTests.lua @@ -1,4 +1,3 @@ -local SfxGroup = require("client.src.music.SfxGroup") local FileUtils = require("client.src.FileUtils") local tableUtils = require("common.lib.tableUtils") @@ -29,22 +28,11 @@ local files = { "chain22_2.ogg" } -local matchingGroup1 = { - "chain2.ogg", - "chain3.wav", - "chain5.mp3", - "chain08.ogg" -} - -local matchingGroup2 = { - -} - local function testFileMatching(pattern, separator, controlGroup) - local matchingFiles = SfxGroup.getMatchingFiles(files, pattern, separator) + local matchingFiles = FileUtils.getMatchingFiles(files, pattern, separator) for _, matched in ipairs(matchingFiles) do - assert(tableUtils.contains(matchingGroup1, matched), "Unexpectedly matched " .. matched) + assert(tableUtils.contains(controlGroup, matched), "Unexpectedly matched " .. matched) end for _, control in ipairs(controlGroup) do assert(tableUtils.contains(matchingFiles, control), "Expected the matching files to also match " .. control) From 096d570bb389b0451ec9724c420d71c86d77f42c Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 14:47:46 +0100 Subject: [PATCH 04/10] move test file --- client/tests/{SfxGroupTests.lua => FileUtilsTests.lua} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename client/tests/{SfxGroupTests.lua => FileUtilsTests.lua} (100%) diff --git a/client/tests/SfxGroupTests.lua b/client/tests/FileUtilsTests.lua similarity index 100% rename from client/tests/SfxGroupTests.lua rename to client/tests/FileUtilsTests.lua From 723ba267bfa1795fdd10ce2127031c183b2d7fbc Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 15:50:46 +0100 Subject: [PATCH 05/10] use file name matching routine for loading single panels and add tests + some cleanup --- client/src/FileUtils.lua | 48 +++++++++++++++++++----- client/src/graphics/graphics_util.lua | 8 ++-- client/src/mods/Panels.lua | 7 +--- client/src/music/SfxGroup.lua | 54 +-------------------------- client/tests/FileUtilsTests.lua | 35 ++++++++++++++--- testLauncher.lua | 2 +- 6 files changed, 74 insertions(+), 80 deletions(-) diff --git a/client/src/FileUtils.lua b/client/src/FileUtils.lua index a4dff82b..14f1610f 100644 --- a/client/src/FileUtils.lua +++ b/client/src/FileUtils.lua @@ -7,9 +7,14 @@ local PREFIX_OF_IGNORED_DIRECTORIES = "__" -- Collection of functions for file operations local fileUtils = {} +fileUtils.SUPPORTED_IMAGE_FORMATS = {".png", ".jpg", ".jpeg"} +fileUtils.SUPPORTED_SOUND_FORMATS = {".mp3", ".ogg", ".wav", ".it", ".flac"} + -- returns the directory items with a default filter and an optional filetype filter -- by default, filters out everything starting with __ and Mac's .DS_Store file -- optionally the result can be filtered to return only "file" or "directory" items +---@param path string +---@param fileType ("file" | "directory")? function fileUtils.getFilteredDirectoryItems(path, fileType) local results = {} @@ -89,6 +94,9 @@ function fileUtils.recursiveRemoveFiles(folder, targetName) end end +-- returns the table for the deserialized json at the specified file path +---@param file string +---@return table? # nil if the file could not be read or deserialization failed function fileUtils.readJsonFile(file) if not love.filesystem.getInfo(file, "file") then logger.debug("No file at specified path " .. file) @@ -106,13 +114,13 @@ function fileUtils.readJsonFile(file) logger.error("Error reading " .. file .. ":\n" .. errorMsg .. ":\n" .. fileContent) return nil else + ---@cast value table return value end end end end -fileUtils.SUPPORTED_SOUND_FORMATS = {".mp3", ".ogg", ".wav", ".it", ".flac"} --returns a source, or nil if it could not find a file function fileUtils.loadSoundFromSupportExtensions(path_and_filename, streamed) for k, extension in ipairs(fileUtils.SUPPORTED_SOUND_FORMATS) do @@ -170,20 +178,23 @@ function fileUtils.saveTextureToFile(texture, filePath, format) love.filesystem.write(filePath .. "." .. format, data) end +---@param replay Replay function fileUtils.saveReplay(replay) local path = replay:generatePath("/") local filename = replay:generateFileName() - local replayJson = json.encode(replay) + -- TODO: This is for legacy support of the replay browser only; + -- as Replay is a common.data object, client should not use it to write client specific fields Replay.lastPath = path - pcall( - function() - love.filesystem.createDirectory(path) - love.filesystem.write(path .. "/" .. filename .. ".json", replayJson) - end - ) + fileUtils.writeJson(path .. "/" .. filename .. ".json", replay) end -function fileUtils.getMatchingFiles(files, pattern, extensionList, separator) +---@param files string[] +---@param pattern string +---@param validExtensions string[] +---@param separator string? +---@return string[] # files filtered down to only strings matching the specified pattern, separator and valid extension list +function fileUtils.getMatchingFiles(files, pattern, validExtensions, separator) + separator = separator or "" local stringLen = string.len(pattern) local matchedFiles = tableUtils.filter(files, function(file) @@ -196,7 +207,7 @@ function fileUtils.getMatchingFiles(files, pattern, extensionList, separator) else local goodExtension -- this check is doubly good because it enforces lower case extensions even on windows - for i, extension in ipairs(extensionList) do + for i, extension in ipairs(validExtensions) do local length = extension:len() if file:sub(-length) == extension then goodExtension = extension @@ -220,6 +231,7 @@ function fileUtils.getMatchingFiles(files, pattern, extensionList, separator) return false else local numberPart = middlePart:sub(sepLen + 1) + -- we need to string.match on top of casting tonumber because of Lua accepting scientific notation strings as numbers if string.match(numberPart, "%d+") == numberPart and tonumber(numberPart) then -- there are really only digits that form a number in the number part return true @@ -235,4 +247,20 @@ function fileUtils.getMatchingFiles(files, pattern, extensionList, separator) return matchedFiles end +---@param path string +---@param data string +function fileUtils.write(path, data) + love.filesystem.createDirectory(path) + local success, message = love.filesystem.write(path, data) + if not success then + error("Failed to write to " .. path .. " : " .. message) + end +end + +function fileUtils.writeJson(path, tab) + local encoded = json.encode(tab) + ---@cast encoded string # json.encode always returns a string if not called with a second argument + fileUtils.write(path, encoded) +end + return fileUtils \ No newline at end of file diff --git a/client/src/graphics/graphics_util.lua b/client/src/graphics/graphics_util.lua index 81f8c835..4eb3051e 100644 --- a/client/src/graphics/graphics_util.lua +++ b/client/src/graphics/graphics_util.lua @@ -1,5 +1,6 @@ local consts = require("common.engine.consts") local logger = require("common.lib.logger") +local FileUtils = require("client.src.FileUtils") -- Utility methods for drawing local GraphicsUtil = { @@ -62,7 +63,7 @@ function GraphicsUtil.privateLoadImageWithExtensionAndScale(pathAndName, extensi end return result end - + logger.error("Error loading image: " .. fileName .. " Check it is valid and try resaving it in an image editor. If you are not the owner please get them to update it or download the latest version.") result = GraphicsUtil.privateLoadImageWithExtensionAndScale("themes/Panel Attack/transparent", ".png", 1) assert(result ~= next) @@ -72,10 +73,9 @@ function GraphicsUtil.privateLoadImageWithExtensionAndScale(pathAndName, extensi return nil end +local supportedScales = {3, 2, 1} function GraphicsUtil.loadImageFromSupportedExtensions(pathAndName) - local supportedImageFormats = {".png", ".jpg", ".jpeg"} - local supportedScales = {3, 2, 1} - for _, extension in ipairs(supportedImageFormats) do + for _, extension in ipairs(FileUtils.SUPPORTED_IMAGE_FORMATS) do for _, scale in ipairs(supportedScales) do local image = GraphicsUtil.privateLoadImageWithExtensionAndScale(pathAndName, extension, scale) if image then diff --git a/client/src/mods/Panels.lua b/client/src/mods/Panels.lua index c9022197..fa83adf9 100644 --- a/client/src/mods/Panels.lua +++ b/client/src/mods/Panels.lua @@ -235,12 +235,7 @@ function Panels:loadSingles() for color = 1, 8 do images[color] = {} - local files = tableUtils.filter(panelFiles, function(f) - local start, finish = string.find(f, "panel" .. color .. "%d+%.") - local lastDotIndex = string.find(f, "%.") - -- only add them if they aren't pre- or postfixed in some way - return start == 1 and finish == lastDotIndex - end) + local files = fileUtils.getMatchingFiles(panelFiles, "panel" .. color, fileUtils.SUPPORTED_IMAGE_FORMATS) local indexToFile = {} diff --git a/client/src/music/SfxGroup.lua b/client/src/music/SfxGroup.lua index 59e78bb1..de67612a 100644 --- a/client/src/music/SfxGroup.lua +++ b/client/src/music/SfxGroup.lua @@ -14,61 +14,9 @@ function(self, path, pattern, separator) self.separator = separator or "" end) -function SfxGroup.getMatchingFiles(files, pattern, separator) - local stringLen = string.len(pattern) - local matchedFiles = tableUtils.filter(files, - function(file) - local startIndex, endIndex = string.find(file, pattern, nil, true) - if not startIndex then - return false - elseif startIndex > 1 then - -- this means the name is prefixed with something else - return false - else - local goodExtension - -- this check is doubly good because it enforces lower case extensions even on windows - for i, extension in ipairs(FileUtils.SUPPORTED_SOUND_FORMATS) do - local length = extension:len() - if file:sub(-length) == extension then - goodExtension = extension - break - end - end - if not goodExtension then - return false - else - -- now check for actual exact matching: - -- first cut off the matching part - local middlePart = file:sub(stringLen + 1) - -- and then the extension - middlePart = middlePart:sub(1, - goodExtension:len() - 1) - if middlePart:len() == 0 then - -- this is just the exact pattern + file extension - return true - else - local sepLen = separator:len() - if middlePart:sub(1, sepLen) ~= separator then - return false - else - local numberPart = middlePart:sub(sepLen + 1) - if string.match(numberPart, "%d+") == numberPart and tonumber(numberPart) then - -- there are really only digits that form a number in the number part - return true - else - return false - end - end - end - end - end - end) - - return matchedFiles -end - function SfxGroup:load(yields) local files = FileUtils.getFilteredDirectoryItems(self.path, "file") - self.matchingFiles = SfxGroup.getMatchingFiles(files, self.pattern, self.separator) + self.matchingFiles = FileUtils.getMatchingFiles(files, self.pattern, FileUtils.SUPPORTED_SOUND_FORMATS, self.separator) end return SfxGroup \ No newline at end of file diff --git a/client/tests/FileUtilsTests.lua b/client/tests/FileUtilsTests.lua index cf56ccd5..ba7dcb72 100644 --- a/client/tests/FileUtilsTests.lua +++ b/client/tests/FileUtilsTests.lua @@ -25,11 +25,22 @@ local files = { "chain2_04.ogg", "chain3_2.ogg", -- extra number before separator - "chain22_2.ogg" + "chain22_2.ogg", + "panel11.png", + "panel102.png", + "ppanel13.png", + "panel1_4.png", + "panel15.PNG", + "panel01_6.png", + "panel017.png", + "panel-2.png", + "panel00.png", + "panel18.png.jpg", + "panel109.jpag", } -local function testFileMatching(pattern, separator, controlGroup) - local matchingFiles = FileUtils.getMatchingFiles(files, pattern, separator) +local function testFileMatching(pattern, validExtensions, separator, controlGroup) + local matchingFiles = FileUtils.getMatchingFiles(files, pattern, validExtensions, separator) for _, matched in ipairs(matchingFiles) do assert(tableUtils.contains(controlGroup, matched), "Unexpectedly matched " .. matched) @@ -49,7 +60,7 @@ local function testFileMatching1() "chain08.ogg", } - testFileMatching(pattern, separator, expected) + testFileMatching(pattern, FileUtils.SUPPORTED_SOUND_FORMATS, separator, expected) end local function testfileMatching2() @@ -62,8 +73,20 @@ local function testfileMatching2() "chain2_04.ogg", } - testFileMatching(pattern, separator, expected) + testFileMatching(pattern, FileUtils.SUPPORTED_SOUND_FORMATS, separator, expected) +end + +local function testfileMatching3() + local pattern = "panel1" + local separator = "" + local expected = { + "panel11.png", + "panel102.png", + } + + testFileMatching(pattern, FileUtils.SUPPORTED_IMAGE_FORMATS, separator, expected) end testFileMatching1() -testfileMatching2() \ No newline at end of file +testfileMatching2() +testfileMatching3() \ No newline at end of file diff --git a/testLauncher.lua b/testLauncher.lua index 1c9cc4ef..0a8f173d 100644 --- a/testLauncher.lua +++ b/testLauncher.lua @@ -31,7 +31,7 @@ function love.load() end local tests = { - "client.tests.SfxGroupTests", + "client.tests.FileUtilsTests", "client.tests.ModControllerTests", "common.tests.engine.StackRollbackReplayTests", "client.tests.QueueTests", From 4f53d7e3a75ec0bf340f55dfe9b78962506b905a Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 17:18:20 +0100 Subject: [PATCH 06/10] finish FileGroup and SfxGroup more or less? --- client/src/FileGroup.lua | 65 ++++++++++++++++++++++++++++++++ client/src/music/SfxGroup.lua | 71 +++++++++++++++++++++++++++++------ 2 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 client/src/FileGroup.lua diff --git a/client/src/FileGroup.lua b/client/src/FileGroup.lua new file mode 100644 index 00000000..0630af01 --- /dev/null +++ b/client/src/FileGroup.lua @@ -0,0 +1,65 @@ +local class = require("common.lib.class") +local FileUtils = require("client.src.FileUtils") + +---@param filename1 string +---@param filename2 string +---@return string # the filename that won the collision +local function resolveCollision(filename1, filename2) + -- TODO: establish a consistent priority by something more coherent than string sort? + -- e.g. going by extension, preferring lossless over lossy, better compression over worse + -- could also prefer leading 0 over non-leading etc. + return filename1 +end + +---@return string[] +local function indexMatchingFiles(matchingFiles, pattern, separator) + table.sort(matchingFiles) + local indexed = {} + local patternLength = pattern:len() + local separatorLength = separator:len() + local index + for i, filename in ipairs(matchingFiles) do + local cut = FileUtils.getFileNameWithoutExtension(filename) + cut = cut:sub(patternLength + separatorLength + 1) + if cut:len() == 0 then + index = 1 + else + index = tonumber(cut) + end + ---@cast index integer + if not indexed[index] then + indexed[index] = filename + else + -- oh no, we have a collision + indexed[index] = resolveCollision(indexed[index], filename) + end + end + + return indexed +end + +-- A FileGroup is a group of files matching a certain pattern +-- beyond the pattern they are in particular numbered with integers +-- creating a FileGroup makes all file names belonging to the group available in matchingFiles +-- a concrete assignment of indices has happened in indexedFiles; \n +-- in the process, files that have the same index will get eliminated until only one is left for each index +---@class FileGroup +---@field path string +---@field pattern string The pattern to search for as plain text; luaregex disabled +---@field validExtensions string[] +---@field separator string the separator by which alternative copies separate their index, default "" +---@field matchingFiles string[] the files that got matched during the load process of the FileGroup +---@field indexedFiles string[] the files indexed by their suffix; only one file per index guaranteed +local FileGroup = class( +function(self, path, pattern, validExtensions, separator) + self.path = path + self.pattern = pattern + self.validExtensions = validExtensions + self.separator = separator or "" + + local files = FileUtils.getFilteredDirectoryItems(self.path, "file") + self.matchingFiles = FileUtils.getMatchingFiles(files, self.pattern, self.validExtensions, self.separator) + self.indexedFiles = indexMatchingFiles(self.matchingFiles, self.pattern, self.separator) +end) + +return FileGroup \ No newline at end of file diff --git a/client/src/music/SfxGroup.lua b/client/src/music/SfxGroup.lua index de67612a..1d3debe4 100644 --- a/client/src/music/SfxGroup.lua +++ b/client/src/music/SfxGroup.lua @@ -2,21 +2,70 @@ local class = require("common.lib.class") local tableUtils = require("common.lib.tableUtils") local FileUtils = require("client.src.FileUtils") +-- A group of SFX that belong together +-- Only 1 SFX in the group may play at the same time +-- playing another SFX from the group will interrupt other ongoing SFX ---@class SfxGroup ----@field path string ----@field pattern string The pattern to search for as plain text; luaregex disabled ----@field separator string the separator by which alternative copies separate their index, default "" ----@field matchingFiles string[] the files that got matched during the load process of the SfxGroup +---@field fileGroup FileGroup +---@field sources love.Source[] +---@field volumeMultiplier number +---@field lastPlaying love.Source +---@overload fun(fileGroup: FileGroup, volumeMultiplier: number?): SfxGroup local SfxGroup = class( -function(self, path, pattern, separator) - self.path = path - self.pattern = pattern - self.separator = separator or "" +---@param fileGroup FileGroup +function(self, fileGroup, volumeMultiplier) + self.fileGroup = fileGroup + self.volumeMultiplier = volumeMultiplier or 1 + + -- fileGroup.indexedFiles may have gaps; this may be relevant to a SfxGroupGroup but not for a SfxGroup, just use all files + local continuouslyIndexedFiles = tableUtils.toContinuouslyIndexedTable(fileGroup.indexedFiles) + self.sources = {} + -- if there are gaps in indexedFiles, tough luck, they'll get ignored + for i, filename in ipairs(continuouslyIndexedFiles) do + self.sources[i] = love.audio.newSource(fileGroup.path .. "/" .. filename, "static") + end end) -function SfxGroup:load(yields) - local files = FileUtils.getFilteredDirectoryItems(self.path, "file") - self.matchingFiles = FileUtils.getMatchingFiles(files, self.pattern, FileUtils.SUPPORTED_SOUND_FORMATS, self.separator) +function SfxGroup:setVolume(volume) + for _, source in ipairs(self.sources) do + source:setVolume(volume * self.volumeMultiplier) + end +end + +function SfxGroup:play() + if self.lastPlaying then + self.lastPlaying:stop() + end + + self.lastPlaying = self.sources[math.random(#self.sources)] + self.lastPlaying:play() +end + +function SfxGroup:isPlaying() + if not self.lastPlaying then + return false + else + return self.lastPlaying:isPlaying() + end +end + +function SfxGroup:stop() + if self.lastPlaying then + self.lastPlaying:stop() + end +end + +function SfxGroup:clone() +---@diagnostic disable-next-line: param-type-mismatch + local clone = setmetatable({}, SfxGroup) + clone.fileGroup = self.fileGroup + clone.volumeMultiplier = self.volumeMultiplier + clone.sources = {} + for i, source in ipairs(self.sources) do + clone.sources[i] = source:clone() + end + + return clone end return SfxGroup \ No newline at end of file From fbcb083cc31f77ffd5ca4ceb4395844db554eeaa Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 18:03:04 +0100 Subject: [PATCH 07/10] start integrating SfxGroup with Character --- client/src/FileGroup.lua | 1 + client/src/FileUtils.lua | 10 +-- client/src/mods/Character.lua | 102 +++++++++++---------------- client/src/music/SfxGroup.lua | 13 +++- client/src/music/SoundController.lua | 6 +- client/tests/FileUtilsTests.lua | 22 ++++-- 6 files changed, 80 insertions(+), 74 deletions(-) diff --git a/client/src/FileGroup.lua b/client/src/FileGroup.lua index 0630af01..8784972f 100644 --- a/client/src/FileGroup.lua +++ b/client/src/FileGroup.lua @@ -50,6 +50,7 @@ end ---@field separator string the separator by which alternative copies separate their index, default "" ---@field matchingFiles string[] the files that got matched during the load process of the FileGroup ---@field indexedFiles string[] the files indexed by their suffix; only one file per index guaranteed +---@overload fun(path: string, pattern: string, validExtensions: string[], separator: string?): FileGroup local FileGroup = class( function(self, path, pattern, validExtensions, separator) self.path = path diff --git a/client/src/FileUtils.lua b/client/src/FileUtils.lua index 14f1610f..53a8fb23 100644 --- a/client/src/FileUtils.lua +++ b/client/src/FileUtils.lua @@ -185,7 +185,7 @@ function fileUtils.saveReplay(replay) -- TODO: This is for legacy support of the replay browser only; -- as Replay is a common.data object, client should not use it to write client specific fields Replay.lastPath = path - fileUtils.writeJson(path .. "/" .. filename .. ".json", replay) + fileUtils.writeJson(path, filename .. ".json", replay) end ---@param files string[] @@ -249,18 +249,18 @@ end ---@param path string ---@param data string -function fileUtils.write(path, data) +function fileUtils.write(path, filename, data) love.filesystem.createDirectory(path) - local success, message = love.filesystem.write(path, data) + local success, message = love.filesystem.write(path .. "/" .. filename, data) if not success then error("Failed to write to " .. path .. " : " .. message) end end -function fileUtils.writeJson(path, tab) +function fileUtils.writeJson(path, filename, tab) local encoded = json.encode(tab) ---@cast encoded string # json.encode always returns a string if not called with a second argument - fileUtils.write(path, encoded) + fileUtils.write(path, filename, encoded) end return fileUtils \ No newline at end of file diff --git a/client/src/mods/Character.lua b/client/src/mods/Character.lua index eedd097c..ef5b841e 100644 --- a/client/src/mods/Character.lua +++ b/client/src/mods/Character.lua @@ -14,6 +14,8 @@ local StageTrack = require("client.src.music.StageTrack") local DynamicStageTrack = require("client.src.music.DynamicStageTrack") local RelayStageTrack = require("client.src.music.RelayStageTrack") local Mod = require("client.src.mods.Mod") +local FileGroup = require("client.src.FileGroup") +local SfxGroup = require("client.src.music.SfxGroup") ---@type Character local default_character = nil -- holds default assets fallbacks @@ -559,13 +561,13 @@ function Character.reassignLegacySfx(self) self.sounds.chain[i] = nil end end - if #self.sounds.chain2_echo > 0 then + if self.sounds.chain2_echo then self.sounds.chain[6] = self.sounds.chain2_echo -- shouldn't show up in sound test any longer self.sounds.chain2_echo = nil maxIndex = 6 end - if #self.sounds.chain_echo > 0 then + if self.sounds.chain_echo then self.sounds.chain[5] = self.sounds.chain_echo -- shouldn't show up in sound test any longer self.sounds.chain_echo = nil @@ -602,49 +604,41 @@ local perSizeSfxStart = { chain = 2, combo = 4, shock = 3} function Character.loadSfx(self, name, yields) local sfx = {} - local stringLen = string.len(name) - local files = tableUtils.filter(self.files, function(file) return string.find(file, name, nil, true) end) - - local maxIndex = -1 - -- load sounds - for i = 1, #files do - stringLen = string.len(name) - local index = tonumber(string.match(files[i], "%d+", stringLen + 1)) - - -- for files with no suffix at all, index would be nil but they should go in sfx[1] instead - local targetIndex = 1 - if index ~= nil then - -- otherwise use the index as normal - targetIndex = index + if not perSizeSfxStart[name] then + local fileGroup = FileGroup(self.path, name, fileUtils.SUPPORTED_SOUND_FORMATS) + if next(fileGroup.matchingFiles) then + return SfxGroup(fileGroup, 1) + else + return nil end + else + local stringLen = string.len(name) + local files = tableUtils.filter(self.files, function(file) return string.find(file, name, nil, true) end) + + local maxIndex = -1 + -- load sounds + for i = 1, #files do + stringLen = string.len(name) + local index = tonumber(string.match(files[i], "%d+", stringLen + 1)) + + -- for files with no suffix at all, index would be nil but they should go in sfx[1] instead + local targetIndex = 1 + if index ~= nil then + -- otherwise use the index as normal + targetIndex = index + end - if perSizeSfxStart[name] then if sfx[targetIndex] == nil then sfx[targetIndex] = self:loadSubSfx(name, index) end - else - local sound = fileUtils.loadSoundFromSupportExtensions(self.path .. "/" .. files[i], false) - if sound ~= nil then - sfx[targetIndex] = sound - end - if yields then - coroutine.yield() + if sfx[targetIndex] then + maxIndex = math.max(maxIndex, targetIndex) end end - if sfx[targetIndex] then - maxIndex = math.max(maxIndex, targetIndex) - end - end - - if perSizeSfxStart[name] then self:fillInMissingSounds(sfx, name, maxIndex) - else - -- #table may yield erroneous (too large) results for tables with gaps - -- Character:playRandomSfx() relies on #table being accurate so we redo the table here if it has gaps - sfx = tableUtils.toContinuouslyIndexedTable(sfx) end assert(sfx ~= nil) @@ -725,8 +719,8 @@ end -- sound playing / sound control function Character.playSelectionSfx(self) - if self.sounds.selection and #self.sounds.selection > 0 then - SoundController:playRandomSfx(self.sounds.selection) + if self.sounds.selection then + self.sounds.selection:play() else GAME.theme:playValidationSfx() end @@ -809,41 +803,27 @@ function Character.playAttackSfx(self, attack) end function Character.playGarbageMatchSfx(self) - if self.sounds.garbage_match and #self.sounds.garbage_match ~= 0 then - SoundController:stopSfx(self.sounds.garbage_match) - SoundController:playRandomSfx(self.sounds.garbage_match) + if self.sounds.garbage_match then + self.sounds.garbage_match:play() end end function Character.playGarbageLandSfx(self) - if self.sounds.garbage_land and #self.sounds.garbage_land ~= 0 then - SoundController:stopSfx(self.sounds.garbage_land) - SoundController:playRandomSfx(self.sounds.garbage_land) + if self.sounds.garbage_land then + self.sounds.garbage_land:play() end end -- tauntUp is rolled externally in order to send the exact same taunt index to the enemy as plays locally function Character.playTauntUpSfx(self, tauntUp) - if self.sounds.taunt_up and #self.sounds.taunt_up ~= 0 then - SoundController:stopSfx(self.sounds.taunt_up) - -- self might be a replacement character with less taunts than the selected one so confirm the index first - if self.sounds.taunt_up[tauntUp] then - SoundController:playSfx(self.sounds.taunt_up[tauntUp]) - else - SoundController:playRandomSfx(self.sounds.taunt_up, self.sounds.taunt_down) - end + if self.sounds.taunt_up then + self.sounds.taunt_up:play() end end function Character.playTauntDownSfx(self, tauntDown) - if self.sounds.taunt_down and #self.sounds.taunt_down ~= 0 then - SoundController:stopSfx(self.sounds.taunt_down) - -- self might be a replacement character with less taunts than the selected one so confirm the index first - if self.sounds.taunt_down[tauntDown] then - SoundController:playSfx(self.sounds.taunt_down[tauntDown]) - else - SoundController:playRandomSfx(self.sounds.taunt_down, self.sounds.taunt_up) - end + if self.sounds.taunt_down then + self.sounds.taunt_down:play() end end @@ -857,7 +837,11 @@ function Character.playTaunt(self, tauntType, index) end function Character:playWinSfx() - SoundController:playRandomSfx(self.sounds.win, themes[config.theme].sounds.fanfare1) + if self.sounds.win then + self.sounds.win:play() + else + themes[config.theme].sounds.fanfare1:play() + end end function Character.applyConfigVolume(self) diff --git a/client/src/music/SfxGroup.lua b/client/src/music/SfxGroup.lua index 1d3debe4..2054ab47 100644 --- a/client/src/music/SfxGroup.lua +++ b/client/src/music/SfxGroup.lua @@ -1,6 +1,5 @@ local class = require("common.lib.class") local tableUtils = require("common.lib.tableUtils") -local FileUtils = require("client.src.FileUtils") -- A group of SFX that belong together -- Only 1 SFX in the group may play at the same time @@ -26,18 +25,26 @@ function(self, fileGroup, volumeMultiplier) end end) +SfxGroup.TYPE = "SfxGroup" + function SfxGroup:setVolume(volume) for _, source in ipairs(self.sources) do source:setVolume(volume * self.volumeMultiplier) end end -function SfxGroup:play() +---@param index integer? optionally specify an exact index you want to play if possible +function SfxGroup:play(index) if self.lastPlaying then self.lastPlaying:stop() end - self.lastPlaying = self.sources[math.random(#self.sources)] + if index and self.sources[index] then + self.lastPlaying = self.sources[index] + else + self.lastPlaying = self.sources[math.random(#self.sources)] + end + self.lastPlaying:play() end diff --git a/client/src/music/SoundController.lua b/client/src/music/SoundController.lua index 6aa86f9f..bbf68428 100644 --- a/client/src/music/SoundController.lua +++ b/client/src/music/SoundController.lua @@ -14,12 +14,12 @@ SoundController = { -- applies the sfx volume setting to the passed sfx which can be a table of sources or a source itself function SoundController:applySfxVolume(sfx) - if type(sfx) == "table" then + if (type(sfx) == "userdata" and sfx:typeOf("Source")) or sfx.TYPE == "SfxGroup" then + sfx:setVolume(config.SFX_volume / 100) + elseif type(sfx) == "table" then for _, v in pairs(sfx) do SoundController:applySfxVolume(v) end - elseif type(sfx) == "userdata" and sfx:typeOf("Source") then - sfx:setVolume(config.SFX_volume / 100) end end diff --git a/client/tests/FileUtilsTests.lua b/client/tests/FileUtilsTests.lua index ba7dcb72..38c56fda 100644 --- a/client/tests/FileUtilsTests.lua +++ b/client/tests/FileUtilsTests.lua @@ -37,6 +37,8 @@ local files = { "panel00.png", "panel18.png.jpg", "panel109.jpag", + "garbage_match.ogg", + "garbage_match2.ogg", } local function testFileMatching(pattern, validExtensions, separator, controlGroup) @@ -63,7 +65,7 @@ local function testFileMatching1() testFileMatching(pattern, FileUtils.SUPPORTED_SOUND_FORMATS, separator, expected) end -local function testfileMatching2() +local function testFileMatching2() local pattern = "chain2" local separator = "_" local expected = { @@ -76,7 +78,7 @@ local function testfileMatching2() testFileMatching(pattern, FileUtils.SUPPORTED_SOUND_FORMATS, separator, expected) end -local function testfileMatching3() +local function testFileMatching3() local pattern = "panel1" local separator = "" local expected = { @@ -87,6 +89,18 @@ local function testfileMatching3() testFileMatching(pattern, FileUtils.SUPPORTED_IMAGE_FORMATS, separator, expected) end +local function testFileMatching4() + local pattern = "garbage_match" + local separator = "" + local expected = { + "garbage_match.ogg", + "garbage_match2.ogg" + } + + testFileMatching(pattern, FileUtils.SUPPORTED_SOUND_FORMATS, separator, expected) +end + testFileMatching1() -testfileMatching2() -testfileMatching3() \ No newline at end of file +testFileMatching2() +testFileMatching3() +testFileMatching4() \ No newline at end of file From 4017382982a30223c17713bbc1125aa4bbb70386 Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 19:56:53 +0100 Subject: [PATCH 08/10] fix taunts not playing --- client/src/mods/Character.lua | 2 +- client/src/network/PlayerStack.lua | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/client/src/mods/Character.lua b/client/src/mods/Character.lua index ef5b841e..3dc26fe7 100644 --- a/client/src/mods/Character.lua +++ b/client/src/mods/Character.lua @@ -33,7 +33,7 @@ local comboStyle = {classic = 0, per_combo = 1} ---@field panels string? Id of a panel set for super select ---@field images table graphical assets of the character ---@field telegraph_garbage_images userdata[][] graphical assets for telegraph display ----@field sounds table> sound effect assets of the character +---@field sounds table | SfxGroup> sound effect assets of the character ---@field musics table music assets of the character ---@field hasMusic boolean? if the character has any music ---@field flag string? flag to be displayed in selection menus diff --git a/client/src/network/PlayerStack.lua b/client/src/network/PlayerStack.lua index 1503418d..4d92cb83 100644 --- a/client/src/network/PlayerStack.lua +++ b/client/src/network/PlayerStack.lua @@ -5,11 +5,11 @@ local PlayerStack = require("client.src.PlayerStack") function PlayerStack.handle_input_taunt(self) if self.inputMethod ~= "touch" then local input = self.player.inputConfiguration - if input.isDown["TauntUp"] and self:can_taunt() and #self.character.sounds.taunt_up > 0 then - self.taunt_up = math.random(#self.character.sounds.taunt_up) + if input.isDown["TauntUp"] and self:can_taunt() and self.character.sounds.taunt_up then + self.taunt_up = math.random(#self.character.sounds.taunt_up.sources) GAME.netClient:sendTauntUp(self.taunt_up) - elseif input.isDown["TauntDown"] and self:can_taunt() and #self.character.sounds.taunt_down > 0 then - self.taunt_down = math.random(#self.character.sounds.taunt_down) + elseif input.isDown["TauntDown"] and self:can_taunt() and self.character.sounds.taunt_down then + self.taunt_down = math.random(#self.character.sounds.taunt_down.sources) GAME.netClient:sendTauntDown(self.taunt_down) end end From a56ecce4f59e1f494ff0e7740a48dc97b6321f53 Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 21:56:56 +0100 Subject: [PATCH 09/10] replace subSfx loading with loading SfxGroups instead --- client/src/mods/Character.lua | 78 +++++++++++++---------------------- 1 file changed, 29 insertions(+), 49 deletions(-) diff --git a/client/src/mods/Character.lua b/client/src/mods/Character.lua index 3dc26fe7..f1e8e571 100644 --- a/client/src/mods/Character.lua +++ b/client/src/mods/Character.lua @@ -33,7 +33,7 @@ local comboStyle = {classic = 0, per_combo = 1} ---@field panels string? Id of a panel set for super select ---@field images table graphical assets of the character ---@field telegraph_garbage_images userdata[][] graphical assets for telegraph display ----@field sounds table | SfxGroup> sound effect assets of the character +---@field sounds table | SfxGroup> sound effect assets of the character ---@field musics table music assets of the character ---@field hasMusic boolean? if the character has any music ---@field flag string? flag to be displayed in selection menus @@ -630,7 +630,14 @@ function Character.loadSfx(self, name, yields) if sfx[targetIndex] == nil then - sfx[targetIndex] = self:loadSubSfx(name, index) + local searchName = name + if index then + searchName = searchName .. index + end + local fileGroup = FileGroup(self.path, searchName, fileUtils.SUPPORTED_SOUND_FORMATS) + if next(fileGroup.matchingFiles) then + sfx[targetIndex] = SfxGroup(fileGroup, 1) + end end if sfx[targetIndex] then @@ -645,45 +652,6 @@ function Character.loadSfx(self, name, yields) return sfx end --- loads all variations for the sfx with the base name sfxName and returns them in a continuous integer key'd table -function Character.loadSubSfx(self, name, index, yields) - local sfxTable = {} - - if index == nil then - -- index 1 can be implicit, e.g. chain, chain_2, chain2, chain2_2 (actually the official spec) - -- change it to an empty string so it doesn't crash on concat - index = "" - end - local stringLen = string.len(name..index) - local subfiles = tableUtils.filter(self.files, - function(file) - return file == name..index or - (string.find(file, name .. index) and - -- exclude chain22 while searching for chain2 - tonumber(string.sub(file, stringLen + 1, stringLen + 1)) == nil and - -- exclude combo_echo / chain_echo, only take comboN_M/chainN_M - tonumber(string.sub(file, stringLen + 2, stringLen + 2)) ~= nil) - end) - - if #subfiles > 0 then - for j = 1, #subfiles do - local subSound = fileUtils.loadSoundFromSupportExtensions(self.path .. "/" .. subfiles[j], false) - if subSound ~= nil then - sfxTable[#sfxTable+1] = subSound - end - if yields then - coroutine.yield() - end - end - end - - if #sfxTable > 0 then - return sfxTable - else - return nil - end -end - function Character.fillInMissingSounds(self, sfxTable, name, maxIndex) local fillUpSound = nil if maxIndex > 0 then @@ -735,32 +703,44 @@ function Character.playComboSfx(self, size) -- so if this error ever occurs, something is seriously cursed error("Found neither chain nor combo sfx upon trying to play combo sfx") else - SoundController:playRandomSfx(self.sounds.chain[0]) + self.sounds.chain[0]:play() end else -- combo sfx available! if self.combo_style == comboStyle.classic then -- roll among all combos in case a per_combo style character had its combostyle changed to classic local rolledIndex = math.random(#self.sounds.combo) - SoundController:playRandomSfx(self.sounds.combo[rolledIndex]) + self.sounds.combo[rolledIndex]:play() else -- use fallback sound if the combo size is higher than the highest combo sfx - SoundController:playRandomSfx(self.sounds.combo[size], self.sounds.combo[0]) + if self.sounds.combo[size] then + self.sounds.combo[size]:play() + else + self.sounds.combo[0]:play() + end end end end function Character.playChainSfx(self, length) -- chain[0] always exists by virtue of the default character SFX - SoundController:playRandomSfx(self.sounds.chain[length], self.sounds.chain[0]) + if self.sounds.chain[length] then + self.sounds.chain[length]:play() + else + self.sounds.chain[0]:play() + end end function Character.playShockSfx(self, size) - if #self.sounds.shock > 0 then - SoundController:playRandomSfx(self.sounds.shock[size], self.sounds.shock[0]) + if self.sounds.shock then + if self.sounds.shock[size] then + self.sounds.shock[size]:play() + else + self.sounds.shock[0]:play() + end else - if size >= 6 and #self.sounds.combo_echo > 0 then - SoundController:playRandomSfx(self.sounds.combo_echo) + if size >= 6 and self.sounds.combo_echo then + self.sounds.combo_echo:play() else self:playComboSfx(size) end From 3c47313e87a93d143a695ad368bca958ddb281d1 Mon Sep 17 00:00:00 2001 From: Endaris Date: Sat, 28 Dec 2024 23:50:53 +0100 Subject: [PATCH 10/10] fix crash when playing shock sfx --- client/src/mods/Character.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/mods/Character.lua b/client/src/mods/Character.lua index f1e8e571..5aee1b15 100644 --- a/client/src/mods/Character.lua +++ b/client/src/mods/Character.lua @@ -732,7 +732,7 @@ function Character.playChainSfx(self, length) end function Character.playShockSfx(self, size) - if self.sounds.shock then + if #self.sounds.shock > 0 then if self.sounds.shock[size] then self.sounds.shock[size]:play() else