diff --git a/.gitignore b/.gitignore index 991feacd..486e2e6c 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ *.user *.userosscache *.sln.docstates +.vscode/BROWSE.* # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs @@ -35,7 +36,7 @@ bld/ # Visual Studio 2015 cache/options directory .vs/ -.vscode/* +#.vscode/* # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ @@ -345,3 +346,5 @@ Temporary Items log.txt rd.db rd.db-journal +/lib/Any +/sjis.txt diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..291b8bf9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "arcadedata"] + path = arcadedata + url = git@gitlab.com:agka/raindrop-arcade-data.git +[submodule "GameData"] + path = GameData + url = git@github.com:zardoru/raindrop-data.git diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json new file mode 100644 index 00000000..4ac2b9e9 --- /dev/null +++ b/.vscode/c_cpp_properties.json @@ -0,0 +1,90 @@ +{ + "configurations": [ + { + "name": "Mac", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + }, + "macFrameworkPath": [ + "/System/Library/Frameworks", + "/Library/Frameworks" + ] + }, + { + "name": "Linux", + "includePath": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "defines": [], + "intelliSenseMode": "clang-x64", + "browse": { + "path": [ + "/usr/include", + "/usr/local/include", + "${workspaceRoot}" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "" + } + }, + { + "name": "Win32", + "includePath": [ + "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include/*", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}/src/", + "${workspaceRoot}/lib/include/", + "${workspaceRoot}/lib/include/portaudio", + "${workspaceRoot}/lib/include/libmpg123", + "${workspaceRoot}/lib/include/libpng", + "${workspaceRoot}/lib/include/libjpeg-turbo", + "${workspaceRoot}/lib/include/zlib", + "${workspaceRoot}/lib/include/libsndfile", + "${workspaceRoot}/lib/include/stb", + "${workspaceRoot}/lib/include/sqlite3", + "${workspaceRoot}/lib/include/soxr", + "${workspaceRoot}/lib/include/LuaBridge", + "${workspaceRoot}/lib/include/lua", + "${workspaceRoot}/lib/include/stdex", + "${workspaceRoot}/msvc/packages/boost.1.64.0.0/lib/native/include" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "WIN32" + ], + "intelliSenseMode": "msvc-x64", + "browse": { + "path": [ + "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.10.25017/include/*", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/um", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/ucrt", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/shared", + "C:/Program Files (x86)/Windows Kits/10/Include/10.0.15063.0/winrt", + "${workspaceRoot}/src/" + ], + "limitSymbolsToIncludedHeaders": true, + "databaseFilename": "${workspaceRoot}/.vscode/browse.db" + } + } + ], + "version": 3 +} \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..e7aec343 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,66 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(Windows) Attach", + "type": "cppvsdbg", + "request": "attach", + "processId": "${command:pickProcess}" + }, + { + "name": "(Windows) Run Test osu file - Autoplay (debug/test.osu)", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceRoot}/msvc/Debug/raindrop.exe", + "args": ["-p", "-i", "s3a/test.osu", "--config", "config-debug.ini"], + "stopAtEntry": false, + "cwd": "${workspaceRoot}/debug", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Run Test BMS file - Autoplay (debug/test.bms)", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceRoot}/msvc/Debug/raindrop.exe", + "args": ["-p", "-i", "test.bms", "--config", "config-debug.ini"], + "stopAtEntry": false, + "cwd": "${workspaceRoot}/debug", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Run Test BMS file (debug/test.bms)", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceRoot}/msvc/Debug/raindrop.exe", + "args": ["-p", "-i", "test.bms", "--config", "config-debug.ini"], + "stopAtEntry": false, + "cwd": "${workspaceRoot}/debug", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Run Tests", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceRoot}/msvc/Debug/raindrop.exe", + "args": ["--test"], + "stopAtEntry": false, + "cwd": "${workspaceRoot}/debug", + "environment": [], + "externalConsole": true + }, + { + "name": "(Windows) Debug Launch", + "type": "cppvsdbg", + "request": "launch", + "program": "${workspaceRoot}/msvc/Debug/raindrop.exe", + "args": ["--config", "config-debug.ini"], + "stopAtEntry": false, + "cwd": "${workspaceRoot}/debug", + "environment": [], + "externalConsole": true + } + ] + } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..54c4974d --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,18 @@ +{ + "files.exclude": { + "**/.git": true, + "**/.svn": true, + "**/.hg": true, + "**/CVS": true, + "**/.DS_Store": true, + "**.dll": true, + "**.db": true, + "**/xcode":true, + "**/release":true, + "**/songs":true, + "**/debug":true, + "**/msvc":true, + "**/lib":true, + "**/src/ext":true + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 00000000..94f5ea97 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,58 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=733558 + // for the documentation about the tasks.json format + "version": "2.0.0", + "tasks": [ + { + "taskName": "Build Debug", + "type": "process", + "command": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\MSBuild\\15.0\\Bin\\msbuild.exe", + "args": [ + "msvc/raindrop.vcxproj", + "/property:GenerateFullPaths=true", + "/t:build", + "/p:Configuration=Debug" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + }, + { + "taskName": "Build Release", + "type": "process", + "command": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\MSBuild\\15.0\\Bin\\msbuild.exe", + "args": [ + "msvc/raindrop.vcxproj", + "/property:GenerateFullPaths=true", + "/t:build", + "/p:Configuration=Release" + ], + "group": "build", + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + }, + { + "taskName": "Rebuild Release", + "type": "process", + "command": "C:\\Program Files (x86)\\Microsoft Visual Studio\\2017\\Community\\MSBuild\\15.0\\Bin\\msbuild.exe", + "args": [ + "msvc/raindrop.vcxproj", + "/property:GenerateFullPaths=true", + "/t:Clean;Build", + "/p:Configuration=Release" + ], + "group": "build", + "presentation": { + "reveal": "always" + }, + "problemMatcher": "$msCompile" + } + ] +} \ No newline at end of file diff --git a/GameData b/GameData new file mode 160000 index 00000000..44813507 --- /dev/null +++ b/GameData @@ -0,0 +1 @@ +Subproject commit 44813507123337e4ed443dfdf61bf3021e1ca9ff diff --git a/GameData/Scripts/AnimationFunctions.lua b/GameData/Scripts/AnimationFunctions.lua deleted file mode 100644 index ebef104f..00000000 --- a/GameData/Scripts/AnimationFunctions.lua +++ /dev/null @@ -1,81 +0,0 @@ -function getMoveFunction(sX, sY, eX, eY, obj) - return function(frac) - obj.X = sX + (eX - sX)*frac - obj.Y = sY + (eY - sY)*frac - return 1 - end -end - -function getUncropFunction(w, h, iw, ih, obj) - return function(frac) - obj.Width = w - obj.Height = h*(1-frac) - obj:SetCropByPixels(0, iw, ih*frac*0.5, (ih - (ih*frac*0.5))) - return 1 - end -end - -function getFadeFunction(sF, eF, obj) - return function (frac) - obj.Alpha = sF + (eF - sF)*frac - return 1 - end -end - --- bezier over 1 dimension -local function Bez1D (p2, p1, t) - local cx = 3 * p1 - local bx = 3 * (p2 - p1) - cx - local ax = 1 - cx - bx - local xt = ax * t*t*t + bx * t*t + cx * t - - return xt -end - --- 1D cubic bez derivative -local function BezdAdT(aa1, aa2, t) - return 3 * (1 - 3 * aa2 + 3 * aa1) * t * t + - 2 * (3 * aa2 - 6 * aa1) * t + - 3 * aa1 -end - --- p[1] == x, p[2] == y -function CubicBezier(p1, p2, t) - return Bez1D(p2[1], p1[1], t), Bez1D(p2[2], p1[2], t) -end - -Ease = { - ElasticSquare = function(p) - local attn = 1 + 1 - math.asin(1.0 / p) * 2.0 / math.pi - local pi = math.pi - local sin = math.sin - return function (x) - return sin(x*x * pi / 2.0 * attn) * p - end - end, - CubicBezier = function (p1, p2) - --[[ - based off - http://greweb.me/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/ - ]] - - return function(t) - - -- newton raphson - local function tforx(ax) - local guess = ax - for i=1,4 do - local cs = BezdAdT(p1[1], p2[1], guess) - if cs == 0 then return guess end - - local cx = Bez1D(p1[1], p2[1], guess) - ax - guess = guess - cx / cs - end - - return guess - end - - return Bez1D(p1[2], p2[2], tforx(t)) - end - end -} diff --git a/GameData/Scripts/FixedObjects.lua b/GameData/Scripts/FixedObjects.lua deleted file mode 100644 index 41aefe27..00000000 --- a/GameData/Scripts/FixedObjects.lua +++ /dev/null @@ -1,60 +0,0 @@ -game_require "librd" - -FixedObjects = { XRatio = 1, YRatio = 1 } - -function FixedObjects:CreateObjectFromParameters(tbl, constants) - local Object = Engine:CreateObject() - - print("Create object " .. tbl[2]) - - local name = tbl[2] - -- table.dump(constants) - Object.Texture = constants[tbl[1]] or tbl[1] or 0 - with (Object, { - X = (constants[tbl[3]] or tbl[3] or 0) * self.XRatio, - Y = (constants[tbl[4]] or tbl[4] or 0) * self.YRatio, - Width = (constants[tbl[5]] or tbl[5] or 1) * self.XRatio, - Height = (constants[tbl[6]] or tbl[6] or 1) * self.YRatio, - Layer = constants[tbl[7]] or tbl[7] or Object.Layer, - Rotation = constants[tbl[9]] or tbl[9] or 0, - ScaleX = constants[tbl[10]] or tbl[10] or 1, - ScaleY = constants[tbl[11]] or tbl[11] or 1 - }) - - -- Object.Centered = constants[tbl[8]] or tbl[8] or 0 - self.Sprites[name] = Object -end - -function FixedObjects:CreateFromCSV(file, constants) - local File = io.open(GetSkinFile(file)) - print ("Opening " .. GetSkinFile(file)) - - constants = constants or {} - - if File == nil then - return - end - - for line in File:lines() do - if line[1] ~= "#" then -- Not a comment - local tbl = string.split(line) - if tbl then - -- table.dump(tbl) - self:CreateObjectFromParameters(tbl, constants) - end - end - end - - io.close(File) -end - -function FixedObjects:new() - local ret = {} - ret.Sprites = {} - setmetatable(ret, self) - return ret -end - -FixedObjects.__index = FixedObjects - -return FixedObjects diff --git a/GameData/Scripts/FrameInterpolator.lua b/GameData/Scripts/FrameInterpolator.lua deleted file mode 100644 index 5b63e137..00000000 --- a/GameData/Scripts/FrameInterpolator.lua +++ /dev/null @@ -1,60 +0,0 @@ -game_require "TextureAtlas" - -FrameInterpolator = { - DefaultFramerate = 60, - TotalFrames = 0 -} - -FrameInterpolator.__index = FrameInterpolator - -function FrameInterpolator.FilenameAssign(fn) - return (fn + 1) .. ".png" -end - -function FrameInterpolator:New(sprite_file, duration, object) - local out = {} - setmetatable(out, self) - - out.SpriteSheet = TextureAtlas:new(GetSkinFile(sprite_file)) - - if out.SpriteSheet == nil then - print "Sprite sheet couldn't be found. Sorry." - return nil - end - - out.Object = object or Engine:CreateObject() - local i = 0 - for k, v in pairs(out.SpriteSheet.Sprites) do - i = i + 1 - end - - out.TotalFrames = i or 1 - out.Duration = duration or 1 - - out.CurrentTime = 0 - - out.Object.Texture = out.SpriteSheet.File or "null" - - out:Update(0) - - return out -end - -function FrameInterpolator:GetFrameAtFrac(frac) - local Frame = math.floor(frac * (self.TotalFrames - 1)) - return Frame -end - -function FrameInterpolator:SetFraction(frac) - local fn = self.FilenameAssign(self:GetFrameAtFrac(frac)) - self.SpriteSheet:SetObjectCrop(self.Object, fn) -end - -function FrameInterpolator:GetLerp() - return clamp(self.CurrentTime / self.Duration, 0, 1) -end - -function FrameInterpolator:Update(delta) - self.CurrentTime = self.CurrentTime + delta - self:SetFraction(self:GetLerp()) -end diff --git a/GameData/Scripts/Histogram.lua b/GameData/Scripts/Histogram.lua deleted file mode 100644 index bbf06146..00000000 --- a/GameData/Scripts/Histogram.lua +++ /dev/null @@ -1,112 +0,0 @@ -game_require "librd" - -Histogram = {} -Histogram.__index = Histogram - -function Histogram:GenerateObjects(w, h) - local skeep = self.Player.Scorekeeper - local pnt_cnt = skeep.HistogramPointCount - - with (self, { - item_size = w / pnt_cnt, - Objects = {}, - Width = w, - Height = h, - X = 0, - Y = 0 - }) - - for i=1,pnt_cnt do - local ref = ScreenObject { - Texture = "Global/white.png", - Width = self.item_size - } - - self.Objects[i] = ref - -- x, y, and h are set in updatepoints - end - - self.centerSep = Engine:CreateObject() - self:UpdatePoints() -end - -function Histogram:UpdatePoints() - local skeep = self.Player.Scorekeeper - local top_point = skeep.HistogramHighestPoint - - for k,ref in pairs(self.Objects) do - -- change this 128 by the amount of ms + 1 the histogram covers - -- basically floor(point_count / 2) - - ref.X = self.item_size * (k - 1) + self.X - ref.Height = self.Height * skeep:GetHistogramPoint(k - 128) / top_point - ref.Y = self.Y - ref.Height + self.Height - - if (k - 128) == 0 then - with (self.centerSep, { - Texture = "Global/white.png", - Width = self.item_size, - Height = self.Height, - X = ref.X, - Y = self.Y - }) - end - - end -end - -function Histogram:SetLayer(layer) - if not layer then return end - - for k,v in pairs(self.Objects) do - v.Layer = layer - end - self.centerSep.Layer = layer - 1 - if self.bg then - self.bg.layer = layer - 2 - end -end - -function Histogram:SetColor(r, g, b) - for k,v in pairs(self.Objects) do - v.Red = r or v.Red - v.Green = g or v.Green - v.Blue = b or v.Blue - end - - self.centerSep.Red = r * 0.5 or self.centerSep.Red - self.centerSep.Green = g * 0.5 or self.centerSep.Green - self.centerSep.Blue = b * 0.5 or self.centerSep.Blue -end - -function Histogram:SetPosition(x, y) - self.X = x or self.X - self.Y = y or self.Y - self:UpdatePoints() -end - -function Histogram:SetBackground(image) - if self.bg then - self.bg.Image = image - else - self.bg = Engine:CreateObject() - self.bg.X = self.X - self.bg.Y = self.Y - self.bg.Texture = image - self.bg.Width = self.Width - self.bg.Height = self.Height - end - - return self.bg -end - -function Histogram:new(player, width, height, layer) - local out = {} - local skeep = player.Scorekeeper - - setmetatable(out, self) - out.Player = player - out:GenerateObjects(width or skeep.HistogramPointCount * 1.15, height or 100) - out:SetLayer(layer or 16) - return out -end diff --git a/GameData/Scripts/box.lua b/GameData/Scripts/box.lua deleted file mode 100644 index 46061afb..00000000 --- a/GameData/Scripts/box.lua +++ /dev/null @@ -1,161 +0,0 @@ -Box = { - TopLeft = 1, - Top = 2, - TopRight = 3, - Left = 4, - Center = 5, - Right = 6, - BottomLeft = 7, - Bottom = 8, - BottomRight = 9, - - - On = nil, - Off = 0, -} - -Box.__index = Box - ---[[ - If the index exists, use the specified corner if it's not zero, otherwise - use the default corner, and show it. - If the 'Transform' parameter exists. append to all objects this transformation. - - params.Width = The width of the interior of the box. Must be larger than the sum of the corners' width. - params.Height = Height of the interior of the box. Must be larger than corners. - params.Source = (optional) The image source. If non-existant, use Global/box.png. This picture will be divided into a 3x3 grid. - params[1..9] = Replace, on, or off sides. If on, is nil, if off, is 0, if we should replace, the index will be used according to the definitions in the Box table. - params.Layer = Set all objects to this layer, or 16 by default. - params.StretchCenter = if true (default) stretch the center all across the area of the box. -]] - -function Box:ModifyTextureBounds(index, Object) - if index == Box.TopLeft then - Object:SetCropByPixels(0, self.CellSize.Width, 0, self.CellSize.Height) - elseif index == Box.Top then - Object:SetCropByPixels(self.CellSize.Width, self.CellSize.Width * 2, 0, self.CellSize.Height) - elseif index == Box.TopRight then - Object:SetCropByPixels(self.CellSize.Width * 2, self.CellSize.Width * 3, 0, self.CellSize.Height) - elseif index == Box.Left then - Object:SetCropByPixels(0, self.CellSize.Width, self.CellSize.Height, self.CellSize.Height * 2) - elseif index == Box.Center then - Object:SetCropByPixels(self.CellSize.Width, self.CellSize.Width * 2, self.CellSize.Height, self.CellSize.Height * 2) - elseif index == Box.Right then - Object:SetCropByPixels(self.CellSize.Width * 2, self.CellSize.Width * 3, self.CellSize.Height, self.CellSize.Height * 2) - elseif index == Box.BottomLeft then - Object:SetCropByPixels(0, self.CellSize.Width, self.CellSize.Height * 2, self.CellSize.Height * 3) - elseif index == Box.Bottom then - Object:SetCropByPixels(self.CellSize.Width, self.CellSize.Width * 2, self.CellSize.Height * 2, self.CellSize.Height * 3) - elseif index == Box.BottomRight then - Object:SetCropByPixels(self.CellSize.Width * 2, self.CellSize.Width * 3, self.CellSize.Height * 2, self.CellSize.Height * 3) - end -end - -function Box:CreateCorner(index, replace) - - if replace == nil then - replace = index - end - - if replace == 0 then -- if it's nil, on the other hand.. - return - end - - local BoxStartInteriorX = self.CellSize.Width - local BoxStartInteriorY = self.CellSize.Height - local BoxBottom = BoxStartInteriorY + self.InteriorSize.Height - local BoxRight = BoxStartInteriorX + self.InteriorSize.Width - - self.Objects = self.Objects or {} - self.Objects[#self.Objects+1] = Engine:CreateObject() - local Object = self.Objects[#self.Objects] - Object.Image = self.Image - Object.Width = self.CellSize.Width - Object.Height = self.CellSize.Height - Object.Layer = self.Layer - - if self.Transform then - Object.ChainTransformation = self.Transform - end - - if index == Box.TopLeft then - Object.X = 0 - Object.Y = 0 - elseif index == Box.Top then - Object.X = BoxStartInteriorX - Object.Y = 0 - Object.Width = self.InteriorSize.Width - elseif index == Box.TopRight then - Object.X = BoxRight - Object.Y = 0 - elseif index == Box.Left then - Object.X = 0 - Object.Y = BoxStartInteriorY - Object.Height = self.InteriorSize.Height - elseif index == Box.Center then - if self.StretchCenter then - Object.X = 0 - Object.Y = 0 - Object.Width = self.InteriorSize.Width + self.CellSize.Width * 2 - Object.Height = self.InteriorSize.Height + self.CellSize.Height * 2 - Object.Layer = self.Layer-1 - else - Object.X = BoxStartInteriorX - Object.Y = BoxStartInteriorY - Object.Width = self.InteriorSize.Width - Object.Height = self.InteriorSize.Height - end - elseif index == Box.Right then - Object.X = BoxRight - Object.Y = BoxStartInteriorY - Object.Height = self.InteriorSize.Height - elseif index == Box.BottomLeft then - Object.X = 0 - Object.Y = BoxBottom - elseif index == Box.Bottom then - Object.X = BoxStartInteriorX - Object.Y = BoxBottom - Object.Width = self.InteriorSize.Width - elseif index == Box.BottomRight then - Object.X = BoxRight - Object.Y = BoxBottom - end - - self:ModifyTextureBounds(replace, Object) -end - -function Box:CreateCorners(params) - self.Image = params.Source or "Global/box.png" - self.Transform = params.Transform - self.Layer = params.Layer or 20 - - self.Master = Engine:CreateObject() - self.Master.Image = self.Image - self.Master.Alpha = 0 - - self.StretchCenter = params.StretchCenter or true - self.CellSize = { Width = self.Master.Width/3, Height = self.Master.Height/3 } - self.InteriorSize = { Width = params.Width or self.CellSize.Width, Height = params.Height or self.CellSize.Height } - - if self.Transform == nil then - print "no transform.." - end - - for i=1, 9 do - self:CreateCorner(i, params[i]) - end -end - -function Box:Create(params) - if type(params) ~= "table" then - return nil - end - - local out = {} - setmetatable(out, self) - out:CreateCorners(params) - - return out -end - -return Box diff --git a/GameData/Scripts/debug.lua b/GameData/Scripts/debug.lua deleted file mode 100644 index 5761c6c7..00000000 --- a/GameData/Scripts/debug.lua +++ /dev/null @@ -1,3 +0,0 @@ -package.path = package.path .. ";C:\\Windows\\SysWOW64\\ZeroBraneStudio\\lualibs\\?.lua" -package.cpath = package.cpath .. ";C:\\Windows\\SysWOW64\\ZeroBraneStudio\\bin\\clibs52\\?.dll" -require("mobdebug").start() diff --git a/GameData/Scripts/librd.lua b/GameData/Scripts/librd.lua deleted file mode 100644 index 6ea876a5..00000000 --- a/GameData/Scripts/librd.lua +++ /dev/null @@ -1,161 +0,0 @@ -function split(line, _sep) - local sep = _sep or "," - local restable = {} - local i = 1 - for k in string.gmatch(line, "([^" .. sep .. "]+)") do - restable[i] = k - i = i + 1 - end - - return restable -end - -string.split = split - -floor = math.floor -ceil = math.ceil -sin = math.sin -cos = math.cos -min = math.min -max = math.max -pow = math.pow - -function clamp (v, mn, mx) - return math.min(math.max(v, mn), mx) -end - -function sign(x) - if x == 0 then return 0 end - if x > 0 then return 1 else return -1 end -end - -function sum(l) - local rt = 0 - for k,v in ipairs(l) do - rt = rt + v - end - return rt -end - -table.join = function (a, b) - local ret = {} - for k,v in pairs(a) do - ret[k] = v - end - for k,v in pairs(b) do - ret[k] = v - end - return ret -end - -table.dump = function (a) - print ("a = {") - for k,v in pairs(a) do - print (k, "=", v, ",") - end - print ("}") -end - -function lerp(current, start, finish, startval, endval) - return (current - start) * (endval - startval) / (finish - start) + startval -end - -function clerp(c, s, f, sv, ev) - return clamp(lerp(c,s,f,sv,ev), sv, ev) -end - -function mix(r, s, e) - return lerp(r, 0, 1, s, e) -end - -function cmix(r, s, e) - return clerp(r, 0, 1, s, e) -end - -function fract(d) - return d - floor(d) -end - -function with(obj, t) - if type(t) == "table" then - for k,v in pairs(t) do - obj[k] = v - end - end - return obj -end - -function map(f, t) - local ret = {} - for k,v in pairs(t) do - ret[k] = f(v) - end - return ret -end - ---[[ - Warning: using pairs (underlying on the with) - does not guarantee order. If you're setting image with - ScreenObject or with, you could potentially end up - side effect'd at the wrong order. -]] -function ScreenObject(t) - local x = Engine:CreateObject() - return with(x, t) -end - -function AdjustInBox(transform, params) - params = params or {x = 0, y = 0, w = ScreenWidth, h = ScreenHeight} - local x = params.x or 0 - local y = params.y or 0 - local w = params.w or ScreenWidth - local h = params.h or ScreenHeight - local oldWidth = transform.Width - local oldHeight = transform.Height - local Background = transform or params.Background - - if not Background then return end - - Background.X = x - Background.Y = y - - local VRatio = h / Background.Height - - Background.ScaleX = VRatio - Background.ScaleY = VRatio - - local modWidth = Background.ScaleX * Background.Width - local modHeight = Background.ScaleY * Background.Height - - -- Center in box. - Background.X = x + w / 2 - modWidth / 2 - Background.Y = y + h / 2 - modHeight / 2 -end - -librd = { - make_new = function (t, initializer) - assert(initializer) - t.__index = t - t.new = function (self, rt) - local ret = {} - setmetatable(ret, self) - with(t, rt) - initializer(ret) - return ret - end - return t - end, - intToDigits = function(i, base) - local b = base or 10 - local ret = {} - i = floor(i) - while i >= 1 do - local rem = floor(i) % b - table.insert(ret, 1, rem) - i = floor(i / 10) - end - return ret - end -} - -return librd diff --git a/GameData/Scripts/noteskin_defs.lua b/GameData/Scripts/noteskin_defs.lua deleted file mode 100644 index f923a25d..00000000 --- a/GameData/Scripts/noteskin_defs.lua +++ /dev/null @@ -1,882 +0,0 @@ -require "utils" -require "librd" - --- Auxiliary variables. -NoteImage1 = "VSRG/note1.png" -NoteImage2 = "VSRG/note2.png" -NoteImage3 = "VSRG/note3.png" -NoteImage4 = "VSRG/note4.png" -NoteImage5 = "VSRG/note5.png" -NoteImageHold1 = "VSRG/note1L.png" -NoteImageHold2 = "VSRG/note2L.png" -NoteImageHold3 = "VSRG/note3L.png" -NoteImageHold4 = "VSRG/note4L.png" -NoteImageHold5 = "VSRG/note5L.png" - -GearStartX = 80 - -Channels1Sizes = { - 84 -} - -Channels4Sizes = { - 84, - 84, - 84, - 84 -} - -Channels5Sizes = { - 67, - 67, - 67, - 67, - 67 -} - -Channels6Sizes = { - 56, - 56, - 56, - 56, - 56, - 56 -} - -Channels7Sizes = { - 48, - 48, - 48, - 48, - 48, - 48, - 48 -} - -Channels8Sizes = { - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42 -} - - -Channels9Sizes = { - 40, - 40, - 40, - 40, - 40, - 40, - 40, - 40, - 40 -} - -Channels10Sizes = { - 36, - 36, - 36, - 36, - 36, - 36, - 36, - 36, - 36, - 36 -} - -Channels12Sizes = { - 56, - 56, - 56, - 56, - 56, - 56, - 56, - 56, - 56, - 56, - 56, - 56 -} - -Channels16Sizes = { - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42, - 42 -} - -Channels6SizesSpecial = {} -Channels8SizesSpecial = {} -for k,v in ipairs(Channels8Sizes) do - if k == 1 then - Channels6SizesSpecial[k] = Channels6Sizes[k] * 1.3 - Channels8SizesSpecial[k] = v * 1.3 - else - Channels6SizesSpecial[k] = Channels6Sizes[k] - Channels8SizesSpecial[k] = v - end -end - -Channels1Positions = {} -Channels4Positions = {} -Channels5Positions = {} -Channels6Positions = {} -Channels6PositionsSpecial = {} -Channels7Positions = {} -Channels8Positions = {} -Channels8PositionsSpecial = {} -Channels9Positions = {} -Channels10Positions = {} -Channels12Positions = {} -Channels16Positions = {} - -GearWidths = {} - -Sizeup(Channels1Positions, Channels1Sizes, 1) -Sizeup(Channels4Positions, Channels4Sizes, 4) -Sizeup(Channels5Positions, Channels5Sizes, 5) - -Sizeup(Channels6PositionsSpecial, Channels6SizesSpecial, 6) -Sizeup(Channels6Positions, Channels6Sizes, 6) - -Sizeup(Channels7Positions, Channels7Sizes, 7) - -Sizeup(Channels8PositionsSpecial, Channels8SizesSpecial, 8) -Sizeup(Channels8Positions, Channels8Sizes, 8) - -Sizeup(Channels9Positions, Channels9Sizes, 9) -Sizeup(Channels10Positions, Channels10Sizes, 10) -Sizeup(Channels12Positions, Channels12Sizes, 12) -Sizeup(Channels16Positions, Channels16Sizes, 16) - -Special8KWidth = sum(Channels8SizesSpecial) -Special6KWidth = sum(Channels6SizesSpecial) - -GearHeightCommon = 135 - --- Actual channels configuration. --- Lane X positions are always centered. --- - -C1 = "key1.png" -C1D = "key1d.png" -C2 = "key2.png" -C2D = "key2d.png" -C3 = "key3.png" -C3D = "key3d.png" -C4 = "key4.png" -C4D = "key4d.png" -C5 = "key5.png" -C5D = "key5d.png" - --- Channels16 is, of course, DP. -Channels16 = { - -- Gear bindings - - Key1 = C3, -- scratch channel on the left side - Key2 = C1, - Key3 = C2, - Key4 = C1, - Key5 = C2, - Key6 = C1, - Key7 = C2, - Key8 = C1, - - Key9 = C3, -- scratch channel on the right side - Key10 = C1, - Key11 = C2, - Key12 = C1, - Key13 = C2, - Key14 = C1, - Key15 = C2, - Key16 = C1, - - - Key1Down = C3D, -- scratch channel on the left side - Key2Down = C1D, - Key3Down = C2D, - Key4Down = C1D, - Key5Down = C2D, - Key6Down = C1D, - Key7Down = C2D, - Key8Down = C1D, - - Key9Down = C3D, -- scratch channel on the right side - Key10Down = C1D, - Key11Down = C2D, - Key12Down = C1D, - Key13Down = C2D, - Key14Down = C1D, - Key15Down = C2D, - Key16Down = C1D, - - GearHeight = GearHeightCommon, - GearWidth = GearWidthByChannels[16], - GearStartX = 80, - NoteHeight = 16, - BarlineWidth = GearWidthByChannels[16], - - -- Note Images - Key1Image = NoteImage3, - Key2Image = NoteImage1, - Key3Image = NoteImage2, - Key4Image = NoteImage1, - Key5Image = NoteImage2, - Key6Image = NoteImage1, - Key7Image = NoteImage2, - Key8Image = NoteImage1, - - Key9Image = NoteImage3, - Key10Image = NoteImage1, - Key11Image = NoteImage2, - Key12Image = NoteImage1, - Key13Image = NoteImage2, - Key14Image = NoteImage1, - Key15Image = NoteImage2, - Key16Image = NoteImage1, - - -- Hold Bodies - Key1HoldImage = NoteImageHold3, - Key2HoldImage = NoteImageHold1, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold1, - Key5HoldImage = NoteImageHold2, - Key6HoldImage = NoteImageHold1, - Key7HoldImage = NoteImageHold2, - Key8HoldImage = NoteImageHold1, - - Key9HoldImage = NoteImageHold3, - Key10HoldImage = NoteImageHold1, - Key11HoldImage = NoteImageHold2, - Key12HoldImage = NoteImageHold1, - Key13HoldImage = NoteImageHold2, - Key14HoldImage = NoteImageHold1, - Key15HoldImage = NoteImageHold2, - Key16HoldImage = NoteImageHold1, - - -- Lane positions - Key1X = Channels16Positions[1], - Key2X = Channels16Positions[2], - Key3X = Channels16Positions[3], - Key4X = Channels16Positions[4], - Key5X = Channels16Positions[5], - Key6X = Channels16Positions[6], - Key7X = Channels16Positions[7], - Key8X = Channels16Positions[8], - - Key9X = Channels16Positions[16], - Key10X = Channels16Positions[10], - Key11X = Channels16Positions[11], - Key12X = Channels16Positions[12], - Key13X = Channels16Positions[13], - Key14X = Channels16Positions[14], - Key15X = Channels16Positions[15], - Key16X = Channels16Positions[9], - - -- Lane Widths - Key1Width = Channels16Sizes[1], - Key2Width = Channels16Sizes[2], - Key3Width = Channels16Sizes[3], - Key4Width = Channels16Sizes[4], - Key5Width = Channels16Sizes[5], - Key6Width = Channels16Sizes[6], - Key7Width = Channels16Sizes[7], - Key8Width = Channels16Sizes[8], - Key9Width = Channels16Sizes[16], - Key10Width = Channels16Sizes[10], - Key11Width = Channels16Sizes[11], - Key12Width = Channels16Sizes[12], - Key13Width = Channels16Sizes[13], - Key14Width = Channels16Sizes[14], - Key15Width = Channels16Sizes[15], - Key16Width = Channels16Sizes[9] -} - --- Channels12 is of course, 5k DP. -Channels12 = { - -- Gear bindings - Key1 = C3, -- scratch channel on the left side - Key2 = C1, - Key3 = C2, - Key4 = C1, - Key5 = C2, - Key6 = C1, - - Key7 = C3, -- scratch channel on the right side - Key8 = C1, - Key9 = C2, - Key10 =C1, - Key11 =C2, - Key12 =C1, - - Key1Down = C3D, -- scratch channel on the left side - Key2Down = C1D, - Key3Down = C2D, - Key4Down = C1D, - Key5Down = C2D, - Key6Down = C1D, - - Key7Down = C3D, -- scratch channel on the right side - Key8Down = C1D, - Key9Down = C2D, - Key10Down =C1D, - Key11Down =C2D, - Key12Down =C1D, - - GearHeight = GearHeightCommon, - GearWidth = GearWidthByChannels[12], - GearStartX = 80, - NoteHeight = 16, - BarlineWidth = GearWidthByChannels[12], - - -- Note Images - Key1Image = NoteImage3, - Key2Image = NoteImage1, - Key3Image = NoteImage2, - Key4Image = NoteImage1, - Key5Image = NoteImage2, - Key6Image = NoteImage1, - - Key7Image = NoteImage3, - Key8Image = NoteImage1, - Key9Image = NoteImage2, - Key10Image = NoteImage1, - Key11Image = NoteImage2, - Key12Image = NoteImage1, - - -- Hold Bodies - Key1HoldImage = NoteImageHold3, - Key2HoldImage = NoteImageHold1, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold1, - Key5HoldImage = NoteImageHold2, - Key6HoldImage = NoteImageHold1, - - Key7HoldImage = NoteImageHold3, - Key8HoldImage = NoteImageHold1, - Key9HoldImage = NoteImageHold2, - Key10HoldImage = NoteImageHold1, - Key11HoldImage = NoteImageHold2, - Key12HoldImage = NoteImageHold1, - - -- Lane positions - Key1X = Channels12Positions[1], - Key2X = Channels12Positions[2], - Key3X = Channels12Positions[3], - Key4X = Channels12Positions[4], - Key5X = Channels12Positions[5], - Key6X = Channels12Positions[6], - - Key7X = Channels12Positions[12], - Key8X = Channels12Positions[8], - Key9X = Channels12Positions[9], - Key10X = Channels12Positions[10], - Key11X = Channels12Positions[11], - Key12X = Channels12Positions[7], - - -- Lane Widths - Key1Width = Channels12Sizes[1], - Key2Width = Channels12Sizes[2], - Key3Width = Channels12Sizes[3], - Key4Width = Channels12Sizes[4], - Key5Width = Channels12Sizes[5], - Key6Width = Channels12Sizes[6], - Key7Width = Channels12Sizes[12], - Key8Width = Channels12Sizes[8], - Key9Width = Channels12Sizes[9], - Key10Width = Channels12Sizes[10], - Key11Width = Channels12Sizes[11], - Key12Width = Channels12Sizes[7] -} --- Channels9 is, by default, pop'n like. -Channels9 = { - Key1 = C4, - Key2 = C1, - Key3 = C2, - Key4 = C3, - Key5 = C5, - Key6 = C3, - Key7 = C2, - Key8 = C1, - Key9 = C4, - Key1Down = C4D, - Key2Down = C1D, - Key3Down = C2D, - Key4Down = C3D, - Key5Down = C5D, - Key6Down = C3D, - Key7Down = C2D, - Key8Down = C1D, - Key9Down = C4D, - - GearHeight = GearHeightCommon, - GearStartX = 80, - NoteHeight = 16, - GearWidth = GearWidthByChannels[9], - BarlineWidth = GearWidthByChannels[9], - - -- Note Images - Key1Image = NoteImage4, - Key2Image = NoteImage1, - Key3Image = NoteImage2, - Key4Image = NoteImage3, - Key5Image = NoteImage5, - Key6Image = NoteImage3, - Key7Image = NoteImage2, - Key8Image = NoteImage1, - Key9Image = NoteImage4, - - -- Hold Bodies - Key1HoldImage = NoteImageHold4, - Key2HoldImage = NoteImageHold1, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold3, - Key5HoldImage = NoteImageHold5, - Key6HoldImage = NoteImageHold3, - Key7HoldImage = NoteImageHold2, - Key8HoldImage = NoteImageHold1, - Key9HoldImage = NoteImageHold4, - - -- Lane positions - Key1X = Channels9Positions[1], - Key2X = Channels9Positions[2], - Key3X = Channels9Positions[3], - Key4X = Channels9Positions[4], - Key5X = Channels9Positions[5], - Key6X = Channels9Positions[6], - Key7X = Channels9Positions[7], - Key8X = Channels9Positions[8], - Key9X = Channels9Positions[9], - -- Lane Widths - Key1Width = Channels9Sizes[1], - Key2Width = Channels9Sizes[2], - Key3Width = Channels9Sizes[3], - Key4Width = Channels9Sizes[4], - Key5Width = Channels9Sizes[5], - Key6Width = Channels9Sizes[6], - Key7Width = Channels9Sizes[7], - Key8Width = Channels9Sizes[8], - Key9Width = Channels9Sizes[9], - -} - --- Channels8 is, by default, 7k+1. Key1 is always the scratch channel. -Channels8Special = { - -- Gear bindings - Key1 = C5, - Key2 = C4, - Key3 = C1, - Key4 = C2, - Key5 = C3, - Key6 = C2, - Key7 = C1, - Key8 = C4, - Key1Down = C5D, - Key2Down = C4D, - Key3Down = C1D, - Key4Down = C2D, - Key5Down = C3D, - Key6Down = C2D, - Key7Down = C1D, - Key8Down = C4D, - - GearHeight = GearHeightCommon, - GearStartX = 80, - NoteHeight = 16, - GearWidth = Special8KWidth, - BarlineWidth = Special8KWidth, - - -- Note Images - Key1Image = NoteImage5, - Key2Image = NoteImage4, - Key3Image = NoteImage1, - Key4Image = NoteImage2, - Key5Image = NoteImage3, - Key6Image = NoteImage2, - Key7Image = NoteImage1, - Key8Image = NoteImage4, - - -- Hold Bodies - Key1HoldImage = NoteImageHold5, - Key2HoldImage = NoteImageHold4, - Key3HoldImage = NoteImageHold1, - Key4HoldImage = NoteImageHold2, - Key5HoldImage = NoteImageHold3, - Key6HoldImage = NoteImageHold2, - Key7HoldImage = NoteImageHold1, - Key8HoldImage = NoteImageHold4, - - -- Lane positions - Key1X = Channels8PositionsSpecial[1], - Key2X = Channels8PositionsSpecial[2], - Key3X = Channels8PositionsSpecial[3], - Key4X = Channels8PositionsSpecial[4], - Key5X = Channels8PositionsSpecial[5], - Key6X = Channels8PositionsSpecial[6], - Key7X = Channels8PositionsSpecial[7], - Key8X = Channels8PositionsSpecial[8], - - -- Lane Widths - Key1Width = Channels8SizesSpecial[1], - Key2Width = Channels8SizesSpecial[2], - Key3Width = Channels8SizesSpecial[3], - Key4Width = Channels8SizesSpecial[4], - Key5Width = Channels8SizesSpecial[5], - Key6Width = Channels8SizesSpecial[6], - Key7Width = Channels8SizesSpecial[7], - Key8Width = Channels8SizesSpecial[8] - } - - Channels8 = { - -- Gear bindings - Key1 = C1, - Key2 = C2, - Key3 = C3, - Key4 = C5, - Key5 = C5, - Key6 = C3, - Key7 = C2, - Key8 = C1, - Key1Down = C1D, - Key2Down = C2D, - Key3Down = C3D, - Key4Down = C5D, - Key5Down = C5D, - Key6Down = C3D, - Key7Down = C2D, - Key8Down = C1D, - - GearHeight = GearHeightCommon, - GearStartX = 80, - NoteHeight = 16, - GearWidth = GearWidthByChannels[8], - BarlineWidth = GearWidthByChannels[8], - - -- Note Images - Key1Image = NoteImage1, - Key2Image = NoteImage2, - Key3Image = NoteImage3, - Key4Image = NoteImage5, - Key5Image = NoteImage5, - Key6Image = NoteImage3, - Key7Image = NoteImage2, - Key8Image = NoteImage1, - - -- Hold Bodies - Key1HoldImage = NoteImageHold1, - Key2HoldImage = NoteImageHold2, - Key3HoldImage = NoteImageHold3, - Key4HoldImage = NoteImageHold5, - Key5HoldImage = NoteImageHold5, - Key6HoldImage = NoteImageHold3, - Key7HoldImage = NoteImageHold2, - Key8HoldImage = NoteImageHold1, - - -- Lane positions - Key1X = Channels8Positions[1], - Key2X = Channels8Positions[2], - Key3X = Channels8Positions[3], - Key4X = Channels8Positions[4], - Key5X = Channels8Positions[5], - Key6X = Channels8Positions[6], - Key7X = Channels8Positions[7], - Key8X = Channels8Positions[8], - - -- Lane Widths - Key1Width = Channels8Sizes[1], - Key2Width = Channels8Sizes[2], - Key3Width = Channels8Sizes[3], - Key4Width = Channels8Sizes[4], - Key5Width = Channels8Sizes[5], - Key6Width = Channels8Sizes[6], - Key7Width = Channels8Sizes[7], - Key8Width = Channels8Sizes[8] - } - --- 7 Channels. By default, it's o2jam-like. -Channels7 = { - -- Gear bindings - Key1 = C4, - Key2 = C1, - Key3 = C2, - Key4 = C3, - Key5 = C2, - Key6 = C1, - Key7 = C4, - Key1Down = C4D, - Key2Down = C1D, - Key3Down = C2D, - Key4Down = C3D, - Key5Down = C2D, - Key6Down = C1D, - Key7Down = C4D, - - GearHeight = GearHeightCommon, - GearStartX = 80, - NoteHeight = 16, - GearWidth = GearWidthByChannels[7], - BarlineWidth = GearWidthByChannels[7], - - -- Note Images - Key1Image = NoteImage4, - Key2Image = NoteImage1, - Key3Image = NoteImage2, - Key4Image = NoteImage3, - Key5Image = NoteImage2, - Key6Image = NoteImage1, - Key7Image = NoteImage4, - - -- Hold Bodies - Key1HoldImage = NoteImageHold4, - Key2HoldImage = NoteImageHold1, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold3, - Key5HoldImage = NoteImageHold2, - Key6HoldImage = NoteImageHold1, - Key7HoldImage = NoteImageHold4, - - -- Lane positions - Key1X = Channels7Positions[1], - Key2X = Channels7Positions[2], - Key3X = Channels7Positions[3], - Key4X = Channels7Positions[4], - Key5X = Channels7Positions[5], - Key6X = Channels7Positions[6], - Key7X = Channels7Positions[7], - - -- Lane Widths - Key1Width = Channels7Sizes[1], - Key2Width = Channels7Sizes[2], - Key3Width = Channels7Sizes[3], - Key4Width = Channels7Sizes[4], - Key5Width = Channels7Sizes[5], - Key6Width = Channels7Sizes[6], - Key7Width = Channels7Sizes[7], -} - --- 6 Channels. By default, it's solo-like. - Channels6Special = { - Key1 = C5, - Key2 = C1, - Key3 = C2, - Key4 = C1, - Key5 = C2, - Key6 = C1, - Key1Down = C5D, - Key2Down = C1D, - Key3Down = C2D, - Key4Down = C1D, - Key5Down = C2D, - Key6Down = C1D, - Key1Image = NoteImage5, - Key2Image = NoteImage1, - Key3Image = NoteImage2, - Key4Image = NoteImage1, - Key5Image = NoteImage2, - Key6Image = NoteImage1, - Key1HoldImage = NoteImageHold5, - Key2HoldImage = NoteImageHold1, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold1, - Key5HoldImage = NoteImageHold2, - Key6HoldImage = NoteImageHold1, - - GearHeight = GearHeightCommon, - Key1Width = Channels6SizesSpecial[1], - Key2Width = Channels6SizesSpecial[2], - Key3Width = Channels6SizesSpecial[3], - Key4Width = Channels6SizesSpecial[4], - Key5Width = Channels6SizesSpecial[5], - Key6Width = Channels6SizesSpecial[6], - Key1X = Channels6PositionsSpecial[1], - Key2X = Channels6PositionsSpecial[2], - Key3X = Channels6PositionsSpecial[3], - Key4X = Channels6PositionsSpecial[4], - Key5X = Channels6PositionsSpecial[5], - Key6X = Channels6PositionsSpecial[6], - - GearWidth = Special6KWidth, - BarlineWidth = Special6KWidth, - GearStartX = 80, - NoteHeight = 16 - } - - Channels6 = { - Key1 = C4, - Key2 = C1, - Key3 = C2, - Key4 = C2, - Key5 = C1, - Key6 = C4, - Key1Down = C4D, - Key2Down = C1D, - Key3Down = C2D, - Key4Down = C2D, - Key5Down = C1D, - Key6Down = C4D, - Key1Image = NoteImage4, - Key2Image = NoteImage1, - Key3Image = NoteImage2, - Key4Image = NoteImage2, - Key5Image = NoteImage1, - Key6Image = NoteImage4, - Key1HoldImage = NoteImageHold4, - Key2HoldImage = NoteImageHold1, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold2, - Key5HoldImage = NoteImageHold1, - Key6HoldImage = NoteImageHold4, - - GearHeight = GearHeightCommon, - - Key1Width = Channels6Sizes[1], - Key2Width = Channels6Sizes[2], - Key3Width = Channels6Sizes[3], - Key4Width = Channels6Sizes[4], - Key5Width = Channels6Sizes[5], - Key6Width = Channels6Sizes[6], - Key1X = Channels6Positions[1], - Key2X = Channels6Positions[2], - Key3X = Channels6Positions[3], - Key4X = Channels6Positions[4], - Key5X = Channels6Positions[5], - Key6X = Channels6Positions[6], - - GearWidth = GearWidthByChannels[6], - BarlineWidth = GearWidthByChannels[6], - GearStartX = 80, - NoteHeight = 16 - } - --- 5 Channels. By default, ez2dj-like. -Channels5 = { - Key1 = C1, - Key2 = C2, - Key3 = C3, - Key4 = C2, - Key5 = C1, - Key1Down = C1D, - Key2Down = C2D, - Key3Down = C3D, - Key4Down = C2D, - Key5Down = C1D, - GearHeight = GearHeightCommon, - Key1Binding = 2, - Key2Binding = 3, - Key3Binding = 4, - Key4Binding = 5, - Key5Binding = 6, - Key1Image = NoteImage1, - Key2Image = NoteImage2, - Key3Image = NoteImage3, - Key4Image = NoteImage2, - Key5Image = NoteImage1, - Key1HoldImage = NoteImageHold1, - Key2HoldImage = NoteImageHold2, - Key3HoldImage = NoteImageHold3, - Key4HoldImage = NoteImageHold2, - Key5HoldImage = NoteImageHold1, - Key1Width = Channels5Sizes[1], - Key2Width = Channels5Sizes[2], - Key3Width = Channels5Sizes[3], - Key4Width = Channels5Sizes[4], - Key5Width = Channels5Sizes[5], - Key1X = Channels5Positions[1], - Key2X = Channels5Positions[2], - Key3X = Channels5Positions[3], - Key4X = Channels5Positions[4], - Key5X = Channels5Positions[5], - GearWidth = GearWidthByChannels[5], - BarlineWidth = GearWidthByChannels[5], - GearStartX = 80, - NoteHeight = 16 -} - --- 4 Channels. By default, it's DJMax-like. -Channels4 = { - Key1 = C1, - Key2 = C2, - Key3 = C2, - Key4 = C1, - Key1Down = C1D, - Key2Down = C2D, - Key3Down = C2D, - Key4Down = C1D, - GearHeight = GearHeightCommon, - Key1Binding = 2, - Key2Binding = 3, - Key3Binding = 5, - Key4Binding = 6, - Key1Image = NoteImage1, - Key2Image = NoteImage2, - Key3Image = NoteImage2, - Key4Image = NoteImage1, - Key1HoldImage = NoteImageHold1, - Key2HoldImage = NoteImageHold2, - Key3HoldImage = NoteImageHold2, - Key4HoldImage = NoteImageHold1, - Key1Width = Channels4Sizes[1], - Key2Width = Channels4Sizes[2], - Key3Width = Channels4Sizes[3], - Key4Width = Channels4Sizes[4], - Key1X = Channels4Positions[1], - Key2X = Channels4Positions[2], - Key3X = Channels4Positions[3], - Key4X = Channels4Positions[4], - GearWidth = GearWidthByChannels[4], - BarlineWidth = GearWidthByChannels[4], - GearStartX = 80, - NoteHeight = 16 -} - -Channels1 = { - Key1 = C3, - Key1Down = C3D, - GearHeight = GearHeightCommon, - Key1Binding = 3, - Key1Image = NoteImage3, - Key1HoldImage = NoteImageHold3, - Key1Width = Channels1Sizes[1], - Key1X = Channels1Positions[1], - GearWidth = GearWidthByChannels[1], - BarlineWidth = GearWidthByChannels[1], - GearStartX = 80, - NoteHeight = 16 -} - -Noteskin = {} -Noteskin[1] = Channels1 -- Yes, I'm completely serious. -Noteskin[4] = Channels4 -Noteskin[5] = Channels5 -Noteskin[6] = Channels6 -Noteskin[7] = Channels7 -Noteskin[8] = Channels8 -Noteskin[9] = Channels9 -Noteskin[12] = Channels12 -Noteskin[16] = Channels16 -Noteskin.__index = Noteskin -NoteskinSpecial = {} -NoteskinSpecial[6] = Channels6Special -NoteskinSpecial[8] = Channels8Special -setmetatable(NoteskinSpecial, Noteskin) - -skin_require("custom_defs") diff --git a/GameData/Scripts/textureatlas.lua b/GameData/Scripts/textureatlas.lua deleted file mode 100644 index 35facd06..00000000 --- a/GameData/Scripts/textureatlas.lua +++ /dev/null @@ -1,65 +0,0 @@ -game_require "librd" - -TextureAtlas = {} -TextureAtlas.__index = TextureAtlas - -function TextureAtlas:SetObjectCrop(Object, Sprite, resize) - local Tab = self.Sprites[Sprite] - if Tab ~= nil then - Object:SetCropByPixels(Tab.x, Tab.x + Tab.w, Tab.y, Tab.y + Tab.h) - if resize then - Object.Width = Tab.w - Object.Height = Tab.h - end - else - print ("TextureAtlas: ", self.File," Picture not found: ", Sprite) - print ("Available: ") - for k,v in pairs(self.Sprites) do - print ("\t", k) - end - end -end - -function TextureAtlas:AssignFrames(Filename) - local Atlas = self - local File = io.open(Filename) - - if File ~= nil then - - for line in File:lines() do - if Atlas.File == nil then - Atlas.File = line; - Atlas.Sprites = {} - else - local restable = split(line) - - Sprite = { - x = tonumber(restable[2]), - y = tonumber(restable[3]), - w = tonumber(restable[4]), - h = tonumber(restable[5]) - } - - Atlas.Sprites[restable[1]] = Sprite - end - end - - io.close(File) - else - print("Error opening " .. Filename .. ". Atlas won't be constructed.") - end -end - -function TextureAtlas:new(filename) - local NewAtlas = {} - setmetatable(NewAtlas, TextureAtlas) - NewAtlas:AssignFrames(filename) - return NewAtlas -end - -function TextureAtlas:skin_new(filename) - return self:new(GetSkinFile(filename)) -end - -return TextureAtlas - diff --git a/GameData/Scripts/utils.lua b/GameData/Scripts/utils.lua deleted file mode 100644 index af52dc36..00000000 --- a/GameData/Scripts/utils.lua +++ /dev/null @@ -1,22 +0,0 @@ -GearWidthByChannels = {} - --- Calculate the positions of lanes based off their sizes and a start position. -function Sizeup(Pos, Size, Num, Pad) - local GearWidthChannel = 0 - local pad = Pad or 0 - - for i=1, Num do - GearWidthChannel = GearWidthChannel + Size[i] + pad - end - - GearWidthByChannels[Num] = GearWidthChannel - - Pos[1] = Size[1] / 2 + GearStartX - for i=2, Num do - Pos[i] = Pos[i-1] + Size[i-1] / 2 + Size[i] / 2 + pad - end -end - -function AutoadjustBackground(params) - AdjustInBox(Background, params) -end diff --git a/GameData/Skins/arctichare/5k.csv b/GameData/Skins/arctichare/5k.csv deleted file mode 100644 index 99828fae..00000000 --- a/GameData/Skins/arctichare/5k.csv +++ /dev/null @@ -1,8 +0,0 @@ -assets/lane-5k.png,bglane,FieldStartX,0,300,1560,2 -assets/lane-scratch.png,slane,ScratchX,0,200,1560,2 -assets/5k-main.png,gear,FieldStartX,1560,300,260,17 -assets/5k-uv.png,uv,FieldStartX,1820,300,150,17 -assets/scratch-base.png,sbase,ScratchX,1560,200,260,17 -assets/scratch-uv.png,suv,ScratchX,1820,200,150,17 -assets/lane-separator.png,lanesep,LaneSepX,0,40,2010,18 -assets/lane-separator.png,lanesep,LaneSep2X,0,40,2010,18 \ No newline at end of file diff --git a/GameData/Skins/arctichare/7k.csv b/GameData/Skins/arctichare/7k.csv deleted file mode 100644 index 098463b9..00000000 --- a/GameData/Skins/arctichare/7k.csv +++ /dev/null @@ -1,8 +0,0 @@ -assets/lane-7k.png,bglane,FieldStartX,0,400,1560,2 -assets/lane-scratch.png,slane,ScratchX,0,200,1560,2 -assets/7k-main.png,gear,FieldStartX,1560,400,260,17 -assets/7k-uv.png,uv,FieldStartX,1820,400,150,17 -assets/scratch-base.png,sbase,ScratchX,1560,200,260,17 -assets/scratch-uv.png,suv,ScratchX,1820,200,150,17 -assets/lane-separator.png,lanesep,LaneSepX,0,40,2010,18 -assets/lane-separator.png,lanesep,LaneSep2X,0,40,2010,18 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/5k-main.png b/GameData/Skins/arctichare/assets/5k-main.png deleted file mode 100644 index f9e9dbcf..00000000 Binary files a/GameData/Skins/arctichare/assets/5k-main.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/5k-uv.png b/GameData/Skins/arctichare/assets/5k-uv.png deleted file mode 100644 index 02e2cf39..00000000 Binary files a/GameData/Skins/arctichare/assets/5k-uv.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/7k-main.png b/GameData/Skins/arctichare/assets/7k-main.png deleted file mode 100644 index da2b8faf..00000000 Binary files a/GameData/Skins/arctichare/assets/7k-main.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/7k-uv.png b/GameData/Skins/arctichare/assets/7k-uv.png deleted file mode 100644 index 9b807ad5..00000000 Binary files a/GameData/Skins/arctichare/assets/7k-uv.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/autoplay.png b/GameData/Skins/arctichare/assets/autoplay.png deleted file mode 100644 index 30491f90..00000000 Binary files a/GameData/Skins/arctichare/assets/autoplay.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/combo-full.png b/GameData/Skins/arctichare/assets/combo-full.png deleted file mode 100644 index fbb71753..00000000 Binary files a/GameData/Skins/arctichare/assets/combo-full.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/digits-big.csv b/GameData/Skins/arctichare/assets/digits-big.csv deleted file mode 100644 index ce9d451a..00000000 --- a/GameData/Skins/arctichare/assets/digits-big.csv +++ /dev/null @@ -1,12 +0,0 @@ -digits-big.png -digits-X.png,0,0,70,120 -digits-0.png,70,0,70,120 -digits-1.png,140,0,70,120 -digits-2.png,210,0,70,120 -digits-3.png,280,0,70,120 -digits-4.png,350,0,70,120 -digits-5.png,420,0,70,120 -digits-6.png,490,0,70,120 -digits-7.png,560,0,70,120 -digits-8.png,630,0,70,120 -digits-9.png,700,0,70,120 diff --git a/GameData/Skins/arctichare/assets/digits-big.png b/GameData/Skins/arctichare/assets/digits-big.png deleted file mode 100644 index 2560061a..00000000 Binary files a/GameData/Skins/arctichare/assets/digits-big.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/digits-small.csv b/GameData/Skins/arctichare/assets/digits-small.csv deleted file mode 100644 index 9aa47a05..00000000 --- a/GameData/Skins/arctichare/assets/digits-small.csv +++ /dev/null @@ -1,12 +0,0 @@ -digits-small.png -digits-X.png,0,0,40,70 -digits-0.png,40,0,40,70 -digits-1.png,80,0,40,70 -digits-2.png,120,0,40,70 -digits-3.png,160,0,40,70 -digits-4.png,200,0,40,70 -digits-5.png,240,0,40,70 -digits-6.png,280,0,40,70 -digits-7.png,320,0,40,70 -digits-8.png,360,0,40,70 -digits-9.png,400,0,40,70 diff --git a/GameData/Skins/arctichare/assets/digits-small.png b/GameData/Skins/arctichare/assets/digits-small.png deleted file mode 100644 index 5bdf3766..00000000 Binary files a/GameData/Skins/arctichare/assets/digits-small.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/explosion-blue.png b/GameData/Skins/arctichare/assets/explosion-blue.png deleted file mode 100644 index 76e2c076..00000000 Binary files a/GameData/Skins/arctichare/assets/explosion-blue.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/explosion-green.png b/GameData/Skins/arctichare/assets/explosion-green.png deleted file mode 100644 index 60cc2aeb..00000000 Binary files a/GameData/Skins/arctichare/assets/explosion-green.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/explosion-hold.png b/GameData/Skins/arctichare/assets/explosion-hold.png deleted file mode 100644 index b184522d..00000000 Binary files a/GameData/Skins/arctichare/assets/explosion-hold.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/explosion-puff.png b/GameData/Skins/arctichare/assets/explosion-puff.png deleted file mode 100644 index cbb622a4..00000000 Binary files a/GameData/Skins/arctichare/assets/explosion-puff.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/explosion-red.png b/GameData/Skins/arctichare/assets/explosion-red.png deleted file mode 100644 index 766be354..00000000 Binary files a/GameData/Skins/arctichare/assets/explosion-red.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/explosion-ripple.png b/GameData/Skins/arctichare/assets/explosion-ripple.png deleted file mode 100644 index 62e92f07..00000000 Binary files a/GameData/Skins/arctichare/assets/explosion-ripple.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-green-off.png b/GameData/Skins/arctichare/assets/gauge-green-off.png deleted file mode 100644 index 2f4882b4..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-green-off.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-green-on.png b/GameData/Skins/arctichare/assets/gauge-green-on.png deleted file mode 100644 index 71fdd83b..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-green-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-groove-off.png b/GameData/Skins/arctichare/assets/gauge-groove-off.png deleted file mode 100644 index b30d782d..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-groove-off.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-groove-on.png b/GameData/Skins/arctichare/assets/gauge-groove-on.png deleted file mode 100644 index 38f49de8..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-groove-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-off.png b/GameData/Skins/arctichare/assets/gauge-off.png deleted file mode 100644 index f40ba43b..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-off.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-on.png b/GameData/Skins/arctichare/assets/gauge-on.png deleted file mode 100644 index 6464b344..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-percent-on.png b/GameData/Skins/arctichare/assets/gauge-percent-on.png deleted file mode 100644 index 4333fb6e..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-percent-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-player.csv b/GameData/Skins/arctichare/assets/gauge-player.csv deleted file mode 100644 index 77d56f27..00000000 --- a/GameData/Skins/arctichare/assets/gauge-player.csv +++ /dev/null @@ -1,3 +0,0 @@ -gauge-player.png -gauge-player-1P.png,0,0,80,70 -gauge-player-2P.png,0,70,80,70 diff --git a/GameData/Skins/arctichare/assets/gauge-player.png b/GameData/Skins/arctichare/assets/gauge-player.png deleted file mode 100644 index 04c2cd83..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-player.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-survival-off.png b/GameData/Skins/arctichare/assets/gauge-survival-off.png deleted file mode 100644 index 62b593b6..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-survival-off.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/gauge-survival-on.png b/GameData/Skins/arctichare/assets/gauge-survival-on.png deleted file mode 100644 index 16508f59..00000000 Binary files a/GameData/Skins/arctichare/assets/gauge-survival-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/glow-l-blue.png b/GameData/Skins/arctichare/assets/glow-l-blue.png deleted file mode 100644 index d16070ac..00000000 Binary files a/GameData/Skins/arctichare/assets/glow-l-blue.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/glow-l-off.png b/GameData/Skins/arctichare/assets/glow-l-off.png deleted file mode 100644 index 08f9ad99..00000000 Binary files a/GameData/Skins/arctichare/assets/glow-l-off.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/glow-l-pink.png b/GameData/Skins/arctichare/assets/glow-l-pink.png deleted file mode 100644 index 1cb9520d..00000000 Binary files a/GameData/Skins/arctichare/assets/glow-l-pink.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/glow-l-red.png b/GameData/Skins/arctichare/assets/glow-l-red.png deleted file mode 100644 index 5799edc3..00000000 Binary files a/GameData/Skins/arctichare/assets/glow-l-red.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/info-speedmod-on.png b/GameData/Skins/arctichare/assets/info-speedmod-on.png deleted file mode 100644 index 34d393bb..00000000 Binary files a/GameData/Skins/arctichare/assets/info-speedmod-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/judge-combo.csv b/GameData/Skins/arctichare/assets/judge-combo.csv deleted file mode 100644 index 4a751bb8..00000000 --- a/GameData/Skins/arctichare/assets/judge-combo.csv +++ /dev/null @@ -1,61 +0,0 @@ -judge-combo.png -judge-perfect-combo-0.png,0,0,100,100 -judge-perfect-combo-1.png,100,0,100,100 -judge-perfect-combo-2.png,200,0,100,100 -judge-perfect-combo-3.png,300,0,100,100 -judge-perfect-combo-4.png,400,0,100,100 -judge-perfect-combo-5.png,500,0,100,100 -judge-perfect-combo-6.png,600,0,100,100 -judge-perfect-combo-7.png,700,0,100,100 -judge-perfect-combo-8.png,800,0,100,100 -judge-perfect-combo-9.png,900,0,100,100 -judge-great-combo-0.png,0,100,100,100 -judge-great-combo-1.png,100,100,100,100 -judge-great-combo-2.png,200,100,100,100 -judge-great-combo-3.png,300,100,100,100 -judge-great-combo-4.png,400,100,100,100 -judge-great-combo-5.png,500,100,100,100 -judge-great-combo-6.png,600,100,100,100 -judge-great-combo-7.png,700,100,100,100 -judge-great-combo-8.png,800,100,100,100 -judge-great-combo-9.png,900,100,100,100 -judge-good-combo-0.png,0,200,100,100 -judge-good-combo-1.png,100,200,100,100 -judge-good-combo-2.png,200,200,100,100 -judge-good-combo-3.png,300,200,100,100 -judge-good-combo-4.png,400,200,100,100 -judge-good-combo-5.png,500,200,100,100 -judge-good-combo-6.png,600,200,100,100 -judge-good-combo-7.png,700,200,100,100 -judge-good-combo-8.png,800,200,100,100 -judge-good-combo-9.png,900,200,100,100 -judge-bad-combo-0.png,0,300,100,100 -judge-bad-combo-1.png,100,300,100,100 -judge-bad-combo-2.png,200,300,100,100 -judge-bad-combo-3.png,300,300,100,100 -judge-bad-combo-4.png,400,300,100,100 -judge-bad-combo-5.png,500,300,100,100 -judge-bad-combo-6.png,600,300,100,100 -judge-bad-combo-7.png,700,300,100,100 -judge-bad-combo-8.png,800,300,100,100 -judge-bad-combo-9.png,900,300,100,100 -judge-miss-combo-0.png,0,400,100,100 -judge-miss-combo-1.png,100,400,100,100 -judge-miss-combo-2.png,200,400,100,100 -judge-miss-combo-3.png,300,400,100,100 -judge-miss-combo-4.png,400,400,100,100 -judge-miss-combo-5.png,500,400,100,100 -judge-miss-combo-6.png,600,400,100,100 -judge-miss-combo-7.png,700,400,100,100 -judge-miss-combo-8.png,800,400,100,100 -judge-miss-combo-9.png,900,400,100,100 -judge-combo-0.png,0,500,100,100 -judge-combo-1.png,100,500,100,100 -judge-combo-2.png,200,500,100,100 -judge-combo-3.png,300,500,100,100 -judge-combo-4.png,400,500,100,100 -judge-combo-5.png,500,500,100,100 -judge-combo-6.png,600,500,100,100 -judge-combo-7.png,700,500,100,100 -judge-combo-8.png,800,500,100,100 -judge-combo-9.png,900,500,100,100 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/judge-combo.png b/GameData/Skins/arctichare/assets/judge-combo.png deleted file mode 100644 index 8ebe13aa..00000000 Binary files a/GameData/Skins/arctichare/assets/judge-combo.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/judge-pgreat.csv b/GameData/Skins/arctichare/assets/judge-pgreat.csv deleted file mode 100644 index 9d7535d4..00000000 --- a/GameData/Skins/arctichare/assets/judge-pgreat.csv +++ /dev/null @@ -1,7 +0,0 @@ -judge-pgreat.png -judge-pgreat-1.png,0,0,500,100 -judge-pgreat-2.png,0,100,500,100 -judge-pgreat-3.png,0,200,500,100 -judge-pgreat-4.png,0,300,500,100 -judge-pgreat-5.png,0,400,500,100 -judge-pgreat-6.png,0,500,500,100 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/judge-pgreat.png b/GameData/Skins/arctichare/assets/judge-pgreat.png deleted file mode 100644 index c1cb88f4..00000000 Binary files a/GameData/Skins/arctichare/assets/judge-pgreat.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/judge.csv b/GameData/Skins/arctichare/assets/judge.csv deleted file mode 100644 index 45324aa2..00000000 --- a/GameData/Skins/arctichare/assets/judge.csv +++ /dev/null @@ -1,6 +0,0 @@ -judge.png -judge-perfect.png,0,0,500,100 -judge-great.png,0,100,500,100 -judge-good.png,0,200,500,100 -judge-bad.png,0,300,500,100 -judge-miss.png,0,400,500,100 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/judge.png b/GameData/Skins/arctichare/assets/judge.png deleted file mode 100644 index f90d3a9e..00000000 Binary files a/GameData/Skins/arctichare/assets/judge.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/key-back.png b/GameData/Skins/arctichare/assets/key-back.png deleted file mode 100644 index 98f472e1..00000000 Binary files a/GameData/Skins/arctichare/assets/key-back.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/key-half.csv b/GameData/Skins/arctichare/assets/key-half.csv deleted file mode 100644 index 1c53c6ce..00000000 --- a/GameData/Skins/arctichare/assets/key-half.csv +++ /dev/null @@ -1,4 +0,0 @@ -key-half.png -key-half-left.png,0,0,100,260 -key-half-middle.png,100,0,100,260 -key-half-right.png,200,0,100,260 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/key-half.png b/GameData/Skins/arctichare/assets/key-half.png deleted file mode 100644 index c58219d6..00000000 Binary files a/GameData/Skins/arctichare/assets/key-half.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/key-major.png b/GameData/Skins/arctichare/assets/key-major.png deleted file mode 100644 index fa34006a..00000000 Binary files a/GameData/Skins/arctichare/assets/key-major.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/key-minor.png b/GameData/Skins/arctichare/assets/key-minor.png deleted file mode 100644 index 3f76b8ee..00000000 Binary files a/GameData/Skins/arctichare/assets/key-minor.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/keys-volume-lights.csv b/GameData/Skins/arctichare/assets/keys-volume-lights.csv deleted file mode 100644 index cc5c6509..00000000 --- a/GameData/Skins/arctichare/assets/keys-volume-lights.csv +++ /dev/null @@ -1,11 +0,0 @@ -keys-volume-lights.png -keys-volume-lights-10.png,0,0,21,94 -keys-volume-lights-9.png,30,0,21,94 -keys-volume-lights-8.png,60,0,21,94 -keys-volume-lights-7.png,90,0,21,94 -keys-volume-lights-6.png,120,0,21,94 -keys-volume-lights-5.png,150,0,21,94 -keys-volume-lights-4.png,180,0,21,94 -keys-volume-lights-3.png,210,0,21,94 -keys-volume-lights-2.png,240,0,21,94 -keys-volume-lights-1.png,270,0,21,94 diff --git a/GameData/Skins/arctichare/assets/keys-volume-lights.png b/GameData/Skins/arctichare/assets/keys-volume-lights.png deleted file mode 100644 index 18168f2a..00000000 Binary files a/GameData/Skins/arctichare/assets/keys-volume-lights.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lane-5k.png b/GameData/Skins/arctichare/assets/lane-5k.png deleted file mode 100644 index 2a3914c2..00000000 Binary files a/GameData/Skins/arctichare/assets/lane-5k.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lane-7k.png b/GameData/Skins/arctichare/assets/lane-7k.png deleted file mode 100644 index e2ec1b54..00000000 Binary files a/GameData/Skins/arctichare/assets/lane-7k.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lane-judge.png b/GameData/Skins/arctichare/assets/lane-judge.png deleted file mode 100644 index 24b085c3..00000000 Binary files a/GameData/Skins/arctichare/assets/lane-judge.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lane-note.png b/GameData/Skins/arctichare/assets/lane-note.png deleted file mode 100644 index 2e114475..00000000 Binary files a/GameData/Skins/arctichare/assets/lane-note.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lane-scratch.png b/GameData/Skins/arctichare/assets/lane-scratch.png deleted file mode 100644 index 0a4ed4ca..00000000 Binary files a/GameData/Skins/arctichare/assets/lane-scratch.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lane-separator.png b/GameData/Skins/arctichare/assets/lane-separator.png deleted file mode 100644 index c09073a2..00000000 Binary files a/GameData/Skins/arctichare/assets/lane-separator.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lightup-1-black.png b/GameData/Skins/arctichare/assets/lightup-1-black.png deleted file mode 100644 index 7dbcfa3b..00000000 Binary files a/GameData/Skins/arctichare/assets/lightup-1-black.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lightup-1-scratch.png b/GameData/Skins/arctichare/assets/lightup-1-scratch.png deleted file mode 100644 index 28a188ca..00000000 Binary files a/GameData/Skins/arctichare/assets/lightup-1-scratch.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lightup-1-white.png b/GameData/Skins/arctichare/assets/lightup-1-white.png deleted file mode 100644 index 881e0a19..00000000 Binary files a/GameData/Skins/arctichare/assets/lightup-1-white.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lightup-2-black.png b/GameData/Skins/arctichare/assets/lightup-2-black.png deleted file mode 100644 index e4892a41..00000000 Binary files a/GameData/Skins/arctichare/assets/lightup-2-black.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lightup-2-scratch.png b/GameData/Skins/arctichare/assets/lightup-2-scratch.png deleted file mode 100644 index c5292752..00000000 Binary files a/GameData/Skins/arctichare/assets/lightup-2-scratch.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/lightup-2-white.png b/GameData/Skins/arctichare/assets/lightup-2-white.png deleted file mode 100644 index 8ce62801..00000000 Binary files a/GameData/Skins/arctichare/assets/lightup-2-white.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/note-bomb.csv b/GameData/Skins/arctichare/assets/note-bomb.csv deleted file mode 100644 index 3ea46f1f..00000000 --- a/GameData/Skins/arctichare/assets/note-bomb.csv +++ /dev/null @@ -1,10 +0,0 @@ -note-bomb.png -note-bomb-1.png,0,0,100,30 -note-bomb-2.png,120,0,100,30 -note-bomb-3.png,0,0,100,30 -note-bomb-4.png,120,0,100,30 -note-bomb-5.png,0,0,100,30 -note-bomb-6.png,120,0,100,30 -note-bomb-7.png,0,0,100,30 -note-bomb-s.png,240,0,200,30 -note-bomb-sd.png,460,0,200,30 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/note-bomb.png b/GameData/Skins/arctichare/assets/note-bomb.png deleted file mode 100644 index b81f2c64..00000000 Binary files a/GameData/Skins/arctichare/assets/note-bomb.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/note-long-off.csv b/GameData/Skins/arctichare/assets/note-long-off.csv deleted file mode 100644 index 339146c9..00000000 --- a/GameData/Skins/arctichare/assets/note-long-off.csv +++ /dev/null @@ -1,28 +0,0 @@ -note-long-off.png -note-long-top-off-1.png,0,0,100,30 -note-long-top-off-2.png,120,0,100,30 -note-long-top-off-3.png,0,0,100,30 -note-long-top-off-4.png,120,0,100,30 -note-long-top-off-5.png,0,0,100,30 -note-long-top-off-6.png,120,0,100,30 -note-long-top-off-7.png,0,0,100,30 -note-long-top-off-s.png,240,0,200,30 -note-long-top-off-sd.png,460,0,200,30 -note-long-mid-off-1.png,0,50,100,30 -note-long-mid-off-2.png,120,50,100,30 -note-long-mid-off-3.png,0,50,100,30 -note-long-mid-off-4.png,120,50,100,30 -note-long-mid-off-5.png,0,50,100,30 -note-long-mid-off-6.png,120,50,100,30 -note-long-mid-off-7.png,0,50,100,30 -note-long-mid-off-s.png,240,50,200,30 -note-long-mid-off-sd.png,460,50,200,30 -note-long-bot-off-1.png,0,100,100,30 -note-long-bot-off-2.png,120,100,100,30 -note-long-bot-off-3.png,0,100,100,30 -note-long-bot-off-4.png,120,100,100,30 -note-long-bot-off-5.png,0,100,100,30 -note-long-bot-off-6.png,120,100,100,30 -note-long-bot-off-7.png,0,100,100,30 -note-long-bot-off-s.png,240,100,200,30 -note-long-bot-off-sd.png,460,100,200,30 diff --git a/GameData/Skins/arctichare/assets/note-long-off.png b/GameData/Skins/arctichare/assets/note-long-off.png deleted file mode 100644 index 6bdc2b8c..00000000 Binary files a/GameData/Skins/arctichare/assets/note-long-off.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/note-long-on.csv b/GameData/Skins/arctichare/assets/note-long-on.csv deleted file mode 100644 index c08e42f6..00000000 --- a/GameData/Skins/arctichare/assets/note-long-on.csv +++ /dev/null @@ -1,28 +0,0 @@ -note-long-on.png -note-long-top-on-1.png,0,0,100,30 -note-long-top-on-2.png,120,0,100,30 -note-long-top-on-3.png,0,0,100,30 -note-long-top-on-4.png,120,0,100,30 -note-long-top-on-5.png,0,0,100,30 -note-long-top-on-6.png,120,0,100,30 -note-long-top-on-7.png,0,0,100,30 -note-long-top-on-s.png,240,0,200,30 -note-long-top-on-sd.png,460,0,200,30 -note-long-mid-on-1.png,0,50,100,30 -note-long-mid-on-2.png,120,50,100,30 -note-long-mid-on-3.png,0,50,100,30 -note-long-mid-on-4.png,120,50,100,30 -note-long-mid-on-5.png,0,50,100,30 -note-long-mid-on-6.png,120,50,100,30 -note-long-mid-on-7.png,0,50,100,30 -note-long-mid-on-s.png,240,50,200,30 -note-long-mid-on-sd.png,460,50,200,30 -note-long-bot-on-1.png,0,100,100,30 -note-long-bot-on-2.png,120,100,100,30 -note-long-bot-on-3.png,0,100,100,30 -note-long-bot-on-4.png,120,100,100,30 -note-long-bot-on-5.png,0,100,100,30 -note-long-bot-on-6.png,120,100,100,30 -note-long-bot-on-7.png,0,100,100,30 -note-long-bot-on-s.png,240,100,200,30 -note-long-bot-on-sd.png,460,100,200,30 diff --git a/GameData/Skins/arctichare/assets/note-long-on.png b/GameData/Skins/arctichare/assets/note-long-on.png deleted file mode 100644 index 98652ab1..00000000 Binary files a/GameData/Skins/arctichare/assets/note-long-on.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/note-normal.csv b/GameData/Skins/arctichare/assets/note-normal.csv deleted file mode 100644 index 64be9243..00000000 --- a/GameData/Skins/arctichare/assets/note-normal.csv +++ /dev/null @@ -1,10 +0,0 @@ -note-normal.png -note-1.png,0,0,100,30 -note-2.png,120,0,100,30 -note-3.png,0,0,100,30 -note-4.png,120,0,100,30 -note-5.png,0,0,100,30 -note-6.png,120,0,100,30 -note-7.png,0,0,100,30 -note-s.png,240,0,200,30 -note-sd.png,460,0,200,30 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/note-normal.png b/GameData/Skins/arctichare/assets/note-normal.png deleted file mode 100644 index d58d2e6d..00000000 Binary files a/GameData/Skins/arctichare/assets/note-normal.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/onekey-uv.png b/GameData/Skins/arctichare/assets/onekey-uv.png deleted file mode 100644 index 251e13d4..00000000 Binary files a/GameData/Skins/arctichare/assets/onekey-uv.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/pace-a.csv b/GameData/Skins/arctichare/assets/pace-a.csv deleted file mode 100644 index 45d485ea..00000000 --- a/GameData/Skins/arctichare/assets/pace-a.csv +++ /dev/null @@ -1,12 +0,0 @@ -pace-a.png -pace-a-0.png,0,0,60,60 -pace-a-1.png,60,0,60,60 -pace-a-2.png,120,0,60,60 -pace-a-3.png,180,0,60,60 -pace-a-4.png,240,0,60,60 -pace-a-5.png,300,0,60,60 -pace-a-6.png,360,0,60,60 -pace-a-7.png,420,0,60,60 -pace-a-8.png,480,0,60,60 -pace-a-plus.png,600,0,60,60 -pace-a-minus.png,660,0,60,60 diff --git a/GameData/Skins/arctichare/assets/pace-a.png b/GameData/Skins/arctichare/assets/pace-a.png deleted file mode 100644 index 0d983cf2..00000000 Binary files a/GameData/Skins/arctichare/assets/pace-a.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/pace-b.csv b/GameData/Skins/arctichare/assets/pace-b.csv deleted file mode 100644 index e562879e..00000000 --- a/GameData/Skins/arctichare/assets/pace-b.csv +++ /dev/null @@ -1,12 +0,0 @@ -pace-b.png -pace-b-0.png,0,0,60,60 -pace-b-1.png,60,0,60,60 -pace-b-2.png,120,0,60,60 -pace-b-3.png,180,0,60,60 -pace-b-4.png,240,0,60,60 -pace-b-5.png,300,0,60,60 -pace-b-6.png,360,0,60,60 -pace-b-7.png,420,0,60,60 -pace-b-8.png,480,0,60,60 -pace-b-plus.png,600,0,60,60 -pace-b-minus.png,660,0,60,60 diff --git a/GameData/Skins/arctichare/assets/pace-b.png b/GameData/Skins/arctichare/assets/pace-b.png deleted file mode 100644 index 2f271e2f..00000000 Binary files a/GameData/Skins/arctichare/assets/pace-b.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/rate-fast.png b/GameData/Skins/arctichare/assets/rate-fast.png deleted file mode 100644 index f3acdc68..00000000 Binary files a/GameData/Skins/arctichare/assets/rate-fast.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/rate-slow.png b/GameData/Skins/arctichare/assets/rate-slow.png deleted file mode 100644 index 3201d8f6..00000000 Binary files a/GameData/Skins/arctichare/assets/rate-slow.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/scratch-base.png b/GameData/Skins/arctichare/assets/scratch-base.png deleted file mode 100644 index 995ff3b8..00000000 Binary files a/GameData/Skins/arctichare/assets/scratch-base.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/scratch-overlay.png b/GameData/Skins/arctichare/assets/scratch-overlay.png deleted file mode 100644 index 4ed16937..00000000 Binary files a/GameData/Skins/arctichare/assets/scratch-overlay.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/scratch-spinner.png b/GameData/Skins/arctichare/assets/scratch-spinner.png deleted file mode 100644 index 3a20dad7..00000000 Binary files a/GameData/Skins/arctichare/assets/scratch-spinner.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/scratch-uv.png b/GameData/Skins/arctichare/assets/scratch-uv.png deleted file mode 100644 index 32088db8..00000000 Binary files a/GameData/Skins/arctichare/assets/scratch-uv.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/screen-center.png b/GameData/Skins/arctichare/assets/screen-center.png deleted file mode 100644 index cdd0b503..00000000 Binary files a/GameData/Skins/arctichare/assets/screen-center.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/screen-status.png b/GameData/Skins/arctichare/assets/screen-status.png deleted file mode 100644 index 682e5629..00000000 Binary files a/GameData/Skins/arctichare/assets/screen-status.png and /dev/null differ diff --git a/GameData/Skins/arctichare/assets/uv.csv b/GameData/Skins/arctichare/assets/uv.csv deleted file mode 100644 index 95bda911..00000000 --- a/GameData/Skins/arctichare/assets/uv.csv +++ /dev/null @@ -1,4 +0,0 @@ -uv.png -uv-left.png,0,0,100,150 -uv-middle.png,100,0,100,150 -uv-right.png,200,0,100,150 \ No newline at end of file diff --git a/GameData/Skins/arctichare/assets/uv.png b/GameData/Skins/arctichare/assets/uv.png deleted file mode 100644 index 02e2cf39..00000000 Binary files a/GameData/Skins/arctichare/assets/uv.png and /dev/null differ diff --git a/GameData/Skins/arctichare/components.lua b/GameData/Skins/arctichare/components.lua deleted file mode 100644 index 8049adde..00000000 --- a/GameData/Skins/arctichare/components.lua +++ /dev/null @@ -1,92 +0,0 @@ -skin_require "skin_defs" -Gauge = skin_require "elements.gauge" -Numbers = skin_require "elements.numbers" -Lightup = skin_require "elements.lightup" -Judgment = skin_require "elements.judgement" - -local Components = {} -function Components:Init() - self.BPM = Numbers:new(1814 * SkinScale, 1800 * SkinScale, true, 3) - self.Mins = Numbers:new(1830 * SkinScale, 2060 * SkinScale, false, 2) - self.Secs = Numbers:new(1930 * SkinScale, 2060 * SkinScale, false, 2) - - if PlayerSide == 1 then - self.Score = Numbers:new(880 * SkinScale, 40 * SkinScale, true, 6) - self.MulLeft = Numbers:new(920 * SkinScale, 1720 * SkinScale, true, 2) - self.MulRight = Numbers:new(1090 * SkinScale, 1720 * SkinScale, true, 2) - self.Gauge = Gauge:new(970 * SkinScale, 1434 * SkinScale, 1, true) - - self.J1 = Numbers:new(30 * SkinScale, 2060 * SkinScale, false, 4) - self.J2 = Numbers:new(250 * SkinScale, 2060 * SkinScale, false, 4) - self.J3 = Numbers:new(470 * SkinScale, 2060 * SkinScale, false, 4) - self.J4 = Numbers:new(690 * SkinScale, 2060 * SkinScale, false, 4) - self.JM = Numbers:new(910 * SkinScale, 2060 * SkinScale, false, 4) - self.MC = Numbers:new(1130 * SkinScale, 2060 * SkinScale, false, 4) - - self.Lightup = Lightup:new(1) - self.Judgment = Judgment:new(1) - else - self.Score = Numbers:new(2540 * SkinScale, 40 * SkinScale, true, 6) - self.MulLeft = Numbers:new(2570 * SkinScale, 1720 * SkinScale, true, 2) - self.MulRight = Numbers:new(2740 * SkinScale, 1720 * SkinScale, true, 2) - self.Gauge = Gauge:new(970 * SkinScale, 1550 * SkinScale, 2, true) - - self.J1 = Numbers:new(2550 * SkinScale, 2060 * SkinScale, false, 4) - self.J2 = Numbers:new(2770 * SkinScale, 2060 * SkinScale, false, 4) - self.J3 = Numbers:new(2990 * SkinScale, 2060 * SkinScale, false, 4) - self.J4 = Numbers:new(3210 * SkinScale, 2060 * SkinScale, false, 4) - self.JM = Numbers:new(3430 * SkinScale, 2060 * SkinScale, false, 4) - self.MC = Numbers:new(3650 * SkinScale, 2060 * SkinScale, false, 4) - - self.Lightup = Lightup:new(2) - self.Judgment = Judgment:new(2) - end -end - -function Components:Update(delta) - local SongTime = Game:GetPlayer(0).Time - local SongDuration = Game:GetPlayer(0).Duration - self.BPM:update(CurrentBPM) - - local left = SongDuration - SongTime - - if left > 0 then - local mins = math.floor(left / 60) - local secs = math.floor(left - mins * 60) - self.Mins:update(mins) - self.Secs:update(secs, true) - else - self.Mins:update(0) - self.Secs:update(0, true) - end - - local Player = Game:GetPlayer(0) - local ScoreKeeper = Game:GetPlayer(0).Scorekeeper - - self.Score:update(ScoreKeeper:GetScore(ST_EX)) - local int = math.floor(Player.SpeedMultiplier) - local frac = math.floor((Player.SpeedMultiplier - int) * 100) - self.MulLeft:update(int) - self.MulRight:update(frac, true) - self.Gauge:update(delta) - - self.J1:update(ScoreKeeper:GetJudgmentCount(SKJ_W1)) - self.J2:update(ScoreKeeper:GetJudgmentCount(SKJ_W2)) - self.J3:update(ScoreKeeper:GetJudgmentCount(SKJ_W3)) - self.J4:update(ScoreKeeper:GetJudgmentCount(SKJ_W4)) - self.JM:update(ScoreKeeper:GetJudgmentCount(SKJ_MISS)) - self.MC:update(ScoreKeeper:GetScore(ST_MAX_COMBO)) - - self.Lightup:update(delta, KeyArray) - self.Judgment:update(delta) -end - -function Components:OnHit(judge, timeoff) - self.Judgment:onJudge(judge, timeoff) -end - -function Components:OnMiss(judge, timeoff) - self.Judgment:onJudge(judge, timeoff) -end - -return Components diff --git a/GameData/Skins/arctichare/coordinate map.txt b/GameData/Skins/arctichare/coordinate map.txt deleted file mode 100644 index 670dfb17..00000000 --- a/GameData/Skins/arctichare/coordinate map.txt +++ /dev/null @@ -1,63 +0,0 @@ -Arctic Hare skin by TomoAlien -And you... if you're ready to program this! - -Oh... and with help from Dolphin, Zardoru, JerwuQu and other people at Discord BMS channel! - - 880, 40 - Score 1P -2540, 40 - Score 2P - -1340, 40 - Title Ticker [1160x120] - - 880, 200 - BGA Display [2080x1170] - - 970,1434 - Gauge On 1P - 970,1554 - Gauge On 2P - -1080,1430 - Gauge 1P [1600x80] -1080,1550 - Gauge 2P [1600x80] - -2710,1434 - Gauge Percentage 1P -2710,1554 - Gauge Percentage 2P - - 920,1720 - Speed Mod 1P -2570,1720 - Speed Mod 2P - -1814,1800 - BPM - - 30,2060 - Stats 1P: Perfect - 250,2060 - Stats 1P: Great - 470,2060 - Stats 1P: Good - 690,2060 - Stats 1P: Bad - 910,2060 - Stats 1P: Miss -1130,2060 - Stats 1P: Max Combo - -2550,2060 - Stats 2P: Perfect -2770,2060 - Stats 2P: Great -2990,2060 - Stats 2P: Good -3210,2060 - Stats 2P: Bad -3430,2060 - Stats 2P: Miss -3650,2060 - Stats 2P: Max Combo - -1830,2060 - Time Left [Minutes] -1930,2060 - Time Left [Seconds] - - - - -Half-Width Lanes... -Notes are 100px wide. -However, the next lane is NOT offset as previous+100px, but previous+50px. -Scratch lane is its own thing. - -Neons should just be put on the left and right side with additive blending. - -Dolphin wanted different color lightups for P1 and P2. - -Remember to finish off the lanes with lane-separator.png - -The Volume Meter lights are 3 pixels apart, which means that the offset is previous+24px. -The first volume light strip overlay is placed at 27,28. This applies to every single UV block. - -Explosions... - -Well, not sure how they should exactly look, but something along the lines of the main explosions getting bigger with a randomized rotation. \ No newline at end of file diff --git a/GameData/Skins/arctichare/custom_defs.lua b/GameData/Skins/arctichare/custom_defs.lua deleted file mode 100644 index 2179a696..00000000 --- a/GameData/Skins/arctichare/custom_defs.lua +++ /dev/null @@ -1,289 +0,0 @@ -local Channels = Game and Game:GetPlayer(0).Channels - -if not Channels or (Channels ~= 8 and Channels ~= 6) then - if Lanes ~= 8 and Lanes ~= 6 then - print ("Not overwriting noteskin defs - ", Channels, Lanes) - return - end -end - -print "Running arctichare noteskin defs." - -require "utils" -skin_require "skin_defs" - -NoteHeight = 10 -GearStartX5KP1 = 120 -GearStartX7KP1 = 85 -GearStartX5KP2 = 1063 -GearStartX7KP2 = 3000 - -Channels6Sizes = { - 200, - 100, - 100, - 100, - 100, - 100 -} - -Channels8Sizes = { - 200, - 100, - 100, - 100, - 100, - 100, - 100, - 100 -} - --- Scale to raindrop size -ScaleMap(Channels6Sizes) -ScaleMap(Channels8Sizes) - --- ws: lane half-width --- ss: scratch half-width -local ws = 100 * SkinScale / 2 -local ss = 200 * SkinScale / 2 - --- 6 channels, scratch at the right -C6RelativeRS = { - ws * 6 + ss, - ws, - ws * 2, - ws * 3, - ws * 4, - ws * 5 -} - --- 8 channels, scratch at the right -C8RelativeRS = { - ws * 8 + ss, - ws, - ws * 2, - ws * 3, - ws * 4, - ws * 5, - ws * 6, - ws * 7 -} - --- 6 channels, scratch at the left -C6RelativeLS = { - ss, - ss * 2 + ws, - ss * 2 + ws * 2, - ss * 2 + ws * 3, - ss * 2 + ws * 4, - ss * 2 + ws * 5, -} - --- 8 channels, scratch at the left -C8RelativeLS = { - ss, - ss * 2 + ws, - ss * 2 + ws * 2, - ss * 2 + ws * 3, - ss * 2 + ws * 4, - ss * 2 + ws * 5, - ss * 2 + ws * 6, - ss * 2 + ws * 7, -} - -Size6Key = ss * 2 + ws * 6 -Size8Key = ss * 2 + ws * 8 - -if PlayerSide == 1 then - GearStartX5K = GearStartX5KP1 - GearStartX7K = GearStartX7KP1 -else - GearStartX5K = GearStartX5KP2 - GearStartX7K = GearStartX7KP2 -end - -if ScratchSide == 1 then - C6Relative = C6RelativeLS - C8Relative = C8RelativeLS -else - C6Relative = C6RelativeRS - C8Relative = C8RelativeRS -end - --- Channel 6 absolute positions for player 1 -C6P1 = {} -for k,v in ipairs(C6Relative) do - C6P1[#C6P1 + 1] = GearStartX5KP1 + v -end - --- Channel 6 absolute positions for player 2 -C6P2 = {} -for k,v in ipairs(C6Relative) do - C6P2[#C6P2 + 1] = GearStartX5KP2 + v -end - --- Channel 8 absolute positions for player 1 -C8P1 = {} -for k,v in ipairs(C8Relative) do - C8P1[#C8P1 + 1] = GearStartX7KP1 + v -end - --- Channel 8 absolute positions for player 2 -C8P2 = {} -for k,v in ipairs(C8Relative) do - C8P2[#C8P2 + 1] = GearStartX7KP2 + v -end - -if PlayerSide == 1 then - C6P = C6P1 - C8P = C8P1 -elseif PlayerSide == 2 then - C6P = C6P2 - C8P = C8P2 -end - -if Channels == 6 then - GearStartXP1 = GearStartX5KP1 - GearStartXP2 = GearStartX7KP2 - GearWidth = Size6Key - CP1 = C6P1 - CP2 = C6P2 - GearStartX = GearStartX5K -else - GearStartXP1 = GearStartX7KP1 - GearStartXP2 = GearStartX7KP2 - GearWidth = Size8Key - CP1 = C8P1 - CP2 = C8P2 - GearStartX = GearStartX7K -end - -Channels6Common = { - -- Note Images - Key1Image = "s", - Key2Image = "1", - Key3Image = "2", - Key4Image = "3", - Key5Image = "4", - Key6Image = "5", - NoteHeight = NoteHeight, - - -- Lane Widths - Key1Width = Channels6Sizes[1], - Key2Width = Channels6Sizes[2], - Key3Width = Channels6Sizes[3], - Key4Width = Channels6Sizes[4], - Key5Width = Channels6Sizes[5], - Key6Width = Channels6Sizes[6], - - GearWidth = Size6Key, - BarlineWidth = Size6Key, -} - --- Auto positions by player side -Channels6P1 = { - GearStartX = GearStartX5KP1, - FieldStartX = C6P1[2] / SkinScale - ws / SkinScale, -- Unscaled for csv files - ScratchX = C6P1[1] / SkinScale - ss / SkinScale, - LaneSepX = GearStartX5KP1 / SkinScale - 40, - LaneSep2X = GearStartX5KP1 / SkinScale + Size6Key / SkinScale, - - -- Lane positions - Key1X = C6P1[1], - Key2X = C6P1[2], - Key3X = C6P1[3], - Key4X = C6P1[4], - Key5X = C6P1[5], - Key6X = C6P1[6], -} - -Channels6P2 = { - GearStartX = GearStartX5KP2, - FieldStartX = C6P2[2] / SkinScale - ws / SkinScale, -- Unscaled for csv files - ScratchX = C6P2[1] / SkinScale - ss / SkinScale, - LaneSepX = GearStartX5KP2 / SkinScale - 40, - LaneSep2X = GearStartX5KP2 / SkinScale + Size6Key / SkinScale, - - -- Lane positions - Key1X = C6P2[1], - Key2X = C6P2[2], - Key3X = C6P2[3], - Key4X = C6P2[4], - Key5X = C6P2[5], - Key6X = C6P2[6], -} - -Channels8Common = { - GearWidth = Size8Key, - BarlineWidth = Size8Key, - NoteHeight = NoteHeight, - -- Lane Widths - Key1Width = Channels8Sizes[1], - Key2Width = Channels8Sizes[2], - Key3Width = Channels8Sizes[3], - Key4Width = Channels8Sizes[4], - Key5Width = Channels8Sizes[5], - Key6Width = Channels8Sizes[6], - Key7Width = Channels8Sizes[7], - Key8Width = Channels8Sizes[8], - - -- Note Images - Key1Image = "s", - Key2Image = "1", - Key3Image = "2", - Key4Image = "3", - Key5Image = "4", - Key6Image = "5", - Key7Image = "6", - Key8Image = "7", - } - - Channels8P1 = { - GearStartX = GearStartX7KP1, - FieldStartX = C8P1[2] / SkinScale - ws / SkinScale, -- Unscaled for csv files - ScratchX = C8P1[1] / SkinScale - ss / SkinScale, - LaneSepX = GearStartX7KP1 / SkinScale - 40, - LaneSep2X = GearStartX7KP1 / SkinScale + Size8Key / SkinScale, - - -- Lane positions - Key1X = C8P1[1], - Key2X = C8P1[2], - Key3X = C8P1[3], - Key4X = C8P1[4], - Key5X = C8P1[5], - Key6X = C8P1[6], - Key7X = C8P1[7], - Key8X = C8P1[8], -} - -Channels8P2 = { - GearStartX = GearStartX7KP2, - FieldStartX = C8P2[2] / SkinScale - ws / SkinScale, -- Unscaled for csv files - ScratchX = C8P2[1] / SkinScale - ss / SkinScale, - LaneSepX = GearStartX7KP2 / SkinScale - 40, - LaneSep2X = GearStartX7KP2 / SkinScale + Size8Key / SkinScale, - - -- Lane positions - Key1X = C8P2[1], - Key2X = C8P2[2], - Key3X = C8P2[3], - Key4X = C8P2[4], - Key5X = C8P2[5], - Key6X = C8P2[6], - Key7X = C8P2[7], - Key8X = C8P2[8], -} - -print ("Noteskin table: ", Noteskin) -Noteskin = Noteskin or {} - -if PlayerSide == 1 then - Noteskin[6] = table.join(Channels6P1, Channels6Common) - Noteskin[8] = table.join(Channels8P1, Channels8Common) -else - Noteskin[6] = table.join(Channels6P2, Channels6Common) - Noteskin[8] = table.join(Channels8P2, Channels8Common) -end - -print ("returning defs table...") -return Noteskin diff --git a/GameData/Skins/arctichare/elements/gauge.lua b/GameData/Skins/arctichare/elements/gauge.lua deleted file mode 100644 index 8b61be1e..00000000 --- a/GameData/Skins/arctichare/elements/gauge.lua +++ /dev/null @@ -1,109 +0,0 @@ -skin_require "skin_defs" -skin_require "elements.numbers" - --- arctic hare beatmania gauge -local Gauge = { - construct = function(self, x, y, a, on) - self.ActivePlayerNumber = a - print "===== Building Gauge =====" - if on then - self.ActivePlayer = ScreenObject { - X = x, - Y = y, - Layer = 17 - } - - self.ActivePlayer.Texture = "assets/" .. Gauge.Atlas.File - self.ActivePlayer.Height = 70 - - -- player - if a == 2 then - Gauge.Atlas:SetObjectCrop(self.ActivePlayer, "gauge-player-2P.png") - else - Gauge.Atlas:SetObjectCrop(self.ActivePlayer, "gauge-player-1P.png") - end - - ScaleObj(self.ActivePlayer) - end - - self.GaugeBG = ScreenObject { - X = x + 110 * SkinScale, - Y = y, - Layer = 16 - } - - local gt = Global:GetCurrentGaugeType(0) - if gt == LT_GROOVE then - self.GaugeBG.Texture = "assets/gauge-groove-off.png" - elseif gt == LT_EASY or gt == LT_O2JAM or gt == LT_STEPMANIA then - self.GaugeBG.Texture = "assets/gauge-green-off.png" - else - self.GaugeBG.Texture = "assets/gauge-survival-off.png" - end - - ScaleObj(self.GaugeBG) - - print (on, self.GaugeBG.Texture, gt) - if on then - self.GaugeHealth = ScreenObject { - X = x + 110 * SkinScale, - Y = y, - Layer = 17, - Lighten = 1 - } - - if gt == LT_GROOVE then - self.GaugeHealth.Texture = "assets/gauge-groove-on.png" - elseif gt == LT_EASY or gt == LT_O2JAM or gt == LT_STEPMANIA then - self.GaugeHealth.Texture = "assets/gauge-green-on.png" - else - self.GaugeHealth.Texture = "assets/gauge-survival-on.png" - end - - ScaleObj(self.GaugeHealth) - - self.HealthBG = ScreenObject { - X = x + 1740 * SkinScale, - Y = y, - Layer = 9 - } - - self.HealthBG.Texture = "assets/gauge-percent-on.png" - ScaleObj(self.HealthBG) - - self.HealthPCT = Numbers:new(x + 1740 * SkinScale, y, false, 3) - end - - - - self.IsOn = on - end, - new = function(self, x, y, active_player_number, is_on) - local ret = {} - setmetatable(ret, self) - ret:construct(x, y, active_player_number, is_on) - return ret - end, - update = function(self, delta) - local health = Game:GetPlayer(0).LifebarPercent / 100 - local Beat = Game:GetPlayer(0).Beat - if self.IsOn then - local hp = math.floor(health * self.Steps) - local hpPCT = math.floor(health * 100) - local w = hp * Gauge.SkinPxPerStep - local beatfx = 1 - (Beat - math.floor(Beat)) - - self.GaugeHealth.Width = w - self.GaugeHealth:SetCropByPixels(0, w, 0, self.GaugeHealth.Height) - self.GaugeHealth.LightenFactor = beatfx - - self.HealthPCT:update(hpPCT) - end - end -} - -Gauge.Atlas = TextureAtlas:skin_new("assets/gauge-player.csv") -Gauge.Steps = 80 -Gauge.SkinPxPerStep = 20 -Gauge.__index = Gauge -return Gauge diff --git a/GameData/Skins/arctichare/elements/judgement.lua b/GameData/Skins/arctichare/elements/judgement.lua deleted file mode 100644 index bc9d0b08..00000000 --- a/GameData/Skins/arctichare/elements/judgement.lua +++ /dev/null @@ -1,200 +0,0 @@ -skin_require "skin_defs" -skin_require "custom_defs" -game_require "TextureAtlas" - -local Judgment = { - JudgeMap = { - "judge-perfect.png", -- w1 etc - "judge-great.png", - "judge-good.png", - "judge-bad.png", - "judge-miss.png", -- w5 is unused, repeat miss - "judge-miss.png" - }, - ComboMap = { - "perfect", - "great", - "good", - "bad", - "miss" - }, - construct = function(self, player_number) - self.Transform = Transformation() - - self.JudgeAtlas = TextureAtlas:skin_new("assets/judge.csv") - self.ComboAtlas = TextureAtlas:skin_new("assets/judge-combo.csv") - self.PGreatAtlas = TextureAtlas:skin_new("assets/judge-pgreat.csv") - self.PacemakerGoodAtlas = TextureAtlas:skin_new("assets/pace-a.csv") - self.PacemakerBadAtlas = TextureAtlas:skin_new("assets/pace-b.csv") - - -- Judgement object - self.Judgment = ScreenObject { - Centered = 1, - Alpha = 0, - Layer = 20, - ChainTransformation = self.Transform - } - - -- force load if not preloaded - self.Judgment.Texture = "assets/" .. self.JudgeAtlas.File - self.Judgment.Texture = "assets/" .. self.PGreatAtlas.File - ScaleObj(self.Judgment) - - -- Combo object - self.ComboDigits = {} - - local ScoreKeeper = Game:GetPlayer(0).Scorekeeper - local max_digits = math.floor(math.log10(ScoreKeeper.MaxNotes)) + 1 - for i=1, max_digits do - local obj = ScreenObject { - Alpha = 0, - ChainTransformation = self.Transform, - Texture = "assets/" .. self.ComboAtlas.File, - Layer = self.Judgment.Layer - } - ScaleObj(obj) - self.ComboDigits[#self.ComboDigits + 1] = obj - end - - self.DigitCount = max_digits - - -- Pacemaker object - - -- Fast/Slow indicator - self.FSIndicator = ScreenObject { - Alpha = 0, - Centered = 1, - Layer = 20, - ChainTransformation = self.Transform - } - - ScaleObj(self.FSIndicator) - - self.TimeToFade = 0.25 - self.TimeRemainingToFade = 0 - self.FadeDuration = 0.1 - self.TimeFading = 0 - self.TimePerPGreatFrame = 0.02 - - self.BlinkTime = 0 - - self.ScaleFeedbackTime = 0.07 - self.TimeScaling = 0 - end, - new = function(self, player_number) - local ret = {} - setmetatable(ret, self) - ret:construct(player_number) - return ret - end, - onJudge = function(self, judge, timeoff) - -- update main judge - self.LastJudge = judge - if judge == 1 or judge == 0 then - -- if pgreat or judge == 0 then - self.Judgment.Texture = "assets/" .. self.PGreatAtlas.File - self.PGreatAtlas:SetObjectCrop(self.Judgment, "judge-pgreat-1.png", true) - --end - else - self.Judgment.Texture = "assets/" .. self.JudgeAtlas.File - self.JudgeAtlas:SetObjectCrop(self.Judgment, Judgment.JudgeMap[judge], true) - end - self.Judgment.Alpha = 1 - self.TimeScaling = self.ScaleFeedbackTime - self.TimeRemainingToFade = self.TimeToFade - - -- update pacemaker - - -- update F/S indicator - if judge == 1 or judge == 0 then - self.FSIndicator.Alpha = 0 - else - self.FSIndicator.Alpha = 1 - if timeoff > 0 then - self.FSIndicator.Texture = "assets/rate-slow.png" - else - self.FSIndicator.Texture = "assets/rate-fast.png" - end - - local jh = self.Judgment.Height * SkinScale / 2 - local ih = self.FSIndicator.Height * SkinScale / 2 - self.FSIndicator.Y = self.Judgment.Y - jh - ih - end - end, - update = function(self, delta) - local ScoreKeeper = Game:GetPlayer(0).Scorekeeper - self.TimeScaling = self.TimeScaling - delta - self.TimeRemainingToFade = self.TimeRemainingToFade - delta - self.BlinkTime = self.BlinkTime + delta - - self.Transform.X = GearStartX - 40 * SkinScale + GearWidth / 2 - self.Transform.Y = 1560 * SkinScale / 2 - - if self.TimeScaling > 0 then - local ys = lerp(self.TimeScaling, self.ScaleFeedbackTime, 0, 1.25, 1.0) - local xs = lerp(self.TimeScaling, self.ScaleFeedbackTime, 0, 1.25, 1.0) - self.Transform.ScaleX = xs - self.Transform.ScaleY = ys - else - self.Transform.ScaleX = 1.0 - self.Transform.ScaleY = 1.0 - end - - local blink_frame - if self.LastJudge == 1 or self.LastJudge == 0 then - local cycleTime = math.fmod(self.BlinkTime, self.TimePerPGreatFrame * 6) - local frame = math.floor(cycleTime / self.TimePerPGreatFrame + 1) - blink_frame = frame - self.PGreatAtlas:SetObjectCrop(self.Judgment, "judge-pgreat-" .. frame .. ".png", true) - else - blink_frame = self.LastJudge - end - - -- Blink and alpha stuff. - if blink_frame and blink_frame ~= 6 then - local combo_str = tostring(ScoreKeeper:GetScore(ST_COMBO)) - local digit_w = 70 - local combo_w = #combo_str * digit_w * SkinScale - - - local judge_space = -15 - local digit_offset = self.Judgment.Width / 2 * SkinScale + judge_space - -- combo digits plus judge center - local dpj = (combo_w + judge_space + self.Judgment.Width * SkinScale) / 2 - - local joffset = (dpj - self.Judgment.Width / 2 * SkinScale) - - if combo_w > 0 then - self.Judgment.X = -joffset - end - - -- Place F/S on top of combo - local jstart = self.Judgment.X + self.Judgment.Width / 2 * SkinScale - self.FSIndicator.X = jstart - - for i=1, #combo_str do - if i > self.DigitCount then - break - end - - local di = #combo_str - (i - 1) - local digit = string.sub(combo_str, di, di) - - local spr = "judge-" .. Judgment.ComboMap[blink_frame] .. "-combo-" .. digit .. ".png" - self.ComboAtlas:SetObjectCrop(self.ComboDigits[di], spr, true) - with ( self.ComboDigits[di], { - Y = -self.Judgment.Height / 2 * SkinScale, - X = digit_offset + combo_w - digit_w * i * SkinScale - joffset, - Alpha = 1 - }) - end - - for i=(#combo_str+1), self.DigitCount do - self.ComboDigits[i].Alpha = 0 - end - end - end -} - -Judgment.__index = Judgment -return Judgment diff --git a/GameData/Skins/arctichare/elements/lightup.lua b/GameData/Skins/arctichare/elements/lightup.lua deleted file mode 100644 index 85c9be5d..00000000 --- a/GameData/Skins/arctichare/elements/lightup.lua +++ /dev/null @@ -1,78 +0,0 @@ -skin_require "skin_defs" -skin_require "custom_defs" - -local Lightup = { - construct = function(self, player_side) - self.ActivePlayerNumber = player_side - - if player_side == 1 then - self.white = "assets/lightup-1-white.png" - self.black = "assets/lightup-1-black.png" - self.scratch = "assets/lightup-1-scratch.png" - self.coords = CP1 - elseif player_side == 2 then - self.white = "assets/lightup-2-white.png" - self.black = "assets/lightup-2-black.png" - self.scratch = "assets/lightup-2-scratch.png" - self.coords = CP2 - end - - local map = {0, 1, 2, 1, 2, 1, 2, 1} - - self.Lights = {} - local sidecnt - if Channels == 12 then - sidecnt = 6 - elseif Channels == 16 then - sidecnt = 8 - else - sidecnt = Channels - end - - for i=1, sidecnt do - self.Lights[i] = Engine:CreateObject() - - local pic - if map[i] == 0 then - pic = self.scratch - elseif map[i] == 1 then - pic = self.white - else - pic = self.black - end - - print("load ", pic) - self.Lights[i].Texture = pic - with(self.Lights[i], { - Centered = 1, - Y = self.Lights[i].Height / 2 * SkinScale, - X = self.coords[i], - Layer = 25, - BlendMode = BlendAdd - }) - - self.Lights[i].Alpha = 0 - - ScaleObj(self.Lights[i]) - print(self.Lights[i].X, self.Lights[i].Y) - end - end, - new = function(self, player_side) - local ret = {} - setmetatable(ret, self) - ret:construct(player_side) - return ret - end, - update = function(self, delta, keystate) - for k,v in ipairs(keystate) do - if v then - self.Lights[k].Alpha = 1 - else - self.Lights[k].Alpha = 0 - end - end - end -} - -Lightup.__index = Lightup -return Lightup diff --git a/GameData/Skins/arctichare/elements/numbers.lua b/GameData/Skins/arctichare/elements/numbers.lua deleted file mode 100644 index bd1853bc..00000000 --- a/GameData/Skins/arctichare/elements/numbers.lua +++ /dev/null @@ -1,70 +0,0 @@ -game_require "TextureAtlas" -skin_require "skin_defs" - -local DigitalNumbers = { - construct = function(self, x, y, isBig, digits) - self.isBig = isBig - - if self.isBig then - self.atlas = TextureAtlas:skin_new("assets/digits-big.csv") - self.width = 70 * SkinScale - self.height = 120 * SkinScale - else - self.atlas = TextureAtlas:skin_new("assets/digits-small.csv") - self.width = 40 * SkinScale - self.height = 70 * SkinScale - end - - self.x = x - self.y = y - - self.digits = digits - self.objects = {} - - for i=1, self.digits do - self.objects[i] = ScreenObject { - X = self.x + self.width * (i - 1), - Y = self.y, - Layer = 21 - } - - self.objects[i].Texture = "assets/" .. self.atlas.File - self.objects[i].Width = self.width - self.objects[i].Height = self.height - end - - self:update(0) - end, - new = function(self, x, y, isBig, digits) - local ret = {} - setmetatable(ret, self) - ret:construct(x, y, isBig, digits) - return ret - end, - update = function(self, num, zeropad) - if num == nil then - return - end - - local s = tostring(num) - if #s > self.digits then - s = string.sub(s, #s - self.digits + 1) - elseif #s < self.digits then - if zeropad then - s = string.rep("0", self.digits - #s) .. s - else - s = string.rep("X", self.digits - #s) .. s - end - end - - - - for i=1, self.digits do - local c = string.sub(s, i, i) - self.atlas:SetObjectCrop(self.objects[i], "digits-" .. c .. ".png") - end - end -} - -DigitalNumbers.__index = DigitalNumbers -return DigitalNumbers diff --git a/GameData/Skins/arctichare/hare.csv b/GameData/Skins/arctichare/hare.csv deleted file mode 100644 index a03a4ef0..00000000 --- a/GameData/Skins/arctichare/hare.csv +++ /dev/null @@ -1,5 +0,0 @@ -assets/screen-center.png,layout,840,0,2160,2010,0 -assets/screen-status.png,status,0,1970,3840,190,17 -assets/glow-l-blue.png,lglow,0,0,200,1970,2 -assets/info-speedmod-on.png,smp1,920,1720,350,120,2 -assets/info-speedmod-on.png,smp2,2570,1720,350,120,2 \ No newline at end of file diff --git a/GameData/Skins/arctichare/mockup.jpg b/GameData/Skins/arctichare/mockup.jpg deleted file mode 100644 index b46151d7..00000000 Binary files a/GameData/Skins/arctichare/mockup.jpg and /dev/null differ diff --git a/GameData/Skins/arctichare/noteskin.lua b/GameData/Skins/arctichare/noteskin.lua deleted file mode 100644 index ffa32f47..00000000 --- a/GameData/Skins/arctichare/noteskin.lua +++ /dev/null @@ -1,181 +0,0 @@ -Lanes = Player.Channels - -if Lanes ~= 8 and Lanes ~= 6 then - fallback_require("noteskin") - return -end - -require "TextureAtlas" -Noteskin, a, b = skin_require "custom_defs" - -print(Noteskin, a, b) --- All notes have their origin centered. - -notes = {} -longnotes = {} - -function SetCommonNoteStuff(note, i) - note.Width = Noteskin[Lanes]['Key' .. i .. 'Width'] - note.X = Noteskin[Lanes]['Key' .. i .. 'X'] - note.Height = NoteHeight - note.Layer = 14 -end - -function MakeNote(i) - ret = Object2D() - ret.Texture = "assets/" .. NoteAtlas.File - NoteAtlas:SetObjectCrop(ret, "note-" .. Noteskin[Lanes]['Key' .. i .. 'Image'] .. ".png") - SetCommonNoteStuff(ret, i) - return ret -end - -function MakeLongNote(i, n, isOn) - ptop = Object2D() - pmid = Object2D() - pbot = Object2D() - - local atlas - local txt - if isOn then - txt = "on" - atlas = LNAtlas.on - elseif not isOn then - txt = "off" - atlas = LNAtlas.off - end - - ptop.Texture = "assets/" .. atlas.File - pmid.Texture = "assets/" .. atlas.File - pbot.Texture = "assets/" .. atlas.File - - local fntop = "note-long-top-" .. txt .. "-" .. i .. ".png" - atlas:SetObjectCrop(ptop, fntop) - - local fnmid = "note-long-mid-" .. txt .. "-" .. i .. ".png" - atlas:SetObjectCrop(pmid, fnmid) - - local fnbot = "note-long-bot-" .. txt .. "-" .. i .. ".png" - atlas:SetObjectCrop(pbot, fnbot) - - SetCommonNoteStuff(ptop, n) - SetCommonNoteStuff(pmid, n) - SetCommonNoteStuff(pbot, n) - return { - top = ptop, - mid = pmid, - bot = pbot - } -end - -function Init() - NoteAtlas = TextureAtlas:skin_new("assets/note-normal.csv") - - LNAtlas = { - on = TextureAtlas:skin_new("assets/note-long-on.csv"), - off = TextureAtlas:skin_new("assets/note-long-off.csv") - } - - BombAtlas = TextureAtlas:skin_new("assets/note-bomb.csv") - - for i=1,Lanes do - local keypic = Noteskin[Lanes]["Key" .. i .. "Image"] - -- normal hit notes - notes[i] = MakeNote(i) - - -- long notes (off by default) - longnotes[i] = {} - longnotes[i].on = MakeLongNote(keypic, i, true) - longnotes[i].off = MakeLongNote(keypic, i, false) - end -end - -function Update(delta, beat) -end - --- 1 is enabled. 2 is being pressed. 0 is failed. 3 is succesful hit. -function drawNormalInternal(lane, loc, frac, active_level) - lane = lane + 1 - notes[lane].Y = loc - Notes:Render(notes[lane]) -end - -function drawHoldTopInternal(lane, loc, frac, active_level) - local note - lane = lane + 1 - if active_level == 2 then - note = longnotes[lane].on.top - else - note = longnotes[lane].off.top - end - - if active_level == 3 or active_level == 0 then - return - end - - note.Y = loc - Notes:Render(note) -end - -function drawHoldBotInternal(lane, loc, frac, active_level) - local note - lane = lane + 1 - if active_level == 2 then - note = longnotes[lane].on.bot - else - note = longnotes[lane].off.bot - end - - if active_level == 3 or active_level == 0 then - return - end - - note.Y = loc - Notes:Render(note) -end - -function drawHoldBodyInternal(lane, loc, size, active_level) - local note - lane = lane + 1 - if active_level == 2 then - note = longnotes[lane].on.mid - else - note = longnotes[lane].off.mid - end - - if active_level == 3 or active_level == 0 then - return - end - - note.Y = loc - note.Height = size - Notes:Render(note) -end - -function drawMineInternal(lane, loc, frac) - -- stub while mines are accounted in the scoring system. -end - --- From now on, only engine variables are being set. --- Barline -Notes.BarlineEnabled = 1 -Notes.BarlineOffset = Noteskin[Lanes].NoteHeight / 2 - -print ("Noteskin lanes is ") -Notes.BarlineStartX = Noteskin[Lanes].GearStartX -Notes.BarlineWidth = Noteskin[Lanes].BarlineWidth -Notes.JudgmentY = ScreenHeight - (1560 * SkinScale - NoteHeight / 2) -Notes.DecreaseHoldSizeWhenBeingHit = 1 -Notes.DanglingHeads = 1 - --- How many extra units do you require so that the whole bounding box is accounted --- when determining whether to show this note or not. -Notes.NoteScreenSize = Noteskin[Lanes].NoteHeight / 2 - -DrawNormal = drawNormalInternal -DrawFake = drawNormalInternal -DrawLift = drawNormalInternal -DrawMine = drawMineInternal - -DrawHoldHead = drawHoldTopInternal -DrawHoldTail = drawHoldBotInternal -DrawHoldBody = drawHoldBodyInternal diff --git a/GameData/Skins/arctichare/preload.lua b/GameData/Skins/arctichare/preload.lua deleted file mode 100644 index c4701484..00000000 --- a/GameData/Skins/arctichare/preload.lua +++ /dev/null @@ -1,4 +0,0 @@ --- Preload file manifest -Preload = { - -} \ No newline at end of file diff --git a/GameData/Skins/arctichare/screengameplay7k.lua b/GameData/Skins/arctichare/screengameplay7k.lua deleted file mode 100644 index 5264d78f..00000000 --- a/GameData/Skins/arctichare/screengameplay7k.lua +++ /dev/null @@ -1,129 +0,0 @@ --- 7+1 and 5+1 supported only -Channels = Game:GetPlayer(0).Channels - -if Channels ~= 8 and Channels ~= 6 then - fallback_require "screengameplay7k" - return -end - -game_require "TextureAtlas" -FixedObjects = game_require "FixedObjects" - -game_require "utils" -game_require "AnimationFunctions" - --- Set up constants for everyone - -skin_require "skin_defs" -skin_require "custom_defs" -Components = skin_require "components" - --- All of these will be loaded in the loading screen instead of --- in the main thread, and will also be unloaded at the end. -skin_require "preload" - --- Status of a lane being pressed or not. -KeyArray = {} - --- Lightning -Lightning = {} -LightingTime = 0.25 - -Bomb = {} -BombTime = 0.2 - -function Init() - Objs = FixedObjects:new() - Objs.XRatio = SkinScale - Objs.YRatio = SkinScale - AutoadjustBackground({ - x = 880 * SkinScale, - y = 200 * SkinScale, - w = 2080 * SkinScale, - h = 1170 * SkinScale - }) - - print("Initializing") - Components:Init() - IsFullCombo = false - - print ("Create fixed objects") - Objs:CreateFromCSV("hare.csv", Noteskin[Channels]) - ObjP1 = FixedObjects:new() - ObjP1.XRatio = SkinScale - ObjP1.YRatio = SkinScale - ObjP2 = FixedObjects:new() - ObjP2.XRatio = SkinScale - ObjP2.YRatio = SkinScale - if Channels == 6 then - local p1s = table.join(Channels6P1, Channels6Common) - local p2s = table.join(Channels6P2, Channels6Common) - - ObjP1:CreateFromCSV("5k.csv", p1s) - ObjP2:CreateFromCSV("5k.csv", p2s) - else - local p1s = table.join(Channels8P1, Channels8Common) - local p2s = table.join(Channels8P2, Channels8Common) - - Objs:CreateFromCSV("7k.csv", p1s) - Objs:CreateFromCSV("7k.csv", p2s) - end - - for i=1, Channels do - KeyArray[i] = false - end -end - -function Cleanup() -end - -function OnFullComboEvent() -end - -function OnFailureEvent() - if Global:GetCurrentCurrentGauge(0) ~= LT_GROOVE then - DoFailAnimation() - return FailAnimation.Duration - else - FadeToBlack() - return SuccessAnimation.Duration - end -end - --- When 'enter' is pressed and the game starts, this function is called. -function OnActivateEvent() -end - -function HitEvent(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease) - Components:OnHit(JudgmentValue, TimeOff) -end - -function MissEvent(TimeOff, Lane, IsHold) - Components:OnMiss(5, TimeOff) -end - -function KeyEvent(Key, Code, IsMouseInput) -end - -function GearKeyEvent (Lane, IsKeyDown) - KeyArray[Lane] = IsKeyDown -end - --- Called when the song is over. -function OnSongFinishedEvent() - DoSuccessAnimation() - return SuccessAnimation.Duration -end - -function Update(Delta) - local SongTime = Game:GetPlayer(0).Time - local SongDuration = Game:GetPlayer(0).Duration - - local SongPercentage = SongTime / (SongDuration + 3) - - if SongTime < 0 then - SongPercentage = math.pow(SongTime / -1.5, 2) - end - - Components:Update(Delta) -end diff --git a/GameData/Skins/arctichare/skin_defs.lua b/GameData/Skins/arctichare/skin_defs.lua deleted file mode 100644 index 77d16aa4..00000000 --- a/GameData/Skins/arctichare/skin_defs.lua +++ /dev/null @@ -1,27 +0,0 @@ --- Scale from 3840x2160 to 1360x768 - -function GetScale() - local srcres = 3840 - local dstres = 1360 - return dstres / srcres -end - -SkinScale = GetScale() - --- scale values of list l by skin scale -function ScaleMap(l) - for k,v in ipairs(l) do - l[k] = l[k] * SkinScale - end -end - -function ScaleObj(o) - o.ScaleX = SkinScale - o.ScaleY = SkinScale -end - --- Player's default side on the field. 1 for left, 2 for right. -PlayerSide = 1 - --- Default side for the scratch. 1 for left, 2 for right. -ScratchSide = 1 diff --git a/GameData/Skins/default/Evaluation/scoreA.png b/GameData/Skins/default/Evaluation/scoreA.png deleted file mode 100644 index 73a1849b..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreA.png and /dev/null differ diff --git a/GameData/Skins/default/Evaluation/scoreAA.png b/GameData/Skins/default/Evaluation/scoreAA.png deleted file mode 100644 index 32eb702a..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreAA.png and /dev/null differ diff --git a/GameData/Skins/default/Evaluation/scoreAAA.png b/GameData/Skins/default/Evaluation/scoreAAA.png deleted file mode 100644 index 63ada050..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreAAA.png and /dev/null differ diff --git a/GameData/Skins/default/Evaluation/scoreB.png b/GameData/Skins/default/Evaluation/scoreB.png deleted file mode 100644 index 55ab0efc..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreB.png and /dev/null differ diff --git a/GameData/Skins/default/Evaluation/scoreC.png b/GameData/Skins/default/Evaluation/scoreC.png deleted file mode 100644 index 358e0f77..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreC.png and /dev/null differ diff --git a/GameData/Skins/default/Evaluation/scoreD.png b/GameData/Skins/default/Evaluation/scoreD.png deleted file mode 100644 index 7cf597ff..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreD.png and /dev/null differ diff --git a/GameData/Skins/default/Evaluation/scoreF.png b/GameData/Skins/default/Evaluation/scoreF.png deleted file mode 100644 index 57fe8afb..00000000 Binary files a/GameData/Skins/default/Evaluation/scoreF.png and /dev/null differ diff --git a/GameData/Skins/default/Global/Background.lua b/GameData/Skins/default/Global/Background.lua deleted file mode 100644 index 5233d8c6..00000000 --- a/GameData/Skins/default/Global/Background.lua +++ /dev/null @@ -1,83 +0,0 @@ - -if BackgroundAnimation then - return -end - -BackgroundAnimation = {Duration = 0.25} - -function BackgroundAnimation.Init(self) - if self.Initialized then - return - end - - self.shader = Shader() - self.shader:Compile [[ - #version 120 - varying vec2 texcoord; - uniform sampler2D tex; - uniform vec4 color; - uniform float frac; - uniform float persp; - void main(void) { - vec2 uv = texcoord - .5; - float z = sqrt(abs(.5*.5 - uv.x*uv.x - uv.y*uv.y)) / persp; - - vec2 rv = uv; - uv /= z; - - uv.x += frac / 4.; - /*vec4 col = texture2D(tex, uv) ; - float v = (col.r + col.g + col.b) / 3.; - vec4 gs = vec4(v,v,v,1.); - vec4 nc = vec4(0.48, 0.79, 1., 1.); - gl_FragColor = gs * persp * z; - */ - gl_FragColor = texture2D(tex, uv) * (z + 0.2); - } - ]] - - self.Initialized = true - self.Blue = Engine:CreateObject() - self.Pink = Engine:CreateObject() - - self.Pink.Texture = "Global/pink.png" - self.Blue.Texture = "Global/tile_aqua.png" - - self.Blue.Height = ScreenHeight - self.Blue.Width = ScreenWidth - self.Blue.Shader = self.shader - - self.Pink.Y = -self.Pink.Height - self.Blue.Y = 0 - self.Pink.Z = 0 - self.Blue.Z = 0 - self.Pink.Alpha = 0 - - self.t = 0 -end - -function BGAOut(frac) - BackgroundAnimation.Pink.Y = -BackgroundAnimation.Pink.Height * (1-frac) - BackgroundAnimation.shader:Send("persp", (frac) * 0.9 + 0.1) - return 1 -end - -function BGAIn(frac) - return BGAOut(1-frac) -end - -function BackgroundAnimation:In() - Engine:AddAnimation(self.Pink, "BGAIn", EaseIn, BackgroundAnimation.Duration, 0) -end - -function BackgroundAnimation:Out() - Engine:AddAnimation(self.Pink, "BGAOut", EaseOut, BackgroundAnimation.Duration, 0) -end - -function BackgroundAnimation.UpdateObjects(self) -end - -function BackgroundAnimation:Update(Delta) - self.t = self.t + Delta - self.shader.Send(self.shader, "frac", self.t) -end diff --git a/GameData/Skins/default/Global/FadeInScreen.lua b/GameData/Skins/default/Global/FadeInScreen.lua deleted file mode 100644 index da613870..00000000 --- a/GameData/Skins/default/Global/FadeInScreen.lua +++ /dev/null @@ -1,96 +0,0 @@ -game_require "librd" -skin_require "Global/Background" - -function FadeInA1(frac) - ScreenFade.Black.Alpha = frac - return 1 -end - -function invert(f, frac) - newf = function (frac) - return f(1 - frac) - end - - return newf -end - -ScreenFade = { Duration = 0.45 } - -function ScreenFade.Init() - BackgroundAnimation:Init() - ScreenFade.Black = Engine:CreateObject() - ScreenFade.Black.Texture = "Global/filter.png" - - with(ScreenFade.Black, { - Width = ScreenWidth, - Height = ScreenHeight, - Alpha = 0, - Layer = 31 - }) - - IFadeInA1 = invert(FadeInA1) - return - --[["Black1 = Engine:CreateObject() - Black2 = Engine:CreateObject() - - Black1.Image = "Global/filter.png" - Black2.Image = "Global/filter.png" - - Black1.Centered = 1 - Black2.Centered = 1 - - Black1.X = ScreenWidth/2 - Black2.X = ScreenWidth/2 - - Black1.Y = ScreenWidth/4 - Black2.Y = ScreenWidth*3/4 - - Black1.Alpha = 1 - Black2.Alpha = 1 - - Black1.Width = ScreenWidth - Black2.Width = ScreenWidth - - Black1.Height = ScreenHeight/2 - Black2.Height = ScreenHeight/2 - Black1.Z = 31 - Black2.Z = 31 - - IFadeInA1 = invert(FadeInA1) - IFadeInA2 = invert(FadeInA2) - ]] -end - -function ScreenFade.In(nobg) - local Delay = 0 - Engine:StopAnimation(ScreenFade.Black) - - if not nobg then - BackgroundAnimation:In() - Delay = BackgroundAnimation.Duration - end - - Engine:AddAnimation(Black1, "FadeInA1", EaseNone, ScreenFade.Duration, Delay) - return --[[ Lines beyond are previous implementation. - Engine:StopAnimation(Black1) - Engine:StopAnimation(Black2) - Engine:AddAnimation(Black1, "FadeInA1", EaseNone, 0.2, 0) - Engine:AddAnimation(Black2, "FadeInA2", EaseNone, 0.2, 0) - ]] -end - -function ScreenFade.Out(nobg) - Engine:StopAnimation(ScreenFade.Black) - Engine:AddAnimation(nil, "IFadeInA1", EaseNone, ScreenFade.Duration, 0) - - if not nobg then - BackgroundAnimation:Out() - end - - return --[[ - Engine:StopAnimation(Black1) - Engine:StopAnimation(Black2) - Engine:AddAnimation(Black1, "IFadeInA1", EaseNone, 0.2, 0) - Engine:AddAnimation(Black2, "IFadeInA2", EaseNone, 0.2, 0) - ]] -end diff --git a/GameData/Skins/default/Global/blue.png b/GameData/Skins/default/Global/blue.png deleted file mode 100644 index 5ecbbae7..00000000 Binary files a/GameData/Skins/default/Global/blue.png and /dev/null differ diff --git a/GameData/Skins/default/Global/checkbox.png b/GameData/Skins/default/Global/checkbox.png deleted file mode 100644 index 779a2162..00000000 Binary files a/GameData/Skins/default/Global/checkbox.png and /dev/null differ diff --git a/GameData/Skins/default/Global/checkbox_h.png b/GameData/Skins/default/Global/checkbox_h.png deleted file mode 100644 index af5318fb..00000000 Binary files a/GameData/Skins/default/Global/checkbox_h.png and /dev/null differ diff --git a/GameData/Skins/default/Global/checkbox_s.png b/GameData/Skins/default/Global/checkbox_s.png deleted file mode 100644 index b2297e09..00000000 Binary files a/GameData/Skins/default/Global/checkbox_s.png and /dev/null differ diff --git a/GameData/Skins/default/Global/checkbox_sh.png b/GameData/Skins/default/Global/checkbox_sh.png deleted file mode 100644 index a59202b9..00000000 Binary files a/GameData/Skins/default/Global/checkbox_sh.png and /dev/null differ diff --git a/GameData/Skins/default/Global/filter.png b/GameData/Skins/default/Global/filter.png deleted file mode 100644 index 6951796a..00000000 Binary files a/GameData/Skins/default/Global/filter.png and /dev/null differ diff --git a/GameData/Skins/default/Global/pink.png b/GameData/Skins/default/Global/pink.png deleted file mode 100644 index 779be9db..00000000 Binary files a/GameData/Skins/default/Global/pink.png and /dev/null differ diff --git a/GameData/Skins/default/Global/tile_aqua.png b/GameData/Skins/default/Global/tile_aqua.png deleted file mode 100644 index ed87131b..00000000 Binary files a/GameData/Skins/default/Global/tile_aqua.png and /dev/null differ diff --git a/GameData/Skins/default/Global/white.png b/GameData/Skins/default/Global/white.png deleted file mode 100644 index 0ca26a02..00000000 Binary files a/GameData/Skins/default/Global/white.png and /dev/null differ diff --git a/GameData/Skins/default/Loading/loading.png b/GameData/Skins/default/Loading/loading.png deleted file mode 100644 index d83e2cd3..00000000 Binary files a/GameData/Skins/default/Loading/loading.png and /dev/null differ diff --git a/GameData/Skins/default/Loading/loadingbadge.png b/GameData/Skins/default/Loading/loadingbadge.png deleted file mode 100644 index 471d4e68..00000000 Binary files a/GameData/Skins/default/Loading/loadingbadge.png and /dev/null differ diff --git a/GameData/Skins/default/Loading/phrases.lua b/GameData/Skins/default/Loading/phrases.lua deleted file mode 100644 index ecf77ced..00000000 --- a/GameData/Skins/default/Loading/phrases.lua +++ /dev/null @@ -1,143 +0,0 @@ -Phrases = { - Content = { - "That guy is saltier than the dead sea.", - "i have opinions: the movie", - "communication is beautiful", - "TRUST. BELIEVE. SUCCEED.", - "not even kaiden yet", - "a noodle soup a day and your skills won't decay", - "hey girl im kaiden want to go out", - "now with more drops, sadly rain does not produce dubstep.", - "i dropped out of school to play music games", - "tropical storm more like tropical fart", - "protip: dolphins are not capable of playing music games, let alone make music for them.", - "did you hear about this cool game called beatmani?", - "to be honest, it's not ez to dj.", - "at least we won't lose our source code.", - "less woosh more drop", - "studies show that certain rhythm game communities contain more cancerous and autistic people than other communities.", - "hot new bonefir remix knife party", - "i'll only date you if you're kaiden", - "it's called overjoy because the people who plays those charts are masochists", - "studies show that combo-based scoring is the biggest factor of broken equipment in the rhythm game community", - "YOU GET 200 GOLD CROWNS! IT IS EXTRAORDINARY!! YOU ARE THE TRUE TATSUJIN", - "nice meme", - "S P A C E T I M E", - "End Time.", - "I'm sending out a message, a message so sincere..", - "A message so sincere.. I need you more.", - "You gonna finish that ice cream sandwich?", - "TWO DEE ECKS GOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOLD", - "dude nice", - "You know what it is, bitch.", - "You're a master of karate and friendship for everyone!", - "\"your face is buggy\" - peppy", - "holy firetruck", - "CHAMPION OF THE ssssssssssssssssssssSUN", - "what a dumb ass nightingale", - "C H A O S M A I D G U Y", - "What the hell is that.", - "I'm not good enough for Blocko.", - "Evasiva coches.", - "future metallic can - premium skin", - "2/10", - "\"what the fuck is VOS\"", - "Party like it's BM98.", - "Everyone seems a bit too obsessed with the moon. I wonder if they're werewolves...", - "thanks mr. skeltal", - "rice and noodles erryday", - "reticulating splines", - ":^)", - "hi spy", - "hi arcwin", - "protip: to be overjoy you just have to avoid missing", - "Find us at http://vsrg.club !", - "\"Eating children is part of our lives.\"", - "Don't you put it in your mouth.", - "\"Like the game you may all know stepmania\"", - "Time to enjoy. I guess.", - "Are you kaiden yet?", - "Overmapping is a mapping style.", - "\"I play volumax.\" - Hazelnut-", - "\"mario paint music!!\" - peppy", - "very spammy", - "your favourite chart is shit", - "1.33 -> 0.33 -> 1.0 <=/=> 1.5 -> 0.5 -> 1.0", - "rip words", - "misses are bad", - "aiae lmao", - "\"573 or nothing\"", - "wats ur favrit 2hu", - "canmusic makes you ET", - "youdo me and ideu- you", - "As easy as ABCD.", - "You'll full combo it this time.", - "You're gonna carry that weight.", - "fappables/duck.gif", - "16 hours of B.O. blocko power!", - "how can there be 714 bpm if theres only 60 seconds in a minute?", - "Far East Nightbird (Twitch remix)", - "Just hold on. You'll be fine, I promise. Everyday.", - "2spooky", - "i'm not a kaiden i'm a ninja gaiden", - "did you seriously pay peppy 4$ to upload a fucking thomas the tank engine dump", - "\"mania is a pile of unmanageblae shit i'm not fixing it\" - peppy", - "Korean Mmorpg", - "I had a SV change trauma this SV change requires my no response in flying ball towards me", - "Re:apparantly wearing a fedora improves sightreading???", - "\"How does your osu have all notes go to one place?\"", - "Fuga Fuuuga Fuuuuuckga Fuuuuuuuuckga Darkstar PAZO light TRASH ACE WOOD HELL", - "JESUS WON'T COME TO SAVE YOU IN RHYTHM GAME HELL SON", - "slapping colorful hamburgers is one of my many hobbies", - "our park isn't very sunny in fact its raining", - "big colorful buttons", - "\"did you seirously pay peppy $4 to upload a fucking dump chart for the thomas and friends theme\" - fullerene-", - "\"I'LL NEVER PLAY 'BEAT-BEAT REVELATION' AGAIN!\"", - "What is SOWS? I tried to Google it but all I get is pictures of female pigs", - "To Abcdullah: your cheating is obvious, doing 100.00% on lv.26-28 maps from the first attempt is cheating, admit it.", - "konmai", - "haha facerolling", - "But the one in front of the gun lives forever.", - "children of the sky..", - "Open up you heart, no, peace, love for everyone", - "Come on, everybody in the universe, come on", - "I thought I was doing the most when somebody said to me...", - "when somebody said to me...", - "World, hold on! Instead of messing with our future, Tell me no more lies", - "If you ever meet your inner child, don’t cry. Tell them everything is gonna be alright", - "now with more bms!" - } -} - -function Phrases.Init() - Phrases.VSize = 22 - PhraseFont = Fonts.TruetypeFont(GetSkinFile("font.ttf"), Phrases.VSize); - Phrases.Text = StringObject2D() - Phrases.Text.Font = PhraseFont - - local selected = Phrases.Content[math.random(#Phrases.Content)] - Phrases.Text.Text = selected - Phrases.Text.Layer = 12 - Phrases.Text.Alpha = 0 - Phrases.Text.Rotation = 0 - Phrases.Text.X = 0 - Phrases.Text.Y = -Phrases.VSize - - Phrases.BG = Engine:CreateObject() - - Phrases.BG.Texture = "Global/filter.png" - Phrases.BG.Height = 30 - Phrases.BG.Layer = 11 - Phrases.BG.Width = ScreenWidth - Phrases.BG.X = 0 - - Engine:AddTarget(Phrases.Text) -end - -function Phrases.Fade(frac) - Phrases.Text.Alpha = frac - Phrases.Text.Y = -Phrases.VSize + Phrases.VSize * frac - - Phrases.BG.Alpha = frac - Phrases.BG.Y = -Phrases.BG.Height + Phrases.BG.Height * frac -end \ No newline at end of file diff --git a/GameData/Skins/default/MainMenu/BACKs.png b/GameData/Skins/default/MainMenu/BACKs.png deleted file mode 100644 index 069883df..00000000 Binary files a/GameData/Skins/default/MainMenu/BACKs.png and /dev/null differ diff --git a/GameData/Skins/default/MainMenu/FRONTs.png b/GameData/Skins/default/MainMenu/FRONTs.png deleted file mode 100644 index 2b49e4e4..00000000 Binary files a/GameData/Skins/default/MainMenu/FRONTs.png and /dev/null differ diff --git a/GameData/Skins/default/ScreenSelectMusic.rml b/GameData/Skins/default/ScreenSelectMusic.rml deleted file mode 100644 index c6c297ba..00000000 --- a/GameData/Skins/default/ScreenSelectMusic.rml +++ /dev/null @@ -1,96 +0,0 @@ - - - - - - - -
-
- Song Select -
-
-

Sort by...

-
- -
-
-
-
-
-

Hidden

-
- - -

Subsystem

-
-
Gauge

-
Timing

-
-

Speed Class

-
- -
-

Target Speed

-
- -
-
- Autoplay -
-
- Failure Disabled -
-
-
- Upscroll -
-
- Random -
-
-

Rate

-
- -
- -
-
- -
diff --git a/GameData/Skins/default/Scripts/AnimatedObjects.lua b/GameData/Skins/default/Scripts/AnimatedObjects.lua deleted file mode 100644 index 35c8cdcf..00000000 --- a/GameData/Skins/default/Scripts/AnimatedObjects.lua +++ /dev/null @@ -1,132 +0,0 @@ - -ProgressTick = { - Image = "VSRG/progress_tick.png", - Start = -8, - End = ScreenHeight -} - -Pulse = { - Image = "VSRG/pulse_ver.png", - Height = 100, -} - - - -MissHighlight = { - Image = "VSRG/miss_highlight.png" -} - -function ProgressTick:Init() - self.Object = ScreenObject { - Texture = self.Image, - Layer = 18, - X = self.Noteskin.GearStartX - 16 - } - - if self.Transformation then - self.Object.ChainTransformation = self.Transformation - end -end - -librd.make_new(ProgressTick, ProgressTick.Init) - -function ProgressTick:Run(Delta) - if Game.Active then - local dur = self.Player.BeatDuration - local Ratio = self.Player.Beat / dur - if self.Player.Time > 0 then - self.Object.Alpha = 1 - self.Object.Y = cmix(Ratio, self.Start, self.End) - else - self.Object.Alpha = 1 - self.Player.Time / -1.5 - self.Object.Y = cmix(math.pow(self.Player.Time / -1.5, 2), self.Start, self.End) - end - else - self.Object.Alpha = 0 - end -end - -function Pulse:Init() - self.Object = Engine:CreateObject() - - self.Object.Texture = self.Image - with(self.Object, { - BlendMode = BlendAdd, - Layer = 11, - Alpha = 0, - }) - self.Object.X = self.Noteskin.GearStartX - self.Object.Y = self.Player.JudgmentY - self.Object.Height - - if self.Transformation then - self.Object.ChainTransformation = self.Transformation - end -end - -librd.make_new(Pulse, Pulse.Init) - -function Pulse:Run(Delta) - - if Game.Active ~= 0 then - local BeatNth = 2 - local BeatMultiplied = self.Player.Beat * BeatNth - local NthOfBeat = 1 - - if floor(BeatMultiplied) % BeatNth == 0 then - NthOfBeat = fract(BeatMultiplied) - end - - self.Object.Alpha = 1 - NthOfBeat - else - self.Object.Alpha = 0 - end -end - -function MissHighlight:Init() - self.Time = {} - self.CurrentTime = {} - - for i=1, self.Player.Channels do - self[i] = ScreenObject { - Centered = 1, - Texture = self.Image, - X = self.Noteskin["Key" .. i .. "X"], - Y = ScreenHeight / 2, - Alpha = 0, - Layer = 15 - } - - self[i].Width = self.Noteskin["Key" .. i .. "Width"] - self[i].Height = ScreenHeight - - self.Time[i] = 1 - self.CurrentTime[i] = 1 - end - - if self.Transformation then - self[i].ChainTransformation = self.Transformation - end -end - -librd.make_new(MissHighlight, MissHighlight.Init) - -function MissHighlight:Run(Delta) - for i=1, self.Player.Channels do - self.CurrentTime[i] = self.CurrentTime[i] + Delta - self[i].Alpha = max(1 - self.CurrentTime[i] / self.Time[i], 0) - end -end - -function MissHighlight:OnMiss(t, l, h, pn) - if pn ~= self.Player.Number then - return - end - - self.CurrentTime[l] = 0 - local spb = 60 / self.Player.BPM - - if spb ~= math.huge then - self.Time[l] = math.min(spb / 4, 0.5) - end -end - diff --git a/GameData/Skins/default/Scripts/AutoplayAnimation.lua b/GameData/Skins/default/Scripts/AutoplayAnimation.lua deleted file mode 100644 index a2257fcd..00000000 --- a/GameData/Skins/default/Scripts/AutoplayAnimation.lua +++ /dev/null @@ -1,45 +0,0 @@ -AutoAnimation = {} - -function AutoAnimation:Init() - if not self.Player.Auto then - return - end - self.AutoBN = Engine:CreateObject() - - local sx = self.Noteskin.GearStartX + self.Noteskin.GearWidth/2 - BnMoveFunction = getMoveFunction(sx, -60, sx, 100, self.AutoBN) - - self.AutoBN.Texture = "VSRG/auto.png" - - self.AutoBN.Centered = 1 - - Engine:AddAnimation(self.AutoBN, "BnMoveFunction", EaseOut, 0.75, 0 ) - - local w = self.AutoBN.Width - local h = self.AutoBN.Height - - factor = 350 / w * 3/4 - self.AutoBN.Width = w * factor - self.AutoBN.Height = h * factor - self.AutoBN.Layer = 28 - self.AutoFinishAnimation = getUncropFunction(w*factor, h*factor, w, h, self.AutoBN) - self.RunAutoAnimation = true -end - -librd.make_new(AutoAnimation, AutoAnimation.Init) - -function AutoAnimation:OnSongFinish() - if self.AutoBN then - Engine:AddAnimation (AutoBN, "AutoFinishAnimation", EaseOut, 0.35, 0) - RunAutoAnimation = false - end -end - -function AutoAnimation:Run(Delta) - if self.AutoBN and self.RunAutoAnimation == true then - local BeatRate = self.Player.Beat / 2 - local Scale = sin( math.pi * 2 * BeatRate ) - Scale = Scale * Scale * 0.25 + 0.75 - self.AutoBN:SetScale(Scale, Scale) - end -end diff --git a/GameData/Skins/default/Scripts/ComboDisplay.lua b/GameData/Skins/default/Scripts/ComboDisplay.lua deleted file mode 100644 index 62f4bac7..00000000 --- a/GameData/Skins/default/Scripts/ComboDisplay.lua +++ /dev/null @@ -1,233 +0,0 @@ -ComboDisplay = { - DigitWidth = 50, - DigitHeight = 50, - Trasformation = nil, -- Use this to move the combo. - BumpTime = 0, - BumpTotalTime = 0.1, -- Time the animation runs - BumpFactor = 1.3, - BumpVertically = true, - BumpHorizontally = true, - HeightAddition = -20, - HoldBumpFactor = 1.2, - AtlasFile = "VSRG/combosheet.csv", - - ExNotify = true, -- Whether to use the PGREAT-FLAWLESS* notification - ExNotifyImg = "VSRG/combo_bonus.png", - ExNotifyTime = 0.34, - ExNotifyExtraBump = 0.5 -} - -function ComboDisplay:SetName(i) - return i-1 .. ".png" -end - -function ComboDisplay:Init() - self.X = self.Position and self.Position.x or self.Noteskin.GearStartX + self.Noteskin.GearWidth / 2 - self.Y = self.Position and self.Position.y or 0.3 * ScreenHeight - - if self.Player.Upscroll then - self.Y = ScreenHeight - self.Y - end - - self.Atlas = TextureAtlas:skin_new(self.AtlasFile) - - self.Images = {} - self.Targets = {} - - self.BumpMiss = 1 - self.BumpHit = 2 - self.BumpHold = 3 - self.BumpInactive = 0 - - self.BumpColor = 0 - - self.BumpKind = self.BumpInactive - - self.BumpRealTotalTime = self.BumpTotalTime - - self.Digits = {} - self.ExNotifyObject = nil - - for i = 1, 6 do -- Drawing targets - self.Targets[i] = ScreenObject { - Texture = self.Atlas.File, - Centered = 1, - Layer = 24, - Alpha = 0 - } - - if self.Transformation then - self.Targets[i].ChainTransformation = self.Transformation - end - end - - for i = 1, 10 do -- Digit images - self.Images[i] = self:SetName(i) - end - - if self.ExNotify then - self.ExNotifyObject = ScreenObject { - Centered = 1, - Layer = 24, - Alpha = 0, - Texture = self.ExNotifyImg - } - - self.ExNotifyCurTime = 0 - end -end - -librd.make_new(ComboDisplay, ComboDisplay.Init) - -function ComboDisplay:Update() - self.Digits = librd.intToDigits(self.Player.Combo) - - -- active digits = #Digits - local ActDig = #self.Digits + 1 - local Size = { - w = self.DigitWidth, - h = self.DigitHeight - } - - local DisplaySize = ActDig * Size.w / 2 - self.ExNotifyPos = { - x = DisplaySize + self.X, - y = - self.DigitHeight / 2 + self.Y - } - - for i = 1, 6 do - local NewPosition = { - x = DisplaySize - i * Size.w + self.X, - y = self.HeightAddition * (1 - self.BumpTime / self.BumpTotalTime) + self.Y - } - - if i < ActDig then - local d = ActDig - i - self.Atlas:SetObjectCrop(self.Targets[i], self.Images[self.Digits[d]+1]) - - with (self.Targets[i], { - Width = Size.w, - Height = Size.h, - Alpha = 1, - X = NewPosition.x, - Y = NewPosition.y - }) - else - self.Targets[i].Alpha = 0 - end - end -end - -function ComboDisplay:OnHit(j) - - self:Update() - self.BumpTime = 0 - self.BumpKind = BumpHit - self.BumpColor = j == 0 - - if self.BumpColor then - self.ExNotifyCurTime = 0 - end - -end - -function ComboDisplay:OnMiss() - - self:Update() - self.BumpTime = 0 - self.BumpKind = BumpMiss - -end - -function ComboDisplay:Run(Delta) - local Beat = self.Player.Beat - local Ratio = 1 - fract(Beat) - - -- the +2 at the topright - if #self.Digits ~= 0 then - - if self.BumpColor then - - self.ExNotifyObject.X = self.ExNotifyPos.x - self.ExNotifyObject.Y = self.ExNotifyPos.y - - local Factor = 1 + self.ExNotifyExtraBump * Ratio - self.ExNotifyObject:SetScale(Factor) - - else -- Time only runs if we're not at an "AWESOME" hit. - self.ExNotifyCurTime = self.ExNotifyCurTime + Delta - end - - local Ratio = math.max(1 - self.ExNotifyCurTime / self.ExNotifyTime, 0) - self.ExNotifyObject.Alpha = Ratio - - else - self.ExNotifyObject.Alpha = 0 - end - - local RebootHT = false - for i=1,self.Player.Channels do - if self.Player:IsHoldActive(i - 1) then - RebootHT = true - break - end - end - - local HoldScale = 1 - - if RebootHT then - local BT = Beat * 2 - local Ratio = (BT - math.floor(BT)) - HoldScale = self.HoldBumpFactor - Ratio * (self.HoldBumpFactor - 1) - end - - local newScaleRatio = 1 - - -- the numbers themselves - if self.BumpKind ~= 0 then - self.BumpTime = self.BumpTime + Delta - - local Ratio = self.BumpTime / self.BumpRealTotalTime - - if Ratio >= 1 then - self.BumpKind = self.BumpInactive; - end - - newScaleRatio = (self.BumpFactor - (Ratio * math.abs(self.BumpFactor - 1))) * HoldScale - end - - for i= 1, 6 do - local scaleX = 1 - local scaleY = 1 - local usedScale - - if self.BumpKind ~= self.BumpInactive then - usedScale = newScaleRatio - else - usedScale = HoldScale - end - - if self.BumpHorizontally then - scaleX = usedScale - end - - if self.BumpVertically then - scaleY = usedScale - end - - self.Targets[i].ScaleX = scaleX - self.Targets[i].ScaleY = scaleY - - if self.BumpColor ~= 0 then - self.Targets[i].Red = 1 - self.Targets[i].Green = 2.5 - self.Targets[i].Blue = 2.5 - else - self.Targets[i].Red = 1 - self.Targets[i].Green = 1 - self.Targets[i].Blue = 1 - end - end - - self:Update() -end diff --git a/GameData/Skins/default/Scripts/Explosions.lua b/GameData/Skins/default/Scripts/Explosions.lua deleted file mode 100644 index 82972745..00000000 --- a/GameData/Skins/default/Scripts/Explosions.lua +++ /dev/null @@ -1,171 +0,0 @@ -Explosions = { - HitFramerate = 60, - HitFrames = 10, - HitScale = 1, - HitSheet = "VSRG/explsheet.csv", - - HoldFramerate = 60, - HoldFrames = 40, - HoldScale = 1, - HoldSheet = "VSRG/holdsheet.csv", - - KeysSheet = "VSRG/keys.csv", - MissShow = 1 -} - -function Explosions.HitName (i) - return "lightingN-" .. i-1 .. ".png" -end - -function Explosions.HoldName (i) - return "lightingL-" .. i-1 .. ".png" -end - --- Internal functions -function Explosions:ObjectPosition(Obj, Atlas, i, Scale) - Obj.Texture = "VSRG/"..Atlas.File - Obj.Centered = 1 - Obj.X = self.Noteskin["Key"..i.."X"] - Obj.Y = self.Player.JudgmentY - - if self.Player.Upscroll ~= 0 then - Obj.Rotation = 180 - end - - Obj.Layer = (28) - Obj.BlendMode = BlendAdd -- Add - Obj:SetScale(Scale) - Obj.Alpha = 0 -end - -function Explosions:Init() - self.HitImages = {} - self.HitTargets = {} - self.HitTime = {} - self.HitColorize = {} - self.HitFrameTime = 1/self.HitFramerate - self.HitAnimationDuration = self.HitFrameTime * self.HitFrames - - self.HoldImages = {} - self.HoldTargets = {} - self.HoldTime = {} - self.HoldFrameTime = 1/self.HoldFramerate - self.HoldDuration = self.HoldFrameTime * self.HoldFrames - - - self.HitAtlas = TextureAtlas:skin_new(self.HitSheet) - self.HoldAtlas = TextureAtlas:skin_new(self.HoldSheet) - - for i = 1, self.HitFrames do - self.HitImages[i] = self.HitName(i) - end - - for i = 1, self.HoldFrames do - self.HoldImages[i] = self.HoldName(i) - end - - for i = 1, self.Player.Channels do - -- Regular explosions - self.HitTargets[i] = Engine:CreateObject() - - self:ObjectPosition(self.HitTargets[i], self.HitAtlas, i, self.HitScale) - - self.HitTime[i] = self.HitFrameTime * self.HitFrames - self.HitColorize[i] = 0 - - -- Hold explosions - self.HoldTargets[i] = Engine:CreateObject() - - self:ObjectPosition(self.HoldTargets[i], self.HoldAtlas, i, self.HoldScale) - self.HoldTime[i] = self.HoldDuration - end -end - -librd.make_new(Explosions, Explosions.Init) - -function Explosions:Run(Delta) - for i = 1, self.Player.Channels do - self.HitTime[i] = self.HitTime[i] + Delta - - -- Calculate frame - Frame = self.HitTime[i] / self.HitFrameTime + 1 - - if Frame > self.HitFrames or - (self.HitColorize[i] ~= 0 and self.MissShow == 0) then - self.HitTargets[i].Alpha = (0) - else - self.HitTargets[i].Alpha = (1) - - -- Assign size and stuff according to texture atlas - local Tab = self.HitAtlas.Sprites[self.HitImages[floor(Frame)]] - - --[[if not Tab then - print (self.HitImages[math.floor(Frame)]) - end]] - - self.HitTargets[i]:SetCropByPixels(Tab.x, Tab.x+Tab.w, Tab.y+Tab.h, Tab.y) - self.HitTargets[i].Width = Tab.w - self.HitTargets[i].Height = Tab.h - - -- Colorize the explosion red? - if self.HitColorize[i] then - self.HitTargets[i].Red = 1.2 - self.HitTargets[i].Green = 0.2 - self.HitTargets[i].Blue = 0.2 - else - self.HitTargets[i].Red = 1 - self.HitTargets[i].Green = 1 - self.HitTargets[i].Blue = 1 - end - - end - - self.HoldTime[i] = self.HoldTime[i] + Delta - - -- Update animation loop for holds - while self.HoldTime[i] > self.HoldDuration do - self.HoldTime[i] = self.HoldTime[i] - self.HoldDuration - end - - Frame = self.HoldTime[i] / self.HoldFrameTime + 1 - - if not self.Player:IsHoldActive(i - 1) then - self.HoldTargets[i].Alpha = (0) - else - self.HoldTargets[i].Alpha = (1) - local Tab = self.HoldAtlas.Sprites[self.HoldImages[floor(Frame)]] - - self.HoldTargets[i]:SetCropByPixels(Tab.x, Tab.x + Tab.w, Tab.y + Tab.h, Tab.y) - self.HoldTargets[i].Width = Tab.w - self.HoldTargets[i].Height = Tab.h - - end - - end -end - -function Explosions:OnHit(j, t, l, IsHold, IsHoldRelease, pn) - if pn ~= self.Player.Number then - return - end - - if IsHold then - self.HoldTime[l] = 0 - end - - if (not IsHold) or IsHoldRelease then - self.HitTime[l] = 0 - self.HitColorize[l] = false - end -end - -function Explosions:OnMiss(t, l, i, pn) - if pn ~= self.Player.Number then - return - end - - if not IsHold then - self.HitTime[l] = 0 - self.HitColorize[l] = true - end -end diff --git a/GameData/Skins/default/Scripts/FixedObjects.lua b/GameData/Skins/default/Scripts/FixedObjects.lua deleted file mode 100644 index a6c12881..00000000 --- a/GameData/Skins/default/Scripts/FixedObjects.lua +++ /dev/null @@ -1,58 +0,0 @@ - -Filter = { Image = "Global/Filter.png" } -JudgeLine = { Image = "VSRG/JudgeLine.png" } -StageLines = { ImageLeft = "VSRG/stage-left.png", ImageRight = "VSRG/stage-right.png" } - -function Filter:Init() - if GetConfigF("ScreenFilter", "") == 0 then - return - end - - FilterVal = GetConfigF("ScreenFilter", "") - - self.Object = Engine:CreateObject() - - self.Object.Texture = (self.Image) - - self.Object.X = self.Noteskin.GearStartX - self.Object.Width = self.Noteskin.GearWidth - self.Object.Height = ScreenHeight - self.Object.Alpha = FilterVal - self.Object.Layer = 1 -end - -librd.make_new(Filter, Filter.Init) - -function JudgeLine:Init() - self.Object = Engine:CreateObject() - self.Size = { w = self.Noteskin.GearWidth, h = self.Noteskin.NoteHeight } - - self.Object.Texture = self.Image - self.Object.Centered = 1 - - self.Object.X = self.Noteskin.GearStartX + self.Noteskin.GearWidth / 2 - self.Object.Y = self.Player.JudgmentY - - self.Object.Width = self.Size.w - self.Object.Height = self.Size.h - self.Object.Layer = 12 -end - -librd.make_new(JudgeLine, JudgeLine.Init) - -function StageLines:Init() - self.Left = Engine:CreateObject() - self.Right = Engine:CreateObject() - - self.Left.Texture = self.ImageLeft - self.Left.X = self.Noteskin.GearStartX - self.Left.Width - self.Left.Height = ScreenHeight - self.Left.Layer = 16 - - self.Right.Texture = (self.ImageRight) - self.Right.X = (self.Noteskin.GearStartX + self.Noteskin.GearWidth) - self.Right.Height = ScreenHeight - self.Right.Layer = 20 -end - -librd.make_new(StageLines, StageLines.Init) \ No newline at end of file diff --git a/GameData/Skins/default/Scripts/KeyLightning.lua b/GameData/Skins/default/Scripts/KeyLightning.lua deleted file mode 100644 index 7168452d..00000000 --- a/GameData/Skins/default/Scripts/KeyLightning.lua +++ /dev/null @@ -1,147 +0,0 @@ -HitLightning = { Enabled = true } - -HitLightning.Image = "VSRG/HitLightning.png" -HitLightning.Height = 250 - -local function LightFunction () - if GetConfigF("DisableHitlightningAnimation", "") == 1 then - return 0 - else - return 1 - end -end - ---[[ - note to self: - Constructor takes in a player context and a noteskin. - Player = ... - Noteskin = Noteskin[Channels] - - something like that. -]] - -function HitLightning:Init() - - if not self.Enabled or (GetConfigF("Hitlightning", "") == 0) then - print "Hit Lightning disabled." - self.Enabled = false - return - end - - print "Hit Lightning enabled." - - self.OffTime = {} - - self.Times = {} - self.Animate = LightFunction() - - self.Pressed = {} - self.Position = {} - self.Channels = self.Player.Channels - - for i = 1, self.Channels do - self.Times[i] = 1 - self.Pressed[i] = 0 - end - - for i=1, self.Channels do - self[i] = Engine:CreateObject() - - self.OffTime[i] = 1 - - self[i].Texture = self.Image - self[i].Centered = 1 - self[i].BlendMode = BlendAdd - - self[i].Width = self.Noteskin["Key"..i.."Width"] - self[i].Height = self.Height - - local h = self.Player.JudgmentY - local scrollY = 0 - local scrollX = 0 - scrollX = self.Noteskin["Key" .. i .. "X"] - - if self.Player.Upscroll then - scrollY = h + self.Height / 2 - self[i].Rotation = 180 - else - scrollY = h - self.Height / 2 - end - - self.Position[i] = { x = scrollX, y = scrollY } - self[i].X = scrollX - self[i].Y = scrollY - self[i].Layer = 15 - self[i].Alpha = 0 - end -end - -librd.make_new(HitLightning, HitLightning.Init) - -function HitLightning:GearKeyEvent(Lane, IsKeyDown, pn) - if not self.Enabled or pn ~= self.Player.Number then - return - end - - local spb = 60 / self.Player.BPM - - if spb ~= math.huge then - self.OffTime[Lane] = math.min(spb / 1.5, 1) - end - - if self.OffTime[Lane] > 3 then - self.OffTime[Lane] = 3 - end - - if not IsKeyDown then - self.Times[Lane] = 0 - self.Pressed[Lane] = 0 - else - self.Pressed[Lane] = 1 - end -end - -function HitLightning:Run(Delta) - if not self.Enabled then - return - end - - for i=1, self.Channels do - self.Times[i] = self.Times[i] + Delta - - if self.Pressed[i] == 0 then - if self.Times[i] <= self.OffTime[i] then - if self.Animate == 1 then - local Lerping = math.pow(self.Times[i] / self.OffTime[i], 2) - local Additive - self[i].ScaleX = 1 - Lerping - self[i].ScaleY = 1 + 1.5 * Lerping - - Additive = self.Height / 2 * 1.5 * Lerping - - if self.Player.Upscroll then - Additive = Additive * -1 - end - - self[i].Y = self.Position[i].y - Additive - self[i].Alpha = ( 1 - Lerping ) - elseif self.Animate == 2 then - local Lerping = math.pow(self.Times[i] / self.OffTime[i], 2) - local Additive = 0 - - Additive = self.Height / 2 * Lerping - self[i].ScaleY = 1 - Lerping - self[i].Y = self.Position[i].y + Additive - self[i].Alpha = 1 - end - else - self[i].Alpha = 0 - end - - else - self[i]:SetScale(1) - self[i].Alpha = 1 - self[i].Y = self.Position[i].y - end - end -end \ No newline at end of file diff --git a/GameData/Skins/default/Scripts/Keys.lua b/GameData/Skins/default/Scripts/Keys.lua deleted file mode 100644 index eda4c2fc..00000000 --- a/GameData/Skins/default/Scripts/Keys.lua +++ /dev/null @@ -1,52 +0,0 @@ -Keys = { - KeysSheet = "VSRG/keys.csv" - } - -function Keys:Init() - self.KeysUp = {} - self.KeysDown = {} - - self.KeyAtlas = TextureAtlas:skin_new(self.KeysSheet) - - self.Keys = {} - - -- Keys - for i=1, self.Player.Channels do - self.Keys[i] = Engine:CreateObject() - local obj = self.Keys[i] - obj.Centered = 1 - obj.X = self.Noteskin["Key" .. i .. "X"] - obj.Texture = self.KeyAtlas.File - obj.Layer = 27 - self.KeysUp[i] = self.Noteskin["Key" .. i] - self.KeysDown[i] = self.Noteskin["Key" .. i .. "Down"] - - self.KeyAtlas:SetObjectCrop(obj, self.KeysUp[i]) - - obj.Width = self.Noteskin["Key" .. i .. "Width"] - obj.Height = self.Noteskin.GearHeight - - if self.Player.Upscroll then - obj.Y = self.Player.JudgmentY - obj.Height / 2 - self.Noteskin.NoteHeight / 2 - obj.Rotation = 180 - else - obj.Y = self.Player.JudgmentY + obj.Height / 2 + self.Noteskin.NoteHeight / 2 - end - end -end - -librd.make_new(Keys, Keys.Init) - -function Keys:GearKeyEvent(i, IsKeyDown, pn) - if pn ~= self.Player.Number then - return - end - - if IsKeyDown then - self.KeyAtlas:SetObjectCrop(self.Keys[i], self.KeysDown[i]) - else - self.KeyAtlas:SetObjectCrop(self.Keys[i], self.KeysUp[i]) - end -end - -return Keys \ No newline at end of file diff --git a/GameData/Skins/default/Scripts/Lifebar.lua b/GameData/Skins/default/Scripts/Lifebar.lua deleted file mode 100644 index c9047891..00000000 --- a/GameData/Skins/default/Scripts/Lifebar.lua +++ /dev/null @@ -1,128 +0,0 @@ - - -Lifebar = { - FillSize = 500, - MarginFile = "VSRG/stage-lifeb.png", - FillFile = "VSRG/stage-lifeb-s.png", - Width = 50, - FillWidth = 50, - Speed = 10, - FillOffset = -2 -} - -Jambar = { - ImageFG = "VSRG/jam_bar.png" -} - -function Lifebar:Init() - self.Margin = Engine:CreateObject() - self.Fill = Engine:CreateObject() - self.Fill2 = Engine:CreateObject() - - self.Margin.Texture = self.MarginFile - self.Margin.Layer = 25 - self.Margin.Centered = 1 - -- self.Margin.Width = self.Width - - local w = self.Margin.Width - local h = self.Margin.Height - - - self.Position = { - x = self.Noteskin.GearStartX + self.Noteskin.GearWidth + self.Width / 2 + 8, - y = ScreenHeight - h / 2 - } - - self.CurrentPosition = { - x = self.Position.x, - y = ScreenHeight - } - - self.Margin.X = self.CurrentPosition.x - self.Margin.Y = self.Position.y - - self.Fill.Texture = self.FillFile - self.Fill.Width = self.FillWidth - self.Fill.Height = self.FillSize - self.Fill.Layer = 26 - self.Fill.Centered = 1 - - self.Fill2.Texture = self.FillFile - self.Fill2.Layer = 26 - self.Fill2.Centered = 1 - self.Fill2.BlendMode = BlendAdd - self.Fill2.Width = self.Fill.Width - self.Display = 0 - - self:Run(0) -end - -librd.make_new(Lifebar, Lifebar.Init) - -function Lifebar:Run(Delta) - local DeltaLifebar = (self.Player.LifebarPercent / 100 - self.Display) - local DP = 1 - fract(self.Player.Beat) - - self.Display = DeltaLifebar * Delta * self.Speed + self.Display - - local partA = self.Display * 0.98 - local partB = self.Display * 0.02 * DP - local Display = partA + partB - local NewY = ScreenHeight - self.FillSize * (Display) / 2 - local NewYFixed = ScreenHeight - self.FillSize * (self.Display) / 2 - - self.CurrentPosition = self.Position - - self.Fill.ScaleY = self.Display - self.Fill.X = self.Position.x + self.FillOffset - self.Fill.Y = NewYFixed - self.Fill:SetCropByPixels( 0, self.Width, self.FillSize - self.FillSize * self.Display, self.FillSize ) - - self.Fill2.ScaleY = Display - self.Fill2.X = self.Position.x + self.FillOffset - self.Fill2.Y = NewY - self.Fill2:SetCropByPixels( 0, self.Width, self.FillSize - self.FillSize * Display, self.FillSize ) - self.Fill2.Alpha = ( DP * self.Player.LifebarPercent / 100 ) -end - - -function Jambar:Init() - self.Width = 50 - self.Height = self.Height or 335 - - self.BarFG = Engine:CreateObject() - self.BarFG.Texture = self.ImageFG; - - with (self.BarFG, { - Centered = 0, - X = self.Noteskin.GearStartX + self.Noteskin.GearWidth + self.Width / 2 + 5, - Layer = 25, - Width = Jambar.Width, - Height = Jambar.Height, - Lighten = 1 - }) - - self:Run(0) -end - -librd.make_new(Jambar, Jambar.Init) - -function Jambar:Run(Delta) - local remaining - local ScoreKeeper = self.Player.Scorekeeper - - if not ScoreKeeper.UsesO2 then - remaining = ScoreKeeper.JudgedNotes / ScoreKeeper.MaxNotes - else - remaining = 1 - (15 - (ScoreKeeper.CoolCombo % 15 + 1)) / 15 - end - - -- Percentage from 0 to 1 of cool combo - - self.BarFG.LightenFactor = 1 - fract(self.Player.Beat) - - local Offset = remaining * self.Height - self.BarFG.ScaleY = remaining - self.BarFG.Y = ScreenHeight - Offset / 2 - self.BarFG:SetCropByPixels( 0, self.Width, self.BarFG.Height - Offset, self.BarFG.Height ) -end \ No newline at end of file diff --git a/GameData/Skins/default/Scripts/ScoreDisplay.lua b/GameData/Skins/default/Scripts/ScoreDisplay.lua deleted file mode 100644 index 6f0cce28..00000000 --- a/GameData/Skins/default/Scripts/ScoreDisplay.lua +++ /dev/null @@ -1,81 +0,0 @@ -ScoreDisplay = ScoreDisplay or {} - -with(ScoreDisplay, { - DigitWidth = 30, - Sheet = "VSRG/combosheet.csv", - DigitHeight = 30, - DigitCount = 9, -}) - -with(ScoreDisplay, { - W = ScoreDisplay.DigitWidth * ScoreDisplay.DigitCount, - H = ScoreDisplay.DigitHeight -}) - -with(ScoreDisplay, { - X = ScreenWidth - ScoreDisplay.W, -- Topleft - Y = ScreenHeight - ScoreDisplay.H, - Layer = 20 -}) - -function ScoreDisplay:SetName(i) - return i-1 .. ".png" -end - -function ScoreDisplay:Init() - - self.Targets = {} - self.Images = {} - - self.Digits = {} - - self.Score = 0 - self.DisplayScore = 0 - - for i = 1, 10 do -- Digit images - self.Images[i] = self:SetName(i) - end - - self.Atlas = TextureAtlas:new(GetSkinFile(self.Sheet)) - - for i = 1, self.DigitCount do - self.Targets[i] = Engine:CreateObject() - - self.Targets[i].X = self.X + self.W - self.DigitWidth * i - self.Targets[i].Y = self.Y + self.H - self.DigitHeight - self.Targets[i].Texture = self.Atlas.File - self.Targets[i].Width = self.DigitWidth - self.Targets[i].Height = self.DigitHeight - self.Targets[i].Layer = self.Layer - - local Tab = self.Atlas.Sprites[self.Images[1]] - - self.Targets[i]:SetCropByPixels(Tab.x, Tab.x+Tab.w, Tab.y+Tab.h, Tab.y) - - self.Targets[i].Alpha = (1) - end -end - -librd.make_new(ScoreDisplay, ScoreDisplay.Init) - -function ScoreDisplay:Run(Delta) - self.Score = self.Player.Score - - self.DisplayScore = math.min((self.Score - self.DisplayScore) * Delta * 40 + self.DisplayScore, self.Score) - local Digits = librd.intToDigits(self.DisplayScore) - local tdig = #Digits - - for i=1, tdig do - local Tab = self.Atlas.Sprites[self.Images[Digits[tdig - (i - 1)]+1]] - self.Targets[i]:SetCropByPixels(Tab.x, Tab.x+Tab.w, Tab.y, Tab.y+Tab.h) - self.Targets[i].Alpha = (1) - end - - for i=tdig+1, self.DigitCount do - local Tab = self.Atlas.Sprites[self.Images[1]] - - self.Targets[i]:SetCropByPixels(Tab.x, Tab.x+Tab.w, Tab.y+Tab.h, Tab.y) - self.Targets[i].Alpha = (1) - end - -end diff --git a/GameData/Skins/default/Scripts/StageAnimation.lua b/GameData/Skins/default/Scripts/StageAnimation.lua deleted file mode 100644 index 6c741041..00000000 --- a/GameData/Skins/default/Scripts/StageAnimation.lua +++ /dev/null @@ -1,176 +0,0 @@ -FailAnimation = { - Duration = 2 -} - -SuccessAnimation = { - Duration = 5.5 -} - -function fcnot2f(frac) - fcnotify2.Alpha = (1 - frac) - fcnotify2:SetScale(1 + frac * 1.5) - fcnotify2.LightenFactor = (0.3 * (1 - frac)) - return 1 -end - -function DoFullComboAnimation() - fcnotify = Engine:CreateObject () - fcnotify.Texture = "VSRG/fullcombo.png" - - fcnotify.X = ScreenWidth / 2 - fcnotify.Y = -fcnotify.Height / 2 - fcnotify.Z = 30 - fcnotify.Centered = 1 - fcanim = getMoveFunction(fcnotify.X, ScreenHeight + fcnotify.Height/2, fcnotify.X, ScreenHeight*3/4, fcnotify) - - fcnotify2 = Engine:CreateObject() - fcnotify2.Texture = "VSRG/fullcombo.png" - - fcnotify2.X = ScreenWidth / 2 - fcnotify2.Y = ScreenHeight*3/4 - fcnotify2.Z = 30 - fcnotify2.Centered = 1 - fcnotify2.Alpha = 0 - fcnotify2.BlendMode = BlendAdd - fcnotify2.Lighten = 1 - - fcnotfade = getFadeFunction(1, 0, fcnotify) - - Engine:AddAnimation(fcnotify, "fcanim", EaseOut, 0.75, 3) - Engine:AddAnimation(fcnotify2, "fcnot2f", EaseOut, 0.25, 0.75 + 3) - Engine:AddAnimation(fcnotify, "fcnotfade", EaseNone, 0.5, 4) -end - -function FadeInBlack(frac) - Black.Alpha = (frac) - return 1 -end - -function ZoomVertIn(frac) - StageClear.Alpha = (frac) - StageClear:SetScale(frac) - return 1 -end - -function ZoomVertOut(frac) - StageClear.Alpha = (1 - frac) - return 1 -end - -function FadeToBlack() - Black = Engine:CreateObject() - Black.Texture = "Global/filter.png" - Black.Alpha = 0 - Black.Width = ScreenWidth - Black.Height = ScreenHeight - Black.Z = 29 - Engine:AddAnimation(Black, "FadeInBlack", EaseNone, 0.5, 3) -end - -function DoSuccessAnimation() - FadeToBlack() - - StageClear = Engine:CreateObject() - StageClear.Texture = "VSRG/stageclear.png" - StageClear.Centered = 1 - StageClear.X = ScreenWidth / 2 - StageClear.Y = ScreenHeight / 2 - StageClear.Z = 31 - StageClear.Alpha = 0 - - - if IsFullCombo then - DoFullComboAnimation() - end - - Engine:Sort() - Engine:AddAnimation(StageClear, "ZoomVertIn", EaseOut, 0.75, 3) - Engine:AddAnimation(StageClear, "ZoomVertOut", EaseNone, 1, 4) -end - - -function FailBurst(frac) - local TargetScaleA = 4 - local TargetScaleB = 3 - local TargetScaleC = 2 - BE.FnA.Alpha = 1 - frac - BE.FnB.Alpha = 1 - frac - BE.FnC.Alpha = 1 - frac - - BE.FnA.ScaleY = 1 + (TargetScaleA-1) * frac - BE.FnB.ScaleY = 1 + (TargetScaleB-1) * frac - BE.FnC.ScaleY = 1 + (TargetScaleC-1) * frac - BE.FnA.ScaleX = 1 + (TargetScaleA-1) * frac - BE.FnB.ScaleX = 1 + (TargetScaleB-1) * frac - BE.FnC.ScaleX = 1 + (TargetScaleC-1) * frac - - return 1 -end - -function FailAnim(frac) - - local fnh = FailNotif.Height - local fnw = FailNotif.Width - local cosfacadd = 0.75 - local cos = math.cos(frac * 2 * math.pi) * cosfacadd - local ftype = (1-frac) - local sc = (cos + cosfacadd/2) * (ftype * ftype) * 1.2 + 1 - FailNotif.ScaleY = sc - FailNotif.ScaleX = sc - - return 1 -end - -function WhiteFailAnim(frac) - White.Height = ScreenHeight * frac - return 1 -end - -function DoFailAnimation() - White = Engine:CreateObject() - FailNotif = Engine:CreateObject() - - White.Centered = 1 - White.X = ScreenWidth / 2 - White.Y = ScreenHeight / 2 - White.Height = 0 - White.Texture = "Global/white.png" - White.Width = ScreenWidth - FailNotif.Texture = "VSRG/stagefailed.png" - FailNotif.Centered = 1 - FailNotif.X = ScreenWidth / 2 - FailNotif.Y = ScreenHeight / 2 - - White.Z = 30 - FailNotif.Z = 31 - - Engine:AddAnimation(White, "WhiteFailAnim", EaseIn, 0.35, 0) - Engine:AddAnimation(FailNotif, "FailAnim", EaseNone, 0.75, 0) - - BE = {} - BE.FnA = Engine:CreateObject() - BE.FnB = Engine:CreateObject() - BE.FnC = Engine:CreateObject() - BE.FnA.Texture = "VSRG/stagefailed.png" - BE.FnB.Texture = "VSRG/stagefailed.png" - BE.FnC.Texture = "VSRG/stagefailed.png" - - BE.FnA.X = ScreenWidth/2 - BE.FnB.X = ScreenWidth/2 - BE.FnC.X = ScreenWidth/2 - BE.FnA.Y = ScreenHeight/2 - BE.FnB.Y = ScreenHeight/2 - BE.FnC.Y = ScreenHeight/2 - BE.FnA.Centered = 1 - BE.FnB.Centered = 1 - BE.FnC.Centered = 1 - BE.FnA.Alpha = 0 - BE.FnB.Alpha = 0 - BE.FnB.Alpha = 0 - BE.FnA.Z = 31 - BE.FnB.Z = 31 - BE.FnC.Z = 31 - - Engine:Sort() - Engine:AddAnimation(BE.FnA, "FailBurst", EaseOut, 0.7, 0.33 * 0.75) -end diff --git a/GameData/Skins/default/Scripts/TextDisplay.lua b/GameData/Skins/default/Scripts/TextDisplay.lua deleted file mode 100644 index 98318e5d..00000000 --- a/GameData/Skins/default/Scripts/TextDisplay.lua +++ /dev/null @@ -1,142 +0,0 @@ --- text on window - -PlayerText = {} - -function PlayerText:Init() - - self.pacemaker1 = StringObject2D(); - self.pacemaker2 = StringObject2D(); - self.judgments = StringObject2D(); - - self.lifebar = StringObject2D(); - - if fnt1 == nil then - fnt1 = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 20); - fnt2 = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 40); - fntB = Fonts.BitmapFont() - Fonts.LoadBitmapFont(fntB, "font.tga", 8, 16, 6, 15, 0); - end - - -- matches default judgment - local jX = self.Noteskin.GearWidth / 2 + self.Noteskin.GearStartX - local jY = ScreenHeight * 0.4 - - - self.pacemaker1.Text = ""; - self.pacemaker1.Font = fnt1; - self.pacemaker1.X = jX - 65; - self.pacemaker1.Y = jY + 20; - self.pacemaker1.Layer = 24; - - self.pacemaker2.Text = ""; - self.pacemaker2.Font = fnt1; - self.pacemaker2.X = jX - 20; - self.pacemaker2.Y = jY + 20; - self.pacemaker2.Layer = 26; - - self.lifebar.Text = "0"; - self.lifebar.Font = fnt2; - self.lifebar.X = self.Noteskin.GearStartX + self.Noteskin.GearWidth + 80; - self.lifebar.Y = 340; - - self.judgments.Font = fntB - self.judgments.X = self.lifebar.X - self.judgments.Y = 560 - - self.author = StringObject2D() - - self.author.Font = fnt1 - self.author.X = self.lifebar.X - self.author.Y = 380 - - local sng = Global:GetSelectedSong() - local diff = self.Player.Difficulty - if diff.Author ~= "" then - difftxt = string.format("%s by %s", diff.Name, diff.Author) - else - difftxt = string.format("%s", diff.Name) - end - self.author.Text = string.format("\n%s by %s\n%s", sng.Title, sng.Author, difftxt) - - Engine:AddTarget(self.author) - Engine:AddTarget(self.pacemaker1); - Engine:AddTarget(self.pacemaker2); - Engine:AddTarget(self.lifebar); - Engine:AddTarget(self.judgments) -end - -librd.make_new(PlayerText, PlayerText.Init) - -function PlayerText:Run(dt) - - --[[ - You get two pacemakers: - Raindrop Rank (PacemakerText/PacemakerValue) - BMS Rank (BMPacemakerText/BMPacemakerValue) - - Switch between them as you will. - ]] - - -- true = bm, false = rdr - local pmt = self.Player:GetPacemakerText(true) - local pmv = self.Player:GetPacemakerValue(true) - - -- The following update the pacemaker for real. - if pmt then - self.pacemaker1.Text = pmt; - end - - if pmv then - pre_char = "+"; - if pmv < 0 then - pre_char = "-"; - self.pacemaker2.Red = 1 - self.pacemaker2.Green = 0 - self.pacemaker2.Blue = 0 - elseif pmv > 0 then - self.pacemaker2.Red = 0.45 - self.pacemaker2.Green = 0.45 - self.pacemaker2.Blue = 1 - else - self.pacemaker2.Red = 1 - self.pacemaker2.Green = 1 - self.pacemaker2.Blue = 1 - end - - self.pacemaker2.Text = string.format("%s%04d", pre_char, math.abs(pmv)); - end - - self.lifebar.Text = string.format("%03d%%", self.Player.LifebarPercent); - - local mlt = self.Player.SpeedMultiplier - local vspd = self.Player.Speed - local fmtext = string.format("Speed: %02.2fx (%.0f -> %.0f)\n", mlt, vspd, mlt*vspd) - local ScoreKeeper = self.Player.Scorekeeper - local w0, w1, w2, w3, w4, w5 - - w0 = ScoreKeeper:GetJudgmentCount(SKJ_W0) - w1 = ScoreKeeper:GetJudgmentCount(SKJ_W1) - w2 = ScoreKeeper:GetJudgmentCount(SKJ_W2) - w3 = ScoreKeeper:GetJudgmentCount(SKJ_W3) - w4 = ScoreKeeper:GetJudgmentCount(SKJ_W4) - w5 = ScoreKeeper:GetJudgmentCount(SKJ_MISS) - if ScoreKeeper.UsesW0 == false then - if ScoreKeeper.UsesO2 == false then - fmtext = fmtext .. string.format("E:%04d\nS:%04d\nN:%04d\nO:%04d\nM:%04d", w1, w2, w3, w4, w5) - else - local p = ScoreKeeper.Pills - local rem = 15 - ScoreKeeper.CoolCombo % 15 - fmtext = fmtext .. string.format("F:%04d\nE:%04d\nO:%04d\nM:%04d\nP:%04d / CC: %04d", w1, w2, w3, w5, p, rem) - end - else - fmtext = fmtext .. string.format("F:%04d\nE:%04d\nS:%04d\nN:%04d\nO:%04d\nM:%04d", w0, w1, w2, w3, w4, w5) - end - - fmtext = fmtext .. string.format("\nMaxCombo: %d", ScoreKeeper:GetScore(ST_MAX_COMBO)) - fmtext = fmtext .. string.format("\nBPM: %d", self.Player.BPM) - fmtext = fmtext .. string.format("\nAvg. Hit (ms): %f", ScoreKeeper.AvgHit) - - self.judgments.Text = fmtext -end - - diff --git a/GameData/Skins/default/Scripts/judgment.lua b/GameData/Skins/default/Scripts/judgment.lua deleted file mode 100644 index 78b7ae18..00000000 --- a/GameData/Skins/default/Scripts/judgment.lua +++ /dev/null @@ -1,196 +0,0 @@ -Judgment = { - FadeoutTime = 0.5, - FadeoutDuration = 0.15, - Tilt = 7, -- in degrees - - ScaleTime = 0.1, - Scale = 0.21, - ScaleHit = 1.1, - ScaleOK = 0.9, - ScaleMiss = 0.7, - ScaleExtra = 0.2, - - -- if not nil it overrides default position - --Position = {}, - - Table = { - "judge-excellent.png", - "judge-perfect.png", - "judge-good.png", - "judge-bad.png", - "judge-miss.png", - }, - - TimingIndicator = "hiterror.png", - ShowTimingIndicator = true, - ScaleLerp = Ease.ElasticSquare(2.5) -} - - -function Judgment:Init() - print "Judgment Initializing." - self.Atlas = TextureAtlas:skin_new("VSRG/judgment.csv") - - - self.defaultX = self.Noteskin.GearWidth / 2 + self.Noteskin.GearStartX - self.defaultY = ScreenHeight * 0.4 - self.ScaleLerp = self.ScaleLerp or function (x) return x end - print ("Judgment Default Pos: ", self.defaultX, self.defaultY) - - self.Object = ScreenObject { - Layer = 30, - Centered = 1, - Texture = self.Atlas.File, - Alpha = 1 - } - - self.Transform = Transformation() - self.Object.ChainTransformation = self.Transform - - if self.Position then - self.Transform.X = self.Position.x or self.defaultX - self.Transform.Y = self.Position.y or self.defaultY - else - self.Transform.X = self.defaultX - self.Transform.Y = self.defaultY - end - - self.Transform.Width = self.Scale - self.Transform.Height = self.Scale - - - print ("Judgment Real pos/Texture: ", self.Transform.X, self.Transform.Y, self.Object.Texture) - - self.LastAlternation = 0 - self.Time = self.FadeoutTime + self.FadeoutDuration - - self.IndicatorObject = ScreenObject { - Texture = ("VSRG/" .. self.TimingIndicator), - Layer = 24, - Centered = 1, - Alpha = 0, - } - self.IndicatorObject:SetScale( 1 / self.Scale ) - - self.IndicatorObject.ChainTransformation = self.Transform - --self.Value = 0 -end - -librd.make_new(Judgment, Judgment.Init) - -function Judgment:GetComboLerp() - local AAAThreshold = 8.0 / 9.0 - return clerp(self.Player.Combo, -- cur - 0, self.Player.Scorekeeper.MaxNotes * AAAThreshold, -- start end - 0, 1) -- start val end val -end - -function Judgment:Run(Delta) - local ComboLerp = self:GetComboLerp() - - if Game.Active and self.Value then - local AlphaRatio - self.Time = min(self.Time + Delta, self.ScaleTime) - - local sval - if self.Value < 3 then - sval = self.ScaleHit - elseif self.Value < 5 then - sval = self.ScaleOK - else - sval = self.ScaleMiss - end - - local s = lerp(self.ScaleLerp(self.Time / self.ScaleTime), 0, 1, 1, sval) - - self.Transform.ScaleX = s - self.Transform.ScaleY = s - - if self.Time > self.FadeoutTime then - local Time = self.Time - self.FadeoutTime - if Time > 0 then - local Ratio = Time / self.FadeoutDuration - - if Ratio < 1 then - AlphaRatio = 1 - Ratio - else - AlphaRatio = 0 - end - - end - else - AlphaRatio = 1 - end - - local w = self.Object.Width - local h = self.Object.Height - - self.Object.Alpha = (AlphaRatio) - - if self.LastAlternation == 0 then - self.Transform.Rotation = (self.Tilt * ComboLerp) - else - self.Transform.Rotation = (-self.Tilt * ComboLerp) - end - - if self.Value ~= 1 and self.ShowTimingIndicator then -- not a "flawless" - self.IndicatorObject.Alpha = AlphaRatio - - if self.Early then -- early - self.IndicatorObject.X = - (w / 2 + 130) - else - -- late - self.IndicatorObject.X = w / 2 + 130 - end - else - self.IndicatorObject.Alpha = 0 - end - else - self.IndicatorObject.Alpha = 0 - self.Object.Alpha = 0 - end -end - -function Judgment:OnHit(JudgmentValue, Time, l, h, r, pn) - if pn ~= self.Player.Number then - return - end - self.Value = JudgmentValue - - if self.Value == 0 then - self.Object.Lighten = (1) - self.Object.LightenFactor = (1.5) - self.Value = 1 - else - self.Object.Lighten = 0 - self.Object.LightenFactor = 0 - end - - if self.LastAlternation == 0 then - self.LastAlternation = 1 - else - self.LastAlternation = 0 - end - - self.Atlas:SetObjectCrop(self.Object, self.Table[self.Value]) - self.Object.Height = self.Atlas.Sprites[self.Table[self.Value]].h - self.Object.Width = self.Atlas.Sprites[self.Table[self.Value]].w - - if JudgmentValue ~= 5 then - if JudgmentValue ~= -1 then - local CLerp = self:GetComboLerp() - self.Object:SetScale (self.ScaleHit + CLerp * self.ScaleExtra) - else - self.Object:SetScale (self.Scale) - end - else - self.Object:SetScale (self.ScaleMiss) - end - - self.Time = 0 - self.Early = Time < 0 -end - -function Judgment:OnMiss(t, l, h, pn) - self:OnHit(5, t, l, h, h, pn) -end diff --git a/GameData/Skins/default/SongSelect/up.png b/GameData/Skins/default/SongSelect/up.png deleted file mode 100644 index 61c30189..00000000 Binary files a/GameData/Skins/default/SongSelect/up.png and /dev/null differ diff --git a/GameData/Skins/default/SongSelect/up_h.png b/GameData/Skins/default/SongSelect/up_h.png deleted file mode 100644 index d0090d62..00000000 Binary files a/GameData/Skins/default/SongSelect/up_h.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/auto.png b/GameData/Skins/default/VSRG/auto.png deleted file mode 100644 index 74cd05a6..00000000 Binary files a/GameData/Skins/default/VSRG/auto.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/combo_bonus.png b/GameData/Skins/default/VSRG/combo_bonus.png deleted file mode 100644 index c1fb11d3..00000000 Binary files a/GameData/Skins/default/VSRG/combo_bonus.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/combosheet.csv b/GameData/Skins/default/VSRG/combosheet.csv deleted file mode 100644 index ebf81ff8..00000000 --- a/GameData/Skins/default/VSRG/combosheet.csv +++ /dev/null @@ -1,11 +0,0 @@ -VSRG/sheet.png -0.png,130,0,128,128 -1.png,260,0,128,127 -2.png,130,390,128,128 -3.png,130,260,128,128 -4.png,130,130,128,128 -5.png,260,130,128,128 -6.png,0,390,128,128 -7.png,0,260,128,128 -8.png,0,130,128,128 -9.png,0,0,128,128 diff --git a/GameData/Skins/default/VSRG/combosheet.png b/GameData/Skins/default/VSRG/combosheet.png deleted file mode 100644 index 7cc94ae0..00000000 Binary files a/GameData/Skins/default/VSRG/combosheet.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/explsheet.csv b/GameData/Skins/default/VSRG/explsheet.csv deleted file mode 100644 index eda02d45..00000000 --- a/GameData/Skins/default/VSRG/explsheet.csv +++ /dev/null @@ -1,11 +0,0 @@ -explsheet.png -lightingN-0.png,0,678,47,46 -lightingN-1.png,500,234,202,210 -lightingN-2.png,466,496,214,210 -lightingN-3.png,238,496,226,222 -lightingN-4.png,252,0,240,236 -lightingN-5.png,0,248,248,244 -lightingN-6.png,0,0,250,246 -lightingN-7.png,250,248,248,246 -lightingN-8.png,494,0,230,232 -lightingN-9.png,0,496,236,180 diff --git a/GameData/Skins/default/VSRG/explsheet.png b/GameData/Skins/default/VSRG/explsheet.png deleted file mode 100644 index 8e71b916..00000000 Binary files a/GameData/Skins/default/VSRG/explsheet.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/fullcombo.png b/GameData/Skins/default/VSRG/fullcombo.png deleted file mode 100644 index 3888573d..00000000 Binary files a/GameData/Skins/default/VSRG/fullcombo.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/hiterror.png b/GameData/Skins/default/VSRG/hiterror.png deleted file mode 100644 index f3c13555..00000000 Binary files a/GameData/Skins/default/VSRG/hiterror.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/hitlightning.png b/GameData/Skins/default/VSRG/hitlightning.png deleted file mode 100644 index 22dbcc04..00000000 Binary files a/GameData/Skins/default/VSRG/hitlightning.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/holdsheet.csv b/GameData/Skins/default/VSRG/holdsheet.csv deleted file mode 100644 index 8272f8de..00000000 --- a/GameData/Skins/default/VSRG/holdsheet.csv +++ /dev/null @@ -1,41 +0,0 @@ -holdsheet.png -lightingL-0.png,0,1260,250,250 -lightingL-1.png,0,1008,250,250 -lightingL-10.png,1260,0,250,250 -lightingL-11.png,1008,1512,250,250 -lightingL-12.png,1008,1260,250,250 -lightingL-13.png,1008,1008,250,250 -lightingL-14.png,1008,756,250,250 -lightingL-15.png,1008,504,250,250 -lightingL-16.png,1008,252,250,250 -lightingL-17.png,1008,0,250,250 -lightingL-18.png,756,1512,250,250 -lightingL-19.png,756,1260,250,250 -lightingL-2.png,0,756,250,250 -lightingL-20.png,756,1008,250,250 -lightingL-21.png,756,756,250,250 -lightingL-22.png,756,504,250,250 -lightingL-23.png,756,252,250,250 -lightingL-24.png,756,0,250,250 -lightingL-25.png,504,1512,250,250 -lightingL-26.png,1260,1008,250,250 -lightingL-27.png,504,1008,250,250 -lightingL-28.png,504,756,250,250 -lightingL-29.png,504,504,250,250 -lightingL-3.png,0,504,250,250 -lightingL-30.png,504,252,250,250 -lightingL-31.png,504,0,250,250 -lightingL-32.png,252,1512,250,250 -lightingL-33.png,252,1260,250,250 -lightingL-34.png,252,1008,250,250 -lightingL-35.png,252,756,250,250 -lightingL-36.png,252,504,250,250 -lightingL-37.png,252,252,250,250 -lightingL-38.png,252,0,250,250 -lightingL-39.png,0,1512,250,250 -lightingL-4.png,0,252,250,250 -lightingL-5.png,0,0,250,250 -lightingL-6.png,504,1260,250,250 -lightingL-7.png,1260,756,250,250 -lightingL-8.png,1260,504,250,250 -lightingL-9.png,1260,252,250,250 diff --git a/GameData/Skins/default/VSRG/holdsheet.png b/GameData/Skins/default/VSRG/holdsheet.png deleted file mode 100644 index 91aa8911..00000000 Binary files a/GameData/Skins/default/VSRG/holdsheet.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/jam_bar.png b/GameData/Skins/default/VSRG/jam_bar.png deleted file mode 100644 index b5627f70..00000000 Binary files a/GameData/Skins/default/VSRG/jam_bar.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/judgeline.png b/GameData/Skins/default/VSRG/judgeline.png deleted file mode 100644 index f4f453a1..00000000 Binary files a/GameData/Skins/default/VSRG/judgeline.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/judgment.csv b/GameData/Skins/default/VSRG/judgment.csv deleted file mode 100644 index 8618bdb9..00000000 --- a/GameData/Skins/default/VSRG/judgment.csv +++ /dev/null @@ -1,6 +0,0 @@ -VSRG/judgment.png -judge-bad.png,0,685,250,161 -judge-excellent.png,0,0,972,161 -judge-good.png,0,519,447,154 -judge-miss.png,0,346,635,161 -judge-perfect.png,0,173,746,161 diff --git a/GameData/Skins/default/VSRG/judgment.png b/GameData/Skins/default/VSRG/judgment.png deleted file mode 100644 index 728ddcd9..00000000 Binary files a/GameData/Skins/default/VSRG/judgment.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/keys.csv b/GameData/Skins/default/VSRG/keys.csv deleted file mode 100644 index f2a36c13..00000000 --- a/GameData/Skins/default/VSRG/keys.csv +++ /dev/null @@ -1,11 +0,0 @@ -VSRG/keys.png -key1.png,132,130,56,120 -key1d.png,264,130,56,120 -key2.png,66,130,56,120 -key2d.png,66,0,56,120 -key3.png,0,130,56,120 -key3d.png,0,0,56,120 -key4.png,132,0,56,120 -key4d.png,264,0,56,120 -key5.png,198,130,56,120 -key5d.png,198,0,56,120 diff --git a/GameData/Skins/default/VSRG/keys.png b/GameData/Skins/default/VSRG/keys.png deleted file mode 100644 index b6ccb76c..00000000 Binary files a/GameData/Skins/default/VSRG/keys.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/miss_highlight.png b/GameData/Skins/default/VSRG/miss_highlight.png deleted file mode 100644 index 7aca8bd0..00000000 Binary files a/GameData/Skins/default/VSRG/miss_highlight.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note1.png b/GameData/Skins/default/VSRG/note1.png deleted file mode 100644 index 0b1450e3..00000000 Binary files a/GameData/Skins/default/VSRG/note1.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note1L.png b/GameData/Skins/default/VSRG/note1L.png deleted file mode 100644 index 903aa45a..00000000 Binary files a/GameData/Skins/default/VSRG/note1L.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note2.png b/GameData/Skins/default/VSRG/note2.png deleted file mode 100644 index b127e8f1..00000000 Binary files a/GameData/Skins/default/VSRG/note2.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note2L.png b/GameData/Skins/default/VSRG/note2L.png deleted file mode 100644 index 9618c0b6..00000000 Binary files a/GameData/Skins/default/VSRG/note2L.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note3.png b/GameData/Skins/default/VSRG/note3.png deleted file mode 100644 index 241fb6a8..00000000 Binary files a/GameData/Skins/default/VSRG/note3.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note3L.png b/GameData/Skins/default/VSRG/note3L.png deleted file mode 100644 index d429ff64..00000000 Binary files a/GameData/Skins/default/VSRG/note3L.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note4.png b/GameData/Skins/default/VSRG/note4.png deleted file mode 100644 index ba5f9f42..00000000 Binary files a/GameData/Skins/default/VSRG/note4.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note4L.png b/GameData/Skins/default/VSRG/note4L.png deleted file mode 100644 index f235200e..00000000 Binary files a/GameData/Skins/default/VSRG/note4L.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note5.png b/GameData/Skins/default/VSRG/note5.png deleted file mode 100644 index ced8bb26..00000000 Binary files a/GameData/Skins/default/VSRG/note5.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/note5L.png b/GameData/Skins/default/VSRG/note5L.png deleted file mode 100644 index 459c40f6..00000000 Binary files a/GameData/Skins/default/VSRG/note5L.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/progress_tick.png b/GameData/Skins/default/VSRG/progress_tick.png deleted file mode 100644 index 38f18180..00000000 Binary files a/GameData/Skins/default/VSRG/progress_tick.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/pulse.png b/GameData/Skins/default/VSRG/pulse.png deleted file mode 100644 index efbdb733..00000000 Binary files a/GameData/Skins/default/VSRG/pulse.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/pulse_ver.png b/GameData/Skins/default/VSRG/pulse_ver.png deleted file mode 100644 index d609d45e..00000000 Binary files a/GameData/Skins/default/VSRG/pulse_ver.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/sheet.png b/GameData/Skins/default/VSRG/sheet.png deleted file mode 100644 index 11c3a3b0..00000000 Binary files a/GameData/Skins/default/VSRG/sheet.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/stage-left.png b/GameData/Skins/default/VSRG/stage-left.png deleted file mode 100644 index 4a4bdca7..00000000 Binary files a/GameData/Skins/default/VSRG/stage-left.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/stage-lifeb-s.png b/GameData/Skins/default/VSRG/stage-lifeb-s.png deleted file mode 100644 index 0dce6a58..00000000 Binary files a/GameData/Skins/default/VSRG/stage-lifeb-s.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/stage-lifeb.png b/GameData/Skins/default/VSRG/stage-lifeb.png deleted file mode 100644 index 152d0245..00000000 Binary files a/GameData/Skins/default/VSRG/stage-lifeb.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/stage-right.png b/GameData/Skins/default/VSRG/stage-right.png deleted file mode 100644 index 4a4bdca7..00000000 Binary files a/GameData/Skins/default/VSRG/stage-right.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/stageclear.png b/GameData/Skins/default/VSRG/stageclear.png deleted file mode 100644 index 32f47647..00000000 Binary files a/GameData/Skins/default/VSRG/stageclear.png and /dev/null differ diff --git a/GameData/Skins/default/VSRG/stagefailed.png b/GameData/Skins/default/VSRG/stagefailed.png deleted file mode 100644 index 9aac4c83..00000000 Binary files a/GameData/Skins/default/VSRG/stagefailed.png and /dev/null differ diff --git a/GameData/Skins/default/_font.tga b/GameData/Skins/default/_font.tga deleted file mode 100644 index 79b7a404..00000000 Binary files a/GameData/Skins/default/_font.tga and /dev/null differ diff --git a/GameData/Skins/default/barline.png b/GameData/Skins/default/barline.png deleted file mode 100644 index 98f8e2a9..00000000 Binary files a/GameData/Skins/default/barline.png and /dev/null differ diff --git a/GameData/Skins/default/barline_marker.png b/GameData/Skins/default/barline_marker.png deleted file mode 100644 index 68e23304..00000000 Binary files a/GameData/Skins/default/barline_marker.png and /dev/null differ diff --git a/GameData/Skins/default/click.wav b/GameData/Skins/default/click.wav deleted file mode 100644 index b0f8b280..00000000 Binary files a/GameData/Skins/default/click.wav and /dev/null differ diff --git a/GameData/Skins/default/cross.png b/GameData/Skins/default/cross.png deleted file mode 100644 index dae31d34..00000000 Binary files a/GameData/Skins/default/cross.png and /dev/null differ diff --git a/GameData/Skins/default/cursor.png b/GameData/Skins/default/cursor.png deleted file mode 100644 index d86bfb6d..00000000 Binary files a/GameData/Skins/default/cursor.png and /dev/null differ diff --git a/GameData/Skins/default/cursor.rml b/GameData/Skins/default/cursor.rml deleted file mode 100644 index a78d2325..00000000 --- a/GameData/Skins/default/cursor.rml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - diff --git a/GameData/Skins/default/cursor_rdh.png b/GameData/Skins/default/cursor_rdh.png deleted file mode 100644 index bf76b929..00000000 Binary files a/GameData/Skins/default/cursor_rdh.png and /dev/null differ diff --git a/GameData/Skins/default/custom_defs.lua b/GameData/Skins/default/custom_defs.lua deleted file mode 100644 index 4498967a..00000000 --- a/GameData/Skins/default/custom_defs.lua +++ /dev/null @@ -1 +0,0 @@ --- feed me \ No newline at end of file diff --git a/GameData/Skins/default/drop.wav b/GameData/Skins/default/drop.wav deleted file mode 100644 index af074655..00000000 Binary files a/GameData/Skins/default/drop.wav and /dev/null differ diff --git a/GameData/Skins/default/font-combo.tga b/GameData/Skins/default/font-combo.tga deleted file mode 100644 index 7674eed7..00000000 Binary files a/GameData/Skins/default/font-combo.tga and /dev/null differ diff --git a/GameData/Skins/default/font.tga b/GameData/Skins/default/font.tga deleted file mode 100644 index 37903323..00000000 Binary files a/GameData/Skins/default/font.tga and /dev/null differ diff --git a/GameData/Skins/default/font.ttf b/GameData/Skins/default/font.ttf deleted file mode 100644 index 9693fb25..00000000 Binary files a/GameData/Skins/default/font.ttf and /dev/null differ diff --git a/GameData/Skins/default/font_screenevaluation.tga b/GameData/Skins/default/font_screenevaluation.tga deleted file mode 100644 index a7b10986..00000000 Binary files a/GameData/Skins/default/font_screenevaluation.tga and /dev/null differ diff --git a/GameData/Skins/default/healthbar.png b/GameData/Skins/default/healthbar.png deleted file mode 100644 index 91d36a0f..00000000 Binary files a/GameData/Skins/default/healthbar.png and /dev/null differ diff --git a/GameData/Skins/default/hit.ogg b/GameData/Skins/default/hit.ogg deleted file mode 100644 index d065f579..00000000 Binary files a/GameData/Skins/default/hit.ogg and /dev/null differ diff --git a/GameData/Skins/default/hitcircle.png b/GameData/Skins/default/hitcircle.png deleted file mode 100644 index fcaa5425..00000000 Binary files a/GameData/Skins/default/hitcircle.png and /dev/null differ diff --git a/GameData/Skins/default/holdfinish.ogg b/GameData/Skins/default/holdfinish.ogg deleted file mode 100644 index d44f99dd..00000000 Binary files a/GameData/Skins/default/holdfinish.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop1.ogg b/GameData/Skins/default/loop1.ogg deleted file mode 100644 index 23807bea..00000000 Binary files a/GameData/Skins/default/loop1.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop2.ogg b/GameData/Skins/default/loop2.ogg deleted file mode 100644 index b70a6e74..00000000 Binary files a/GameData/Skins/default/loop2.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop3.ogg b/GameData/Skins/default/loop3.ogg deleted file mode 100644 index d54074ef..00000000 Binary files a/GameData/Skins/default/loop3.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop4.ogg b/GameData/Skins/default/loop4.ogg deleted file mode 100644 index 703469a1..00000000 Binary files a/GameData/Skins/default/loop4.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop5.ogg b/GameData/Skins/default/loop5.ogg deleted file mode 100644 index 58f7bc40..00000000 Binary files a/GameData/Skins/default/loop5.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop6.ogg b/GameData/Skins/default/loop6.ogg deleted file mode 100644 index c5efe3ee..00000000 Binary files a/GameData/Skins/default/loop6.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop7.ogg b/GameData/Skins/default/loop7.ogg deleted file mode 100644 index a8f1a03f..00000000 Binary files a/GameData/Skins/default/loop7.ogg and /dev/null differ diff --git a/GameData/Skins/default/loop8.ogg b/GameData/Skins/default/loop8.ogg deleted file mode 100644 index 19e6c0a3..00000000 Binary files a/GameData/Skins/default/loop8.ogg and /dev/null differ diff --git a/GameData/Skins/default/mainmenu.lua b/GameData/Skins/default/mainmenu.lua deleted file mode 100644 index 013935f6..00000000 --- a/GameData/Skins/default/mainmenu.lua +++ /dev/null @@ -1,93 +0,0 @@ -skin_require "Global/FadeInScreen" -game_require "AnimationFunctions" - -Preload = { - "MainMenu/play.png", - "MainMenu/quit.png" -} - -IntroDuration = 0.5 -ExitDuration = 1.5 - -function UpdateIntro(p, delta) - local S = elastic(p) - - -- At 1/3rd of the screen, please. - targBadge.Y = ScreenHeight * 3/7 * (S) - targBadge.Height - targLogo.Y = targBadge.Y - Update(delta) - BGAOut(p*p) -end - -function OnRunningBegin() - ScreenFade.Out() -end - -function OnRestore() - ScreenFade.Out() -end - -function OnIntroBegin() - Engine:SetUILayer(31) -end - -function OnExitBegin() -end - -function UpdateExit(p, delta) - local ease = p*p - UpdateIntro(1-p, delta) - FadeInA1(ease) - BGAIn(ease) -end - -function KeyEvent(k, c, mouse) - if c == 1 then - Global:StartScreen("songselect") - end -end - -function Init() - elastic = Ease.ElasticSquare(1.5) - ScreenFade:Init() - - targLogo = Engine:CreateObject() - targLogo.Texture = "MainMenu/FRONTs.png" - targLogo.X = ScreenWidth / 2 - targLogo.Y = ScreenHeight / 4 - targLogo.Centered = 1 - targLogo.Alpha = 1 - targLogo.Layer = 31 - - targBadge = Engine:CreateObject() - targBadge.Texture = "MainMenu/BACKs.png" - targBadge.X = ScreenWidth / 2 - targBadge.Y = ScreenHeight / 4 - targBadge.Centered = 1 - targBadge.Layer = 31 - - font = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 24) - - s = "press any key..." - title = StringObject2D() - title.Font = font - title.X = ScreenWidth / 2 - font:GetLength(s) / 2 - title.Y = ScreenHeight * 3 / 4 - title.Text = s - title.Z = 31 - Engine:AddTarget(title) - - -- Rocket UI not initialized yet... -end - -function Cleanup() -end - -badgeRotSpeed = 1080 - -function Update(Delta) - - badgeRotSpeed = math.max(badgeRotSpeed - Delta * 240, 120) - targBadge.Rotation = targBadge.Rotation - badgeRotSpeed * Delta - BackgroundAnimation:Update(Delta) -end diff --git a/GameData/Skins/default/miss.wav b/GameData/Skins/default/miss.wav deleted file mode 100644 index 35499827..00000000 Binary files a/GameData/Skins/default/miss.wav and /dev/null differ diff --git a/GameData/Skins/default/noteskin.lua b/GameData/Skins/default/noteskin.lua deleted file mode 100644 index 791f99b2..00000000 --- a/GameData/Skins/default/noteskin.lua +++ /dev/null @@ -1,106 +0,0 @@ -game_require "noteskin_defs" - --- All notes have their origin centered. - -normalNotes = {} -holdBodies = {} - -local TT = Notes.Channels == 6 or Notes.Channels == 8 - -if Player.Turntable and TT then - skin = NoteskinSpecial -else - skin = Noteskin -end -function setNoteStuff(note, i) - note.Width = skin[Notes.Channels]['Key' .. i .. 'Width'] - note.X = skin[Notes.Channels]['Key' .. i .. 'X'] - note.Height = skin[Notes.Channels].NoteHeight - note.Layer = 14 - note.Lighten = true - note.LightenFactor = 0 -end - -function Init() - - for i=1,Notes.Channels do - normalNotes[i] = Object2D() - local note = normalNotes[i] - note.Texture = skin[Notes.Channels]['Key' .. i .. 'Image'] - setNoteStuff(note, i) - - holdBodies[i] = Object2D() - note = holdBodies[i] - note.Texture = skin[Notes.Channels]['Key' .. i .. 'HoldImage'] - setNoteStuff(note, i) - end -end - -function Update(delta, beat) - local fraction = 1 - (beat - math.floor(beat)) - for i=1, Notes.Channels do - local note = normalNotes[i] - note.LightenFactor = fraction - end -end - -function drawNormalInternal(lane, loc, frac, active_level) - local note = normalNotes[lane + 1] - note.Y = loc - - if active_level ~= 3 then - Notes:Render(note) - end -end - --- 1 is enabled. 2 is being pressed. 0 is failed. 3 is succesful hit. -function drawHoldBodyInternal(lane, loc, size, active_level) - local note = holdBodies[lane + 1] - note.Y = loc - note.Height = size - - if active_level == 0 then - note.Red = 0.5 - note.Blue = 0.5 - note.Green = 0.5 - else - note.Red = 1 - note.Blue = 1 - note.Green = 1 - end - - if active_level ~= 3 then - Notes:Render(note) - end -end - -function drawMineInternal(lane, loc, frac) - -- stub while mines are accounted in the scoring system. -end - --- From now on, only engine variables are being set. --- Barline ---game_require "debug" -Notes.BarlineEnabled = 1 -Notes.BarlineOffset = skin[Notes.Channels].NoteHeight / 2 -Notes.BarlineStartX = GearStartX - -Notes.BarlineWidth = skin[Notes.Channels].BarlineWidth - -local jy = skin[Notes.Channels].GearHeight + skin[Notes.Channels].NoteHeight / 2 -Notes.JudgmentY = jy -Notes.DecreaseHoldSizeWhenBeingHit = true -Notes.DanglingHeads = true - --- How many extra units do you require so that the whole bounding box is accounted --- when determining whether to show this note or not. -Notes.NoteScreenSize = skin[Notes.Channels].NoteHeight / 2 - -DrawNormal = drawNormalInternal -DrawFake = drawNormalInternal -DrawLift = drawNormalInternal -DrawMine = drawMineInternal - -DrawHoldHead = drawNormalInternal -DrawHoldTail = drawNormalInternal -DrawHoldBody = drawHoldBodyInternal diff --git a/GameData/Skins/default/override.lua b/GameData/Skins/default/override.lua deleted file mode 100644 index e69de29b..00000000 diff --git a/GameData/Skins/default/raindrop.css b/GameData/Skins/default/raindrop.css deleted file mode 100644 index bfc623ee..00000000 --- a/GameData/Skins/default/raindrop.css +++ /dev/null @@ -1,288 +0,0 @@ -body { - font-family: default; - font-size: 24; - text-align: right; -} - -.widebox -{ - position: absolute; - top: 155px; - display: block; - width: 100%; - overflow-y: scroll; - height: 600%; - text-align: left; - - background: #101010; - border-top-width: 3px; - border-bottom-width: 3px; - border-top-color: white; - border-bottom-color: white; -} - -scrollbarvertical -{ - width: 24px; -} - -scrollbarvertical slidertrack -{ - background: #A0A0A0; -} - -scrollbarvertical sliderbar -{ - background: #303030; - width: 20px; - margin-left: 2px; -} - -scrollbarvertical sliderarrowdec, sliderarrowinc -{ - background: #080808; - height: 35px; -} - -.opt h1 -{ - margin-left: 15%; - display: inline; - text-align: left; - left: 5%; - width: 100%; - font-size: 48; - darken-font-effect: shadow; - darken-offset: 1px 1px; - darken-color: grey; -} - -.opt form -{ - width: 70%; - display: inline-block; - margin-left: 15%; - padding-left: 2%; - background: #0A0A0A; -} - -.opt form input:after -{ - content: "hello"; -} - -#text -{ - font-family: default; -} - -.dbox h1 -{ - display: block; - position: relative; - width: 100%; - text-align: center; - background: rgba(0%,0%,0%,90%); - font-size: 28; - darken-font-effect: shadow; - darken-offset: 1px 1px; - darken-color: grey; - border-color: white; - border-width: 1px 0px; -} - -input { - display: block; -} - -input.checkbox -{ - display: inline; - icon-decorator: image; - icon-image: Global/checkbox.png; -} - -input.checkbox:hover -{ - icon-image: Global/checkbox_h.png; -} - -input.checkbox:checked -{ - icon-image: Global/checkbox_s.png; -} - -input.checkbox:checked:hover -{ - icon-image: Global/checkbox_sh.png; -} - -input.text { - background: black; - display: inline-block; - width: 100%; -} - -.opt input.text { - display: inline-block; - width: 40%; - height: 6%; - font-size: 20px; - margin-bottom: -15px; -} - -input.text:hover { - background: grey; -} - -input.range slidertrack -{ - height: 10px; - background: #303030; - -} - -.row { - display: inline-block; - width: 100%; -} - -.rowlt { - text-align: right; - margin-right: 15px; - display: inline-block; - width: 50%; -} - -.rowrt { - position: relative; - text-align: left; - margin-left: 15px; - display: inline-block; - top: -10px; -} - - -.boxes { - padding-top: 2px; - padding-bottom: 2px; - font-size: 22px; - display: block; -} - -.fl { - display: inline-block; - background: rgba(0,0,0,80%); - width: 50%; - text-align: left; -} - -.fr { - display: inline-block; - width: 50%; - background: rgba(0,0,0,80%); -} - -select, dataselect { - color: white; - font-size: 18; - width: 180; - height: 25; - background: #101010; - text-align: left; -} - -select:hover { - background: grey; -} - -select selectarrow, -dataselect selectarrow { - width: 15; - height: 25; - color: white; - background: rgb(8,8,8); -} - -select selectarrow:hover, dataselect selectarrow:hover { - color: grey; -} - -select selectbox, -dataselect selectbox { - width: auto; - height: auto; - background: black; - color: white; -} - -select option:hover { - background: grey; -} - -.dbox { - display: block; - position: absolute; - background: rgba(0%,0%,0%,60%); - left: 70%; - height: 680; - width: 30%; - border-left-width: 3px; - border-left-color: white; -} - -#hid { - text-align: center; - width: 100%; -} - -.lt { - display: inline-block; - width: 50%; - text-align: left; - background: rgba(0,0,0,70%); - height: 30px; - font-size: 20px; -} - -.rt { - display: inline-block; - width: 50%; - height: 30px; -} - -.rt input.text { - display:inline; - width:200px; - height: 30px; -} - -.selbar { - display: block; - height: 12%; - width: 100%; - background: rgba(0,0,0,90%); - text-align: left; - border-bottom-width: 2px; - border-bottom-color: white; -} - -.sellt { - padding: 2px 5px; - font-size: 40px; - width: 65%; - top: 6px; - position: relative; - display: inline-block; -} - -.selrt { - font-size: 18px; - width: 30%; - display: inline-block; -} - -.selrt h1 { - display: block; - height: 35%; -} diff --git a/GameData/Skins/default/ready.png b/GameData/Skins/default/ready.png deleted file mode 100644 index 25bbd482..00000000 Binary files a/GameData/Skins/default/ready.png and /dev/null differ diff --git a/GameData/Skins/default/save.ogg b/GameData/Skins/default/save.ogg deleted file mode 100644 index 5466f86f..00000000 Binary files a/GameData/Skins/default/save.ogg and /dev/null differ diff --git a/GameData/Skins/default/screen_keytest.lua b/GameData/Skins/default/screen_keytest.lua deleted file mode 100644 index 172d20b0..00000000 --- a/GameData/Skins/default/screen_keytest.lua +++ /dev/null @@ -1,74 +0,0 @@ --- Structure constants -IntroDuration = 0 -ExitDuration = 0 - -Keys = {} - -function Init() - font = Fonts.BitmapFont() - Fonts.LoadBitmapFont(font, "font.tga", 8, 16, 6, 15, 0) - display = StringObject2D() - display.X = 0 - display.Y = 0 - display.Font = font - display.ScaleX = 2 - display.ScaleY = 2 - Engine:AddTarget(display) -end - ---[[ ---key: scan code ---code: 1 is press, 2 is release (0 was repeat) ---is_mouse_input: 0 if not mouse input, 1 if it is. ---]] -function KeyEvent(key, code, is_mouse_input) - if code == 1 then - Keys[key] = code - else - Keys[key] = nil - end -end - --- Intro -function OnIntroBegin() - -end - --- Fraction = Time / IntroDuration -function UpdateIntro(Fraction, Delta) - -end - -function OnIntroEnd() - -end - --- Main loop -function Update(Delta) - local text = "key input test. input code appended below.\n" - for key, value in pairs(Keys) do - if value == 1 then - text = text .. "keycode: " .. key .. "\n" - end - end - - display.Text = text -end - --- Exit/Outro -function OnExitBegin() - -end - -function UpdateExit(Fraction, Delta) - -end - -function OnExitEnd() - -end - --- Resource cleanup (i.e. custom allocated resources...) -function Cleanup() - -end diff --git a/GameData/Skins/default/screencustom_template.lua b/GameData/Skins/default/screencustom_template.lua deleted file mode 100644 index d272e3c9..00000000 --- a/GameData/Skins/default/screencustom_template.lua +++ /dev/null @@ -1,54 +0,0 @@ - --- Structure constants -IntroDuration = 0 -ExitDuration = 0 - -function Init() - -end - ---[[ ---key: scan code ---code: 1 is press, 2 is release (0 was repeat) ---is_mouse_input: 0 if not mouse input, 1 if it is. ---]] -function KeyEvent(key, code, is_mouse_input) - -end - --- Intro -function OnIntroBegin() - -end - --- Fraction = Time / IntroDuration -function UpdateIntro(Fraction, Delta) - -end - -function OnIntroEnd() - -end - --- Main loop -function Update(Delta) - -end - --- Exit/Outro -function OnExitBegin() - -end - -function UpdateExit(Fraction, Delta) - -end - -function OnExitEnd() - -end - --- Resource cleanup (i.e. custom allocated resources...) -function Cleanup() - -end diff --git a/GameData/Skins/default/screenevaluation7k.lua b/GameData/Skins/default/screenevaluation7k.lua deleted file mode 100644 index 898e9d0f..00000000 --- a/GameData/Skins/default/screenevaluation7k.lua +++ /dev/null @@ -1,183 +0,0 @@ -game_require "TextureAtlas" -game_require "Histogram" -skin_require "Global/Background" -skin_require "Global/FadeInScreen" -skin_require "Scripts/ScoreDisplay" - -function SetupFonts() - EvalFont = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 30); -end - -function GetRankImage(ScoreKeeper) - local scorerank = ScoreKeeper.BMRank - if scorerank == PMT_AAA then - return "AAA" - elseif scorerank == PMT_AA then - return "AA" - elseif scorerank == PMT_A then - return "A" - elseif scorerank == PMT_B then - return "B" - elseif scorerank == PMT_C then - return "C" - elseif scorerank == PMT_D then - return "D" - else - return "F" - end - -end - -function SetupRank(player) - RankPic = Engine:CreateObject() - RankPic.Texture = "Evaluation/score" .. GetRankImage(player.Scorekeeper) .. ".png" - RankPic.Centered = 1 - RankPic.X = ScreenWidth / 2 - RankPic.Width / 2 - RankPic.Y = ScreenHeight / 2 - - -- resize to something acceptable - if RankPic.X < RankPic.Width / 2 then - local Rat = (ScreenWidth / 2) / (RankPic.Width) - RankPic:SetScale(Rat) - RankPic.X = ScreenWidth / 2 - RankPic.Width / 2 * Rat - end - - local str = "" - if player.HasFailed then - str = " (failed)" - end - - RankStr = StringObject2D() - RankStr.Text = "rank" .. str - RankStr.X = RankPic.X - EvalFont:GetLength("rank") / 2 - RankStr.Y = ScreenHeight / 2 + RankPic.Height / 2 + 10 - RankStr.Font = EvalFont - - Engine:AddTarget(RankStr) -end - - -function SetupJudgmentsDisplay(player) - local ScoreKeeper = player.Scorekeeper - JudgeStr = StringObject2D() - JudgeStr.Font = EvalFont - - w0 = ScoreKeeper:GetJudgmentCount(SKJ_W0) - w1 = ScoreKeeper:GetJudgmentCount(SKJ_W1) - w2 = ScoreKeeper:GetJudgmentCount(SKJ_W2) - w3 = ScoreKeeper:GetJudgmentCount(SKJ_W3) - w4 = ScoreKeeper:GetJudgmentCount(SKJ_W4) - w5 = ScoreKeeper:GetJudgmentCount(SKJ_MISS) - Score = player.Score - - fmtext = "" - if ScoreKeeper.UsesW0 == false then - if ScoreKeeper.UsesO2 == false then - fmtext = fmtext .. string.format("Flawless: %04d\nSweet: %04d\nNice: %04d\nWeak: %04d\nMiss: %04d", w1, w2, w3, w4, w5) - else - local p = ScoreKeeper.Pills - fmtext = fmtext .. string.format("Flawless: %04d\nSweet: %04d\nNice: %04d\nMiss: %04d\nPills: %d", w1, w2, w3, w5, p) - end - else - fmtext = fmtext .. string.format("Flawless*: %04d\nFlawless: %04d\nSweet: %04d\nNice: %04d\nOK: %04d\nMiss: %04d", w0, w1, w2, w3, w4, w5) - end - - fmtext = fmtext .. string.format("\nMax Combo: %d", ScoreKeeper:GetScore(ST_MAX_COMBO)) - fmtext = fmtext .. string.format("\nNotes hit: %d%%", ScoreKeeper:GetPercentScore(PST_NH)) - fmtext = fmtext .. string.format("\nAccuracy: %d%%", ScoreKeeper:GetPercentScore(PST_ACC)) - fmtext = fmtext .. string.format("\nAverage hit (ms): %.2f" , ScoreKeeper.AvgHit) - fmtext = fmtext .. "\nraindrop rank: " - - if ScoreKeeper.Rank > 0 then - fmtext = fmtext .. "+" .. ScoreKeeper.Rank - else - fmtext = fmtext .. ScoreKeeper.Rank - end - - JudgeStr.Text = fmtext - - ScoreDisplay.X = ScoreDisplay.W / 2 + ScreenWidth / 2 - ScoreDisplay.Y = RankStr.Y + 30 - - JudgeStr.X = ScoreDisplay.X - JudgeStr.Y = ScreenHeight / 2 - RankPic.Height / 2 - - Engine:AddTarget(JudgeStr) -end - -function SetSongTitle(diff) - Filter = Engine:CreateObject() - Filter.Texture = "Global/filter.png" - Filter.X = 0 - Filter.Y = ScreenHeight - 30 - Filter.Width = ScreenWidth - Filter.Height = 40 - - TitleText = StringObject2D() - - sng = toSong7K(Global:GetSelectedSong()) - if diff.Author ~= "" then - difftxt = string.format("%s by %s", diff.Name, diff.Author) - else - difftxt = string.format("%s", diff.Name) - end - - local Text = string.format ("%s by %s (Chart: %s)", Global:GetSelectedSong().Title, Global:GetSelectedSong().Author, difftxt) - TitleText.Text = Text - TitleText.Font = EvalFont - - TitleText.Y = ScreenHeight - 40 - TitleText.X = ScreenWidth / 2 - EvalFont:GetLength(Text) / 2 - - Engine:AddTarget(TitleText) -end - -function SetupHistogram(p) - histogram = Histogram:new(p) - histogram:SetPosition(ScreenWidth / 2 - 255 / 2, 20) - histogram:SetColor(30 / 255, 50 / 255, 200 / 255) - hist_bg = histogram:SetBackground("Global/white.png") - hist_bg.Red = 0.2 - hist_bg.Green = 0.2 - hist_bg.Blue = 0.2 - - hStr = StringObject2D() - hStr.Font = EvalFont - hStr.X = histogram.X - hStr.Y = histogram.Y + histogram.Height - hStr.Text = "histogram" - Engine:AddTarget(hStr) -end - -function Init() - local p = Game:GetPlayer(0) - BackgroundAnimation:Init() - SetupFonts() - SetupRank(p) - SetupJudgmentsDisplay(p) - - sd = ScoreDisplay:new({Player = p}) - sd.Score = Score - - scoreStr = StringObject2D() - scoreStr.Font = EvalFont - scoreStr.X = sd.X - scoreStr.Y = sd.Y + sd.H - scoreStr.Text = "score" - - Engine:AddTarget(scoreStr) - - SetSongTitle(p.Difficulty) - SetupHistogram(p) - ScreenFade.Init() - ScreenFade.Out() -end - -function Cleanup() - -end - -function Update(Delta) - sd:Run(Delta) - BackgroundAnimation:Update(Delta) -end diff --git a/GameData/Skins/default/screenevaluationloop.ogg b/GameData/Skins/default/screenevaluationloop.ogg deleted file mode 100644 index 6568613d..00000000 Binary files a/GameData/Skins/default/screenevaluationloop.ogg and /dev/null differ diff --git a/GameData/Skins/default/screengameplay7k.lua b/GameData/Skins/default/screengameplay7k.lua deleted file mode 100644 index 3cc42cdf..00000000 --- a/GameData/Skins/default/screengameplay7k.lua +++ /dev/null @@ -1,250 +0,0 @@ -game_require "TextureAtlas" -game_require "utils" -game_require "AnimationFunctions" -game_require "Histogram" -skin_require "Global/FadeInScreen" - --- Set up constants for everyone - -game_require "noteskin_defs" -skin_require "Scripts/Explosions" -skin_require "Scripts/ComboDisplay" -skin_require "Scripts/KeyLightning" -skin_require "Scripts/FixedObjects" -skin_require "Scripts/ScoreDisplay" -skin_require "Scripts/AutoplayAnimation" -skin_require "Scripts/Lifebar" -skin_require "Scripts/StageAnimation" -skin_require "Scripts/AnimatedObjects" -skin_require "Scripts/Keys" -skin_require "Scripts/Judgment" -skin_require "Scripts/TextDisplay" -skin_require "override" - --- All of these will be loaded in the loading screen instead of --- in the main thread. -Preload = { - "VSRG/judgeline.png", - "VSRG/stage-left.png", - "VSRG/stage-right.png", - "VSRG/pulse_ver.png", - "VSRG/stagefailed.png", - "VSRG/progress_tick.png", - "VSRG/stage-lifeb.png", - "VSRG/stage-lifeb-s.png", - "VSRG/combosheet.png", - "VSRG/explsheet.png", - "VSRG/holdsheet.png", - "VSRG/jam_bar.png", - "VSRG/judgment.png", - "VSRG/auto.png", - "VSRG/hitlightning.png", - "VSRG/hiterror.png", - "VSRG/miss_highlight.png", - "VSRG/keys.png", - "VSRG/fullcombo.png", - "VSRG/stageclear.png" -} - --- A convenience class to handle events and such. -AnimatedObjects = { - -- Insert objects in this list. - List = { - Filter, - Pulse, - JudgeLine, - StageLines, - ProgressTick, - HitLightning, - ComboDisplay, - ScoreDisplay, - Lifebar, - MissHighlight, - Keys, - Judgment, - Explosions, - Jambar, - PlayerText, - AutoAnimation - }, - - -- Internal functions for automating stuff. - Init = function () - local n = #AnimatedObjects.List - print (n, "objects in Gameplay Screen") - AnimatedObjects.Items = {} - for i = 1, #AnimatedObjects.List do - if AnimatedObjects.List[i] and AnimatedObjects.List[i].new then - local p = Game:GetPlayer(0) - local chan = p.Difficulty.Channels - local ns - local TT = p.Turntable and (p.Channels == 6 or p.Channels == 8) - - if not TT then - ns = Noteskin[chan] - else - ns = NoteskinSpecial[chan] - end - - AnimatedObjects.Items[i] = AnimatedObjects.List[i]:new({ - Player = p, - Noteskin = ns - }) - else - if not AnimatedObjects.List[i] then - print ("AnimatedObjects object",i,"is nil") - end - end - end - end, - - Run = function (Delta) - for i = 1, #AnimatedObjects.Items do - if AnimatedObjects.Items[i] and AnimatedObjects.Items[i].Run ~= nil then - AnimatedObjects.Items[i]:Run(Delta) - end - end - end, - - GearKeyEvent = function (Lane, IsKeyDown, PlayerNumber) - for i = 1, #AnimatedObjects.List do - if AnimatedObjects.Items[i] and AnimatedObjects.Items[i].GearKeyEvent ~= nil then - AnimatedObjects.Items[i]:GearKeyEvent(Lane, IsKeyDown, PlayerNumber) - end - end - end, - - OnHit = function (a, b, c, d, e, f) - for i = 1, #AnimatedObjects.List do - if AnimatedObjects.Items[i] and AnimatedObjects.Items[i].OnHit ~= nil then - AnimatedObjects.Items[i]:OnHit(a, b, c, d, e, f) - end - end - end, - - OnMiss = function (t, l, h, p) - for i = 1, #AnimatedObjects.List do - if AnimatedObjects.Items[i] and AnimatedObjects.Items[i].OnMiss ~= nil then - AnimatedObjects.Items[i]:OnMiss(t, l, h, p) - end - end - end, - - OnActivate = function() - for i = 1, #AnimatedObjects.List do - if AnimatedObjects.Items[i] and AnimatedObjects.Items[i].OnActivate ~= nil then - AnimatedObjects.Items[i]:OnActivate() - end - end - end, - - OnSongFinish = function() - for i = 1, #AnimatedObjects.List do - if AnimatedObjects.Items[i] and AnimatedObjects.Items[i].OnSongFinish ~= nil then - AnimatedObjects.Items[i]:OnSongFinish() - end - end - end -} - -BgAlpha = 0 - --- You can only call Engine:CreateObject, LoadImage and LoadSkin during and after Init is called --- Not on preload time. -function Init() - AutoadjustBackground() - AnimatedObjects.Init() - - - - if GetConfigF("Histogram", "") ~= 0 then - histogram = Histogram:new() - histogram:SetPosition(ScreenWidth - 255, ScreenHeight - 100 - ScoreDisplay.DigitHeight) - histogram:SetColor(30 / 255, 50 / 255, 200 / 255) - hist_bg = histogram:SetBackground("Global/white.png") - hist_bg.Red = 0.2 - hist_bg.Green = 0.2 - hist_bg.Blue = 0.2 - end - - - -- ScreenBackground.Alpha = (0) - Engine:Sort() -end - -function Cleanup() - -- When exiting the screen, this function is called. - -- It's important to clean all targets or memory will be leaked. -end - -function OnFullComboEvent() - IsFullCombo = 1 -end - --- Returns duration of failure animation. -function OnFailureEvent() - if Global.CurrentGaugeType ~= LT_GROOVE then - DoFailAnimation() - return FailAnimation.Duration - else - FadeToBlack() - return SuccessAnimation.Duration - end -end - --- When 'enter' is pressed and the game starts, this function is called. -function OnActivateEvent() - AnimatedObjects.OnActivate() -end - -function HitEvent(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease, PNum) - -- When hits happen, this function is called. - AnimatedObjects.OnHit(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease, PNum) - - if histogram then - histogram:UpdatePoints() - end -end - -function MissEvent(TimeOff, Lane, IsHold, PNum) - -- When misses happen, this function is called. - AnimatedObjects.OnMiss(TimeOff, Lane, IsHold, PNum) - - if histogram then - histogram:UpdatePoints() - end -end - -function KeyEvent(Key, Code, IsMouseInput) - -- All key events, related or not to gear are handled here - if Key == 262 and Code == 1 then -- right arrow - Game:GetPlayer(0).SpeedMultiplier = Game:GetPlayer(0).SpeedMultiplier + 0.25 - end - - if Key == 263 and Code == 1 then -- left arrow - Game:GetPlayer(0).SpeedMultiplier = Game:GetPlayer(0).SpeedMultiplier - 0.25 - end -end - -function GearKeyEvent (Lane, IsKeyDown, PNum) - -- Only lane presses/releases are handled here. - AnimatedObjects.GearKeyEvent(Lane, IsKeyDown, PNum) -end - --- Called when the song is over. -function OnSongFinishedEvent() - AnimatedObjects.OnSongFinish() - DoSuccessAnimation() - return SuccessAnimation.Duration -end - -function Update(Delta) - -- Executed every frame. - - if Game.Active then - AutoAnimation:Run(Delta) - end - - AnimatedObjects.Run(Delta) - -end diff --git a/GameData/Skins/default/screenloading.lua b/GameData/Skins/default/screenloading.lua deleted file mode 100644 index d94b8313..00000000 --- a/GameData/Skins/default/screenloading.lua +++ /dev/null @@ -1,74 +0,0 @@ -skin_require "Loading/phrases" - -IntroDuration = 0.35 -ExitDuration = 0.35 -Acceleration = 0 - - -function UpdateIntro(frac, delta) - targBadge:SetScale(frac) - - frac = 1 - math.pow(1 - frac, 2) - - targLogo.X = targLogo.Width * frac - targLogo.Y = ScreenHeight - targLogo.Height - - BG.Alpha = frac - - Phrases.Fade(frac) - Update(delta) -end - -function UpdateExit(frac, delta) - UpdateIntro(1-frac, delta) -end - -function Init() - Acceleration = 0 - - targLogo = Engine:CreateObject() - targLogo.Texture = "Loading/loading.png" - w = targLogo.Width - h = targLogo.Height - targLogo.Centered = 1 - targLogo.Layer = 16 - - targBadge = Engine:CreateObject() - - targBadge.Texture = "Loading/loadingbadge.png" - targBadge.Centered = 1 - targBadge.Width = 64 - targBadge.Height = 64 - wb = targBadge.Width - targBadge.X = (w - w/2 - wb/2) - targBadge.Y = ScreenHeight - h - targBadge.Layer = 16 - - BG = Engine:CreateObject() - BG.Texture = "STAGEFILE" -- special constant - BG.Centered = 1 - BG.X = ScreenWidth / 2 - BG.Y = ScreenHeight / 2 - - local HRatio = ScreenHeight / BG.Height - local VRatio = ScreenWidth / BG.Width - - BG.ScaleX = math.min(HRatio, VRatio) - BG.ScaleY = math.min(HRatio, VRatio) - BG.Layer = 10 - BG.Alpha = 0 - - Phrases.Init() -end - -function Cleanup() -end - -function Update(Delta) - Acceleration = Acceleration + Delta - - local Bump = Acceleration - math.floor(Acceleration) - targLogo:SetScale(1.1 - 0.1 * Bump) - - targBadge.Rotation = targBadge.Rotation + (6) -end diff --git a/GameData/Skins/default/screenoptions.lua b/GameData/Skins/default/screenoptions.lua deleted file mode 100644 index 98d0b5f0..00000000 --- a/GameData/Skins/default/screenoptions.lua +++ /dev/null @@ -1,49 +0,0 @@ - --- Structure constants -IntroDuration = 0 -ExitDuration = 0 - -function Init() - -end - ---[[ ---key: scan code ---code: 1 is press, 2 is release (0 was repeat) ---is_mouse_input: 0 if not mouse input, 1 if it is. ---]] -function KeyEvent(key, code, is_mouse_input) - -end - --- Intro -function OnIntroBegin() - -end - --- Fraction = Time / IntroDuration -function UpdateIntro(Fraction, Delta) - -end - -function OnIntroEnd() - -end - --- Main loop -function Update(Delta) - -end - --- Exit/Outro -function OnExitBegin() - -end - -function UpdateExit(Fraction, Delta) - -end - -function OnExitEnd() - -end diff --git a/GameData/Skins/default/screenoptions.rml b/GameData/Skins/default/screenoptions.rml deleted file mode 100644 index 4b252b2f..00000000 --- a/GameData/Skins/default/screenoptions.rml +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - -
-
- Options -
-
- -
-
-

Audio


-
-
Threaded Decoder

-
Use WASAPI

-
Use WASAPI Shared Mode

-

- -

General


-
-
Aspect Ratio
- -
-
Fullscreen

-
VSync

-

-

Wheel settings


-
-
Preload

-
Allow grouping BMS by subtitle

-
Disable BMS grouping

-

-

Gameplay settings


-
-
Move keysounds to BGM

-
Interpolate song time

-
Disable BGA

-
Disable Hitsounds (osu!mania only)

-
Judge Offset (ms)
-
Note Offset (s)
- -

-
-
- -
diff --git a/GameData/Skins/default/screenoptionsui.lua b/GameData/Skins/default/screenoptionsui.lua deleted file mode 100644 index a750cc79..00000000 --- a/GameData/Skins/default/screenoptionsui.lua +++ /dev/null @@ -1 +0,0 @@ -print "hello there" \ No newline at end of file diff --git a/GameData/Skins/default/screenselectmusic.lua b/GameData/Skins/default/screenselectmusic.lua deleted file mode 100644 index 0cfe8df3..00000000 --- a/GameData/Skins/default/screenselectmusic.lua +++ /dev/null @@ -1,111 +0,0 @@ -game_require "librd" -skin_require "Global/Background" -skin_require "Global/FadeInScreen" - -skin_require "song_wheel.lua" - - --- Screen Events -function OnSelect() - TransformX = WheelExitX - ScreenFade.In() - return 1 -end - -function OnRestore() - ScreenFade.Out() - - Wheel.SelectedIndex = Wheel.SelectedIndex -- force OnSongChange event -end - -function OnDirectoryChange() - TransformX = WheelX -end - --- ButtonEvents -function DirUpBtnClick() -end - -function KeyEvent(k, c, m) - -end - -function DirUpBtnHover() - DirUpButton.Texture = "SongSelect/up_h.png" -end - -function DirUpBtnHoverLeave() - DirUpButton.Texture = "SongSelect/up.png" -end - -function BackBtnClick() - -end - -function BackBtnHover() - -end - -function BackBtnHoverLeave() - -end - -function Init() - BackgroundAnimation:Init() - ScreenFade.Init() - - font = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 24) - - dd = StringObject2D() - dd.Font = font - dd.Y = 327 - dd.X = 620 - - Engine:AddTarget(dd) - - CreateWheelItems() - - Engine:SetUILayer(24) -end - -function updText() - local sng = Global:GetSelectedSong() - if sng then - local s7k = toSong7K(sng) - if s7k then - local diff = Global:GetDifficulty(0) - if diff then - local author = diff.Author - local nps = diff.Objects / diff.Duration - if string.len(author) > 0 then - author = " by " .. author - end - - dd.Text = "Selected " .. diff.Name .. author .. - string.format("\n%d of %d", Wheel.DifficultyIndex+1, s7k.DifficultyCount) .. - "\n" .. diff.Channels .. " Channels" .. - "\nLevel " .. diff.Level .. - " (" .. string.format("%.02f", nps) .. " nps)" - - end - else - dd.Text = "dotcur mode song"; - end - else - dd.Text = "" - end -end - -function Cleanup() -end - -function ScrollEvent(xoff, yoff) - print (yoff, Wheel.SelectedIndex, Wheel.SelectedIndex - yoff) - Wheel.SelectedIndex = Wheel.SelectedIndex - yoff - Wheel.CursorIndex = Wheel.SelectedIndex -end - -function Update(Delta) - BackgroundAnimation:Update(Delta) - UpdateWheel(Delta) -end diff --git a/GameData/Skins/default/select.wav b/GameData/Skins/default/select.wav deleted file mode 100644 index 24f017be..00000000 Binary files a/GameData/Skins/default/select.wav and /dev/null differ diff --git a/GameData/Skins/default/selectmusicui.lua b/GameData/Skins/default/selectmusicui.lua deleted file mode 100644 index 352cecf1..00000000 --- a/GameData/Skins/default/selectmusicui.lua +++ /dev/null @@ -1,67 +0,0 @@ -function activateAuto(elem) - if elem.id == "autobtn" then - elem.id = "autobtnac" - Global:GetParameters(0).Autoplay = 1 - else - elem.id = "autobtn" - Global:GetParameters(0).Autoplay = 0 - end -end - --- checked is nil or not nil depending on whatever it is. --- it also is on attributes rather than on the element itself (!) -function changesetting(setting, val) - val = (val.attributes.checked ~= nil) and 1 or 0 - if setting == "upscroll" then - Global:GetParameters(0).Upscroll = val - elseif setting == "nofail" then - Global:GetParameters(0).NoFail = val - elseif setting == "random" then - Global:GetParameters(0).Random = val - elseif setting == "auto" then - Global:GetParameters(0).Autoplay = val - end -end - -function changerate(v) - rt = tonumber(v) - if rt and rt > 0 then - Global:GetParameters(0).Rate = rt - end -end - -function gauge_change(event) - Global:GetParameters(0).GaugeType = tonumber(event.parameters["value"]) -end - -function game_change(event) - Global:GetParameters(0).SystemType = tonumber(event.parameters["value"]) -end - -function hidden_change(event) - Global:GetParameters(0).HiddenMode = tonumber(event.parameters["value"]) -end - -function sort_change(event) - Global:SortWheelBy(tonumber(event.parameters["value"])) -end - -function speedclass_change(event) - System.SetConfig("SpeedClass", event.parameters["value"], "Speed") -end - -function speedamt_change(event) - System.SetConfig("SpeedAmount", event.parameters["value"], "Speed") -end - -savedSpeedClass = System.ReadConfigF("SpeedClass", "Speed") -savedSpeedAmt = System.ReadConfigF("SpeedAmount", "Speed") - -function onready(document) - -- restore speed stuff from config... - local scElem = document:GetElementById("sc" .. savedSpeedClass) - local scaElem = document:GetElementById("spdamt") - - scaElem:SetAttribute("value", savedSpeedAmt) - scElem:SetAttribute("selected", 1) -end \ No newline at end of file diff --git a/GameData/Skins/default/skin.lua b/GameData/Skins/default/skin.lua deleted file mode 100644 index 83401e87..00000000 --- a/GameData/Skins/default/skin.lua +++ /dev/null @@ -1,95 +0,0 @@ --- Default skin configuration. - --- Backgrounds -DefaultBackground = "Global/MenuBackground.png" -SelectMusicBackground = "" -EvaluationBackground = DefaultBackground -EvaluationBackground7K = DefaultBackground -MainMenuBackground = "" -DefaultGameplayBackground = DefaultBackground -DefaultGameplay7KBackground = DefaultBackground - --- Gameplay - ---[[ Considerations. - - Playing field size is 800x600. This won't change. - ScreenWidth and ScreenHeight are set automatically. - "Centered" means to use the center of the image instead of the top-left. - -]] - -Cursor = { - RotationSpeed = 140, - Centered = 1, - Zooming = 1, - Size = 60 -} - -Judgment = { - Rotation = -90, - X = 40, - Y = 370, - Centered = 1 - -- Size unused. Determined from image size. -} - -Lifebar = { - Height = 84, -- Width is automatically calculated for now. - X = ScreenWidth - 42, - Y = 384, - Rotation = 90, - Centered = 1 -} - --- Audio -AudioManifest = { - Miss = "miss.wav", - Fail = function() - return Global.CurrentGaugeType ~= LT_GROOVE and "stage_failed.ogg" or "" - end, - ClickPlay = "drop.wav", - - --[[ - -- Better than autodetection. - -- See, if you make a closure, Decision, Hover, then BGM - -- will be called in that order. - -- That means you can choose them in a consistent way. - -- ala LR2 - --]] - SongSelectDecision = "select.wav", - SongSelectHover = "click.wav", - SongSelectBGM = function() - return "loop" .. math.random(8) .. ".ogg" - end -} - --- 7K mode configuration. --- Time that the 'miss' layer will be shown on BMS when a miss occurs. -OnMissBGATime = 0.5 - --- Set screen filter transparency on 7K. -ScreenFilter = 0.9 - --- Whether to display the in-game histogram. -Histogram = 0 - -Hitlightning = 1 - --- show up to 999 ms off -HitErrorDisplayLimiter = 999 - --- Whether to go to song select inmediately on failure -GoToSongSelectOnFailure = 0 - --- Whether to not wait for enter to be pressed to start playing -InmediateActivation = 1 - --- 1 is first after processing SV, 1 is mmod, 2 is cmod, anything else is default. --- default: first speed before processing SV -DefaultSpeedKind = function () - return System.ReadConfigF("SpeedClass", "Speed") -end -DefaultSpeedUnits = function() - return System.ReadConfigF("SpeedAmount", "Speed") -end diff --git a/GameData/Skins/default/song_wheel.lua b/GameData/Skins/default/song_wheel.lua deleted file mode 100644 index bb586396..00000000 --- a/GameData/Skins/default/song_wheel.lua +++ /dev/null @@ -1,262 +0,0 @@ --- Read "state" as the wheel's state -local State = { - X = 0, - ListY = 0, - PendingY = 0, - LastSign = 0, - Time = 0, - ScrollSpeed = 0, -} - - -local WheelItems = {} - --- Wheel item size -local ItemWidth = 600 -local ItemHeight = 76.8 - --- Wheel transformation -local WheelEnterX = 0 -local WheelExitX = -ItemWidth -local WheelSpeed = 900 - -local function FracToWheelIndex(t) - return t * Wheel.DisplayItemCount + Wheel.DisplayStartIndex -end - --- List transformation -function TransformListHorizontal(t) - return State.X -end - -function TransformListVertical(t) - return State.ListY + FracToWheelIndex(t) * ItemHeight -end - -function TransformListWidth(t) - return ItemWidth -end - -function TransformListHeight(t) - return ItemHeight -end - -function OnItemHover(Index, BoundIndex, Line, Selected) - updText() -end - -function OnItemHoverLeave(Index, BoundIndex, Line, Selected) - Wheel.CursorIndex = Wheel.SelectedIndex -end - -function OnItemClick(Index, BoundIndex, Line, Song) - print (Index, BoundIndex, Line, Song, Wheel.ListIndex, Wheel.SelectedIndex, Wheel.CursorIndex) - if Song and (Index == Wheel.SelectedIndex) then - Wheel:ConfirmSelection() - else - Wheel.SelectedIndex = Index - if not Song then - Wheel:ConfirmSelection() -- Go into directories inmediately - end - end -end - - --- This gets called for every item - ideally you dispatch for every item. -function TransformItem(Item, Song, IsSelected, Index) - WheelItems[Item](Song, IsSelected, Index); -end - -local WheelItemStrings = {} -function TransformString(Item, Song, IsSelected, Index, Txt) - WheelItemStrings[Item](Song, IsSelected, Index, Txt); -end - --- This recieves song and difficulty changes. -function OnSongChange(Song, Diff) - updText() - Wheel.CursorIndex = Wheel.SelectedIndex -end - -function CreateWheelItems() - local WheelBackground = Object2D() - State.WheelBackground = WheelBackground - WheelBackground.Texture = "Global/white.png" - WheelBackground.Width = ItemWidth - WheelBackground.Height = ItemHeight - - WheelItems[Wheel:AddSprite(WheelBackground)] = function(Song, IsSelected, Index) - if IsSelected == true then - WheelBackground.Red = 0.1 - WheelBackground.Green = 0.3 - WheelBackground.Blue = 0.7 - else - if Index == Wheel.ListIndex then - WheelBackground.Red = 0.05 - WheelBackground.Green = 0.15 - WheelBackground.Blue = 0.35 - else - local Nrm = Index % 2 - if Nrm == 0 then - WheelBackground.Red = 0 - WheelBackground.Green = 0 - WheelBackground.Blue = 0 - else - WheelBackground.Red = 0.2 - WheelBackground.Green = 0.2 - WheelBackground.Blue = 0.2 - end - end - end - --(WheelBackground) - end - - strName = StringObject2D() - strArtist = StringObject2D() - strDuration = StringObject2D() - strLevel = StringObject2D() - strSubtitle = StringObject2D() - - wheelfont = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 30) - infofont = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 18) - - strName.Font = wheelfont - strArtist.Font = font - strDuration.Font = infofont - strLevel.Font = infofont - strSubtitle.Font = infofont - - local dur_x = 50 - - -- Transform these strings according to what they are - WheelItemStrings[Wheel:AddString(strName)] = function(Song, IsSelected, Index, Txt) - strName.X = strName.X + 10 - if Song then - strName.Text = Song.Title - else - strName.Text = Txt - end - - local w = wheelfont:GetLength(strName.Text) - local m = ItemWidth - dur_x - 20 - if w > m then - strName.ScaleX = m / w - else - strName.ScaleX = 1 - end - - end - - WheelItemStrings[Wheel:AddString(strArtist)] = function(Song, IsSelected, Index, Txt) - strArtist.X = strArtist.X + 10 - strArtist.Y = strArtist.Y + 45 - strArtist.Red = 1 - if Song then - strArtist.Text = "by " .. Song.Author - strArtist.Blue = 0.3 - strArtist.Green = 0.7 - else - strArtist.Text = "directory..." - strArtist.Blue = 0.3 - strArtist.Green = 0.3 - end - - local w = infofont:GetLength(strArtist.Text) - local m = ItemWidth - 20 - - if w > m then - strArtist.ScaleX = m / w - else - strArtist.ScaleX = 1 - end - - end - - WheelItemStrings[Wheel:AddString(strDuration)] = function(Song, IsSelected, Index, Txt) - if Song then - local sng = toSong7K(Song) - if not sng then - sng = toSongDC(Song) - end - - local s = floor(sng:GetDifficulty(0).Duration % 60) - local m = floor( (sng:GetDifficulty(0).Duration - s) / 60 ) - strDuration.Text = string.format("%d:%02d", m, s) - else - strDuration.Text = "" - end - strDuration.X = strDuration.X + ItemWidth - dur_x - strDuration.Y = strDuration.Y + 10 - end - - WheelItemStrings[Wheel:AddString(strLevel)] = function(Song, IsSelected, Index, Txt) - - end - - WheelItemStrings[Wheel:AddString(strSubtitle)] = function(Song, IsSelected, Index, Txt) - strSubtitle.X = strSubtitle.X + 10 - strSubtitle.Y = strSubtitle.Y + 30 - if Song then - strSubtitle.Text = Song.Subtitle - else - strSubtitle.Text = "" - end - - local w = infofont:GetLength(strSubtitle.Text) - local m = ItemWidth - 20 - - if w > m then - strSubtitle.ScaleX = m / w - else - strSubtitle.ScaleX = 1 - end - end - - - WheelSeparator = Engine:CreateObject() - WheelSeparator.Texture = "Global/white.png" - WheelSeparator.Height = ScreenHeight - WheelSeparator.Width = 5 - WheelSeparator.Y = 0 - - wheeltick = Engine:CreateObject() - wheeltick.Texture = "Global/white.png" - wheeltick.Height = 8 - wheeltick.Width = 16 - wheeltick.Layer = 25 - - Wheel.DisplayItemCount = ceil(ScreenHeight / ItemHeight) + 1 - --Wheel.DisplayItemOffset = - Wheel.DisplayItemCount / 2 -end - -function UpdateWheel(Delta) - State.X = clamp(State.X + (WheelEnterX - State.X) * Delta * WheelSpeed, WheelExitX, WheelEnterX) - - WheelSeparator.X = State.X + ItemWidth - wheeltick.Width = math.max(16, ScreenWidth / Wheel.ItemCount) - wheeltick.X = (Wheel.SelectedIndex % Wheel.ItemCount) / (Wheel.ItemCount - 1) * (ScreenWidth - wheeltick.Width) - wheeltick.Y = 86 - - local Offset = ScreenHeight / 2 - ItemHeight / 2 - local SelectedSongCenterY = math.floor(-Wheel.SelectedIndex * ItemHeight + Offset) - State.PendingY = SelectedSongCenterY - State.ListY - State.ScrollSpeed = -math.abs(State.PendingY) / 0.25 - - -- don't overshoot - local dist = math.abs(SelectedSongCenterY - State.ListY) - local deltaWheel = sign(State.PendingY) * math.min(State.ScrollSpeed * Delta, dist) - - if math.abs(State.PendingY) < 1 then - State.ListY = State.ListY + State.PendingY - State.PendingY = 0 - else - State.ListY = State.ListY - deltaWheel - State.PendingY = State.PendingY - deltaWheel - end - - -- Display -halfCount to +halfCount - -- what's the center's current index? - local centerCurrentIndex = floor(-State.ListY / ItemHeight) - Wheel.DisplayStartIndex = centerCurrentIndex - -end \ No newline at end of file diff --git a/GameData/Skins/default/stage_failed.ogg b/GameData/Skins/default/stage_failed.ogg deleted file mode 100644 index 989d2f96..00000000 Binary files a/GameData/Skins/default/stage_failed.ogg and /dev/null differ diff --git a/GameData/Skins/default/texparams.rcf b/GameData/Skins/default/texparams.rcf deleted file mode 100644 index 668548ae..00000000 --- a/GameData/Skins/default/texparams.rcf +++ /dev/null @@ -1,15 +0,0 @@ -font.tga { - minfilter: nearest-mipmap-linear; - maxfilter: nearest; -} -loading.png { - minfilter: nearest; - maxfilter: nearest; - gen-mipmap: false; -} -tile_aqua.png { - maxfilter: nearest; - minfilter: nearest; - wrap-s: repeat; - wrap-t: repeat; -} \ No newline at end of file diff --git a/GameData/Skins/ftb2d/assets/belowbelowfield.png b/GameData/Skins/ftb2d/assets/belowbelowfield.png deleted file mode 100644 index 2eb2706e..00000000 Binary files a/GameData/Skins/ftb2d/assets/belowbelowfield.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/colexplosion.csv b/GameData/Skins/ftb2d/assets/colexplosion.csv deleted file mode 100644 index 7aa311cd..00000000 --- a/GameData/Skins/ftb2d/assets/colexplosion.csv +++ /dev/null @@ -1,5 +0,0 @@ -assets/colexplosion.png -1.png,0,0,168,168 -2.png,0,178,168,168 -3.png,178,0,168,168 -4.png,178,178,168,168 diff --git a/GameData/Skins/ftb2d/assets/colexplosion.png b/GameData/Skins/ftb2d/assets/colexplosion.png deleted file mode 100644 index 36804a4a..00000000 Binary files a/GameData/Skins/ftb2d/assets/colexplosion.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/explosion.csv b/GameData/Skins/ftb2d/assets/explosion.csv deleted file mode 100644 index 44bd64e0..00000000 --- a/GameData/Skins/ftb2d/assets/explosion.csv +++ /dev/null @@ -1,11 +0,0 @@ -assets/explosion.png -9.png,0,148,138,138 -10.png,0,0,138,138 -1.png,290,73,50,50 -2.png,290,0,63,63 -3.png,284,142,80,80 -4.png,0,296,97,97 -5.png,277,278,113,113 -6.png,148,278,119,119 -7.png,148,142,126,126 -8.png,148,0,132,132 \ No newline at end of file diff --git a/GameData/Skins/ftb2d/assets/explosion.png b/GameData/Skins/ftb2d/assets/explosion.png deleted file mode 100644 index 835368b2..00000000 Binary files a/GameData/Skins/ftb2d/assets/explosion.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/field.png b/GameData/Skins/ftb2d/assets/field.png deleted file mode 100644 index ea97ef06..00000000 Binary files a/GameData/Skins/ftb2d/assets/field.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/field_limit.png b/GameData/Skins/ftb2d/assets/field_limit.png deleted file mode 100644 index 89ff28f8..00000000 Binary files a/GameData/Skins/ftb2d/assets/field_limit.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/flair.png b/GameData/Skins/ftb2d/assets/flair.png deleted file mode 100644 index 3e658055..00000000 Binary files a/GameData/Skins/ftb2d/assets/flair.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/glow.csv b/GameData/Skins/ftb2d/assets/glow.csv deleted file mode 100644 index 2609a110..00000000 --- a/GameData/Skins/ftb2d/assets/glow.csv +++ /dev/null @@ -1,36 +0,0 @@ -assets/glow.png -1.png,0,1241,875,63 -10.png,885,73,875,63 -11.png,885,0,875,63 -12.png,0,1679,875,63 -13.png,0,1606,875,63 -14.png,0,1533,875,63 -15.png,0,1460,875,63 -16.png,0,1387,875,63 -17.png,0,1314,875,63 -18.png,885,730,875,63 -19.png,0,1168,875,63 -2.png,885,657,875,63 -20.png,0,1095,875,63 -21.png,0,1022,875,63 -22.png,0,949,875,63 -23.png,0,876,875,63 -24.png,0,803,875,63 -25.png,0,730,875,63 -26.png,0,657,875,63 -27.png,0,584,875,63 -28.png,0,511,875,63 -29.png,0,438,875,63 -3.png,885,584,875,63 -30.png,0,365,875,63 -31.png,0,292,875,63 -32.png,0,219,875,63 -33.png,0,146,875,63 -34.png,0,73,875,63 -35.png,0,0,875,63 -4.png,885,511,875,63 -5.png,885,438,875,63 -6.png,885,365,875,63 -7.png,885,292,875,63 -8.png,885,219,875,63 -9.png,885,146,875,63 diff --git a/GameData/Skins/ftb2d/assets/glow.png b/GameData/Skins/ftb2d/assets/glow.png deleted file mode 100644 index 12e53727..00000000 Binary files a/GameData/Skins/ftb2d/assets/glow.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/holds.csv b/GameData/Skins/ftb2d/assets/holds.csv deleted file mode 100644 index a970fc7e..00000000 --- a/GameData/Skins/ftb2d/assets/holds.csv +++ /dev/null @@ -1,15 +0,0 @@ -assets/holds.png -1.png,135,0,125,20 -10.png,0,240,125,20 -11.png,0,180,125,20 -12.png,0,120,125,20 -13.png,0,60,125,20 -14.png,0,0,125,20 -2.png,270,0,125,20 -3.png,135,300,125,20 -4.png,135,240,125,20 -5.png,135,180,125,20 -6.png,135,120,125,20 -7.png,135,60,125,20 -8.png,270,60,125,20 -9.png,0,300,125,20 diff --git a/GameData/Skins/ftb2d/assets/holds.png b/GameData/Skins/ftb2d/assets/holds.png deleted file mode 100644 index 35f262b0..00000000 Binary files a/GameData/Skins/ftb2d/assets/holds.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/hp_bottom.png b/GameData/Skins/ftb2d/assets/hp_bottom.png deleted file mode 100644 index 0ac9e9f6..00000000 Binary files a/GameData/Skins/ftb2d/assets/hp_bottom.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/hp_fill.png b/GameData/Skins/ftb2d/assets/hp_fill.png deleted file mode 100644 index 0d16f394..00000000 Binary files a/GameData/Skins/ftb2d/assets/hp_fill.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/hp_l.png b/GameData/Skins/ftb2d/assets/hp_l.png deleted file mode 100644 index 503c75a4..00000000 Binary files a/GameData/Skins/ftb2d/assets/hp_l.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/hp_overlay.png b/GameData/Skins/ftb2d/assets/hp_overlay.png deleted file mode 100644 index f330e936..00000000 Binary files a/GameData/Skins/ftb2d/assets/hp_overlay.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/hp_r.png b/GameData/Skins/ftb2d/assets/hp_r.png deleted file mode 100644 index 82677cff..00000000 Binary files a/GameData/Skins/ftb2d/assets/hp_r.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/judge.csv b/GameData/Skins/ftb2d/assets/judge.csv deleted file mode 100644 index f983dfac..00000000 --- a/GameData/Skins/ftb2d/assets/judge.csv +++ /dev/null @@ -1,6 +0,0 @@ -assets/judge.png -1.png,0,0,141,33 -2.png,0,86,141,33 -3.png,0,129,141,33 -4.png,0,172,141,33 -5.png,0,43,141,33 diff --git a/GameData/Skins/ftb2d/assets/judge.png b/GameData/Skins/ftb2d/assets/judge.png deleted file mode 100644 index c32fc076..00000000 Binary files a/GameData/Skins/ftb2d/assets/judge.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/keyhold.png b/GameData/Skins/ftb2d/assets/keyhold.png deleted file mode 100644 index 0747995a..00000000 Binary files a/GameData/Skins/ftb2d/assets/keyhold.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/lightning.csv b/GameData/Skins/ftb2d/assets/lightning.csv deleted file mode 100644 index 740293d4..00000000 --- a/GameData/Skins/ftb2d/assets/lightning.csv +++ /dev/null @@ -1,16 +0,0 @@ -assets/lightning.png -1.png,270,0,125,1250 -10.png,1080,0,125,1250 -11.png,1890,0,125,1250 -12.png,810,0,125,1250 -13.png,675,0,125,1250 -14.png,540,0,125,1250 -15.png,405,0,125,1250 -2.png,135,0,125,1250 -3.png,0,0,125,1250 -4.png,945,0,125,1250 -5.png,1755,0,125,1250 -6.png,1620,0,125,1250 -7.png,1485,0,125,1250 -8.png,1350,0,125,1250 -9.png,1215,0,125,1250 diff --git a/GameData/Skins/ftb2d/assets/lightning.png b/GameData/Skins/ftb2d/assets/lightning.png deleted file mode 100644 index b32b7995..00000000 Binary files a/GameData/Skins/ftb2d/assets/lightning.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/notes.csv b/GameData/Skins/ftb2d/assets/notes.csv deleted file mode 100644 index 5e6f65a4..00000000 --- a/GameData/Skins/ftb2d/assets/notes.csv +++ /dev/null @@ -1,14 +0,0 @@ -assets/notes.png -1.png,0,210,125,25 -10.png,0,105,125,25 -11.png,0,70,125,25 -12.png,0,35,125,25 -13.png,0,0,125,25 -2.png,135,140,125,25 -3.png,135,105,125,25 -4.png,135,70,125,25 -5.png,135,35,125,25 -6.png,135,0,125,25 -7.png,135,175,125,25 -8.png,0,175,125,25 -9.png,0,140,125,25 diff --git a/GameData/Skins/ftb2d/assets/notes.png b/GameData/Skins/ftb2d/assets/notes.png deleted file mode 100644 index 47ce192c..00000000 Binary files a/GameData/Skins/ftb2d/assets/notes.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/assets/songpercent.png b/GameData/Skins/ftb2d/assets/songpercent.png deleted file mode 100644 index ffa8c9cf..00000000 Binary files a/GameData/Skins/ftb2d/assets/songpercent.png and /dev/null differ diff --git a/GameData/Skins/ftb2d/custom_defs.lua b/GameData/Skins/ftb2d/custom_defs.lua deleted file mode 100644 index c1ccbaae..00000000 --- a/GameData/Skins/ftb2d/custom_defs.lua +++ /dev/null @@ -1,107 +0,0 @@ -require "utils" - -GearStartX = 40 - -NoteHeight = 10 - -Channels7Sizes = { - 40, - 40, - 40, - 40, - 40, - 40, - 40 -} - -Channels7Positions = {} - -Sizeup(Channels7Positions, Channels7Sizes, 7) - -Channels7 = { - GearWidth = GearWidthByChannels[7], - BarlineWidth = GearWidthByChannels[7], - - -- Note Images - Key1Image = 1, - Key2Image = 2, - Key3Image = 3, - Key4Image = 4, - Key5Image = 3, - Key6Image = 2, - Key7Image = 1, - - -- Lane positions - Key1X = Channels7Positions[1], - Key2X = Channels7Positions[2], - Key3X = Channels7Positions[3], - Key4X = Channels7Positions[4], - Key5X = Channels7Positions[5], - Key6X = Channels7Positions[6], - Key7X = Channels7Positions[7], - - -- Lane Widths - Key1Width = Channels7Sizes[1], - Key2Width = Channels7Sizes[2], - Key3Width = Channels7Sizes[3], - Key4Width = Channels7Sizes[4], - Key5Width = Channels7Sizes[5], - Key6Width = Channels7Sizes[6], - Key7Width = Channels7Sizes[7], - Map = {1, 2, 3, 4, 5, 6, 7} -} - -Channels4 = { - Key1X = Channels7Positions[2], - Key2X = Channels7Positions[3], - Key3X = Channels7Positions[5], - Key4X = Channels7Positions[6], - - Key1Width = Channels7Sizes[1], - Key2Width = Channels7Sizes[2], - Key3Width = Channels7Sizes[3], - Key4Width = Channels7Sizes[4], - - Map = {2, 3, 5, 6} -} - -Channels6 = { - Key1X = Channels7Positions[1], - Key2X = Channels7Positions[2], - Key3X = Channels7Positions[3], - Key4X = Channels7Positions[5], - Key5X = Channels7Positions[6], - Key6X = Channels7Positions[7], - - Key1Width = Channels7Sizes[1], - Key2Width = Channels7Sizes[2], - Key3Width = Channels7Sizes[3], - Key4Width = Channels7Sizes[4], - Key5Width = Channels7Sizes[5], - Key6Width = Channels7Sizes[6], - - Map = {1, 2, 3, 5, 6, 7} -} - -Channels5 = { - Key1X = Channels7Positions[2], - Key2X = Channels7Positions[3], - Key3X = Channels7Positions[4], - Key4X = Channels7Positions[5], - Key5X = Channels7Positions[6], - - Key1Width = Channels7Sizes[1], - Key2Width = Channels7Sizes[2], - Key3Width = Channels7Sizes[3], - Key4Width = Channels7Sizes[4], - Key5Width = Channels7Sizes[5], - - Map = {2, 3, 4, 5, 6} -} - - -Noteskin = Noteskin or {} -Noteskin[4] = Channels4 -Noteskin[5] = Channels5 -Noteskin[6] = Channels6 -Noteskin[7] = Channels7 \ No newline at end of file diff --git a/GameData/Skins/ftb2d/ftb.csv b/GameData/Skins/ftb2d/ftb.csv deleted file mode 100644 index b5953ec8..00000000 --- a/GameData/Skins/ftb2d/ftb.csv +++ /dev/null @@ -1,12 +0,0 @@ -Global/filter.png,filter,40,0,280,768,0,0 -assets/field.png,field_bg,40,0,280,540,0,0 -assets/hp_bottom.png,hp_bottom,40,600,280,60,18,0 -assets/hp_l.png,hp_left,37,600,3,60,19,0 -assets/hp_r.png,hp_right,320,600,3,60,19,0 -assets/keyhold.png,keyhold,40,600,280,60,20,0 -assets/field_limit.png,stage_l,37,0,3,768,1,0 -assets/field_limit.png,stage_r,320,0,3,768,1,0 -assets/belowbelowfield.png,bottom,40,660,280,108,16,0 -assets/hp_fill.png,hp_fill,40,600,280,60,19,0 -assets/songpercent.png,songpercent,37,768,3,768,21,0 -assets/flair.png,flair,320,768,3,768,21,0 \ No newline at end of file diff --git a/GameData/Skins/ftb2d/ftb_font.ttf b/GameData/Skins/ftb2d/ftb_font.ttf deleted file mode 100644 index b92eae18..00000000 Binary files a/GameData/Skins/ftb2d/ftb_font.ttf and /dev/null differ diff --git a/GameData/Skins/ftb2d/noteskin.lua b/GameData/Skins/ftb2d/noteskin.lua deleted file mode 100644 index d59a5e1d..00000000 --- a/GameData/Skins/ftb2d/noteskin.lua +++ /dev/null @@ -1,98 +0,0 @@ -if Notes.Channels > 7 then - fallback_require("noteskin") - return -end - -skin_require("custom_defs") -game_require("TextureAtlas") --- All notes have their origin centered. - -normalNotes = {} -holdBodies = {} - -function setNoteStuff(note, i) - note.Width = Noteskin[Notes.Channels]['Key' .. i .. 'Width'] - note.X = Noteskin[Notes.Channels]['Key' .. i .. 'X'] - note.Height = NoteHeight - note.Layer = 14 - note.Lighten = 1 - note.LightenFactor = 0 -end - -function Init() - Atlas = TextureAtlas:new("GameData/Skins/ftb2d/assets/notes.csv") - AtlasHolds = TextureAtlas:new("Gamedata/Skins/ftb2d/assets/holds.csv") - for i=1,Notes.Channels do - local MapLane = Noteskin[Notes.Channels].Map[i] - normalNotes[i] = Object2D() - local note = normalNotes[i] - local image = Noteskin[7]['Key' .. MapLane .. 'Image'] .. ".png" - note.Texture = Atlas.File - Atlas:SetObjectCrop(note, image) - setNoteStuff(note, i) - - holdBodies[i] = Object2D() - note = holdBodies[i] - - image = Noteskin[7]['Key' .. MapLane .. 'Image'] .. ".png" - note.Texture = AtlasHolds.File - AtlasHolds:SetObjectCrop(note, image) - setNoteStuff(note, i) - end -end - -function Update(delta, beat) -end - -function drawNormalInternal(lane, loc, frac, active_level) - local note = normalNotes[lane + 1] - note.Y = loc - - if active_level ~= 3 then - Notes:Render(note) - end -end - --- 1 is enabled. 2 is being pressed. 0 is failed. 3 is succesful hit. -function drawHoldBodyInternal(lane, loc, size, active_level) - local note = holdBodies[lane + 1] - note.Y = loc - note.Height = size - - if active_level == 2 then - note.LightenFactor = 1 - else - note.LightenFactor = 0 - end - - if active_level ~= 3 and active_level ~= 0 then - Notes:Render(note) - end -end - -function drawMineInternal(lane, loc, frac) - -- stub while mines are accounted in the scoring system. -end - --- From now on, only engine variables are being set. --- Barline -Notes.BarlineEnabled = 1 -Notes.BarlineOffset = NoteHeight / 2 -Notes.BarlineStartX = GearStartX -Notes.BarlineWidth = Noteskin[7].BarlineWidth -Notes.JudgmentY = 228 + NoteHeight / 2 -Notes.DecreaseHoldSizeWhenBeingHit = 1 -Notes.DanglingHeads = 1 - --- How many extra units do you require so that the whole bounding box is accounted --- when determining whether to show this note or not. -Notes.NoteScreenSize = NoteHeight / 2 - -DrawNormal = drawNormalInternal -DrawFake = drawNormalInternal -DrawLift = drawNormalInternal -DrawMine = drawMineInternal - -DrawHoldHead = drawNormalInternal -DrawHoldTail = nil -DrawHoldBody = drawHoldBodyInternal diff --git a/GameData/Skins/ftb2d/screengameplay7k.lua b/GameData/Skins/ftb2d/screengameplay7k.lua deleted file mode 100644 index 5d86954c..00000000 --- a/GameData/Skins/ftb2d/screengameplay7k.lua +++ /dev/null @@ -1,351 +0,0 @@ -if Game:GetPlayer(0).Channels > 7 then - fallback_require("screengameplay7k") - return -end - -game_require("TextureAtlas") -game_require("FrameInterpolator") -game_require("FixedObjects") -skin_require("custom_defs") -game_require("utils") -game_require("AnimationFunctions") - -skin_require("Global/FadeInScreen") --- Set up constants for everyone - -GearWidth = 275 -GearHeight = GearHeightCommon - --- All of these will be loaded in the loading screen instead of --- in the main thread, and will also be unloaded at the end. -Preload = { - "assets/explosion.png", - "assets/field.png", - "assets/glow.png", - "assets/judge.png", - "assets/belowbelowfield.png", - "assets/lightning.png", - "assets/field_limit.png", - "assets/hp_l.png", - "assets/hp_r.png", - "assets/hp_fill.png", - "assets/notes.png", - "assets/holds.png", - "assets/flair.png", - "assets/hp_bottom.png", - "assets/keyhold.png", - "assets/songpercent.png" -} - -JudgmentAtlas = TextureAtlas:new(GetSkinFile("assets/judge.csv")) -ExplosionColorAtlas = TextureAtlas:new(GetSkinFile("assets/colexplosion.csv")) - --- Status of a lane being pressed or not. -KeyArray = {} - --- Key overlay. -KeyHold = {} - --- Judgments. -Judgments = {} - --- The keys themselves. -Key = {} - -Colours = { - {255, 0, 204}, - {255, 204, 0}, - {203, 255, 0}, - {0, 203, 255} -} -Layout = {1, 2, 3, 4, 3, 2, 1} - -function MakeKeys(i) - KeyHold[i] = Engine:CreateObject() - obj = KeyHold[i] - obj.Texture = "assets/keyhold.png" - obj.Height = 60 - obj.Width = 40 - obj.Centered = 1 - obj.X = Noteskin[7]["Key" .. i .. "X"] - obj.Y = Game:GetPlayer(0).JudgmentY + obj.Height / 2 + 5 - obj.Layer = 16 - obj.Alpha = 1 - - Key[i] = Engine:CreateObject() - obj = Key[i] - obj.Texture = "Global/white.png" - obj.Height = 60 - obj.Width = 40 - obj.Centered = 1 - obj.X = Noteskin[7]["Key" .. i .. "X"] - obj.Y = Game:GetPlayer(0).JudgmentY + obj.Height / 2 + 5 - obj.Layer = 16 - obj.Alpha = 1 - obj.Lighten = 1 - obj.LightenFactor = 0 - - obj.Red = Colours[Layout[i]][1] / 255 - obj.Green = Colours[Layout[i]][2] / 255 - obj.Blue = Colours[Layout[i]][3] / 255 - -end - -function Init() - AutoadjustBackground() - - CreateText() - - print "Creating animated objects" - Glow = FrameInterpolator:New("assets/glow.csv") - - Glow.TotalFrames = 35 - Glow.Object.X = GearStartX - Glow.Object.Y = ScreenHeight - GearWidth + 25 - Glow.Object.Height = 20 - Glow.Object.Width = 280 - Glow.Object.Layer = 20 - - print "Creating fixed objects." - GameObjects = FixedObjects:new() - GameObjects:CreateFromCSV("ftb.csv") - - Sprites = GameObjects.Sprites - HealthBar = Sprites['hp_fill'] - HealthBar.Lighten = 1 - HealthBar.LightenFactor = 2 - HealthBar.ScaleX = 0 - - SongPosition = Sprites["songpercent"] - - Flair = Sprites["flair"] - - print "Creating explosions and lightning." - Explosions = {} - Lightning = {} - for i=1, 7 do - Explosions[i] = {} - Explosions[i].EffectExplosion = FrameInterpolator:New("assets/explosion.csv", 0.15) - - local obj = Explosions[i].EffectExplosion.Object - obj.Width = GearWidth / 7 - obj.Height = obj.Width - obj.X = Noteskin[7]["Key" .. i .. "X"] - obj.Y = Game:GetPlayer(0).JudgmentY - obj.Centered = 1 - obj.Layer = 21 - obj.Alpha = 0 - - obj.Red = Colours[Layout[i]][1] / 255 - obj.Green = Colours[Layout[i]][2] / 255 - obj.Blue = Colours[Layout[i]][3] / 255 - - Lightning[i] = FrameInterpolator:New("assets/lightning.csv", 0.4) - obj = Lightning[i].Object - obj.Height = 540 - obj.Width = GearWidth / 7 - obj.X = Noteskin[7]["Key" .. i .. "X"] - obj.Width / 2 - obj.Y = 0 - obj.Layer = 24 - obj.Alpha = 0 - - obj.Red = Colours[Layout[i]][1] / 255 - obj.Green = Colours[Layout[i]][2] / 255 - obj.Blue = Colours[Layout[i]][3] / 255 - - KeyArray[i] = false - - MakeKeys(i) - - Judgments[i] = Engine:CreateObject() - obj = Judgments[i] - obj.Texture = JudgmentAtlas.File - obj.Width = GearWidth / 7 - obj.Height = 0.234 * obj.Width - obj.Layer = 22 - obj.Alpha = 0 - obj.Centered = 1 - obj:SetScale(1) - obj.X = Noteskin[7]["Key" .. i .. "X"] - obj.Y = Game:GetPlayer(0).JudgmentY - obj.Height / 2 - end - - Engine:Sort() -end - -function Cleanup() -end - -function OnFullComboEvent() -end - -function OnFailureEvent() - return 0 -end - --- When 'enter' is pressed and the game starts, this function is called. -function OnActivateEvent() -end - -function Explode(frac, targ) - targ:SetScale(1 + frac * 2.5) - targ.Alpha = 1 - frac - return 1 -end - -function Judgment(frac, targ) - targ.Alpha = 1 - frac - targ.Y = Game:GetPlayer(0).JudgmentY - 100 * frac - return 1 -end - --- The judgments are in inverted order, so yup. -map = {5, 4, 3, 2, 1} -function JUDGE(JudgmentValue, Lane) - if JudgmentValue == 0 then - JudgmentValue = 1 - end - - JudgmentAtlas:SetObjectCrop(Judgments[Lane], map[JudgmentValue] .. ".png") - Engine:AddAnimation(Judgments[Lane], "Judgment", EaseIn, 0.4, 0) -end - -function HitEvent(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease) - local MapLane = Noteskin[Game:GetPlayer(0).Channels].Map[Lane] - - Engine:AddAnimation(Explosions[MapLane].EffectExplosion.Object, "Explode", EaseNone, 0.25, 0) - Explosions[MapLane].EffectExplosion.CurrentTime = 0 - - JUDGE(JudgmentValue, MapLane) -end - -function MissEvent(TimeOff, Lane, IsHold) - JUDGE(5, Lane) -end - -function KeyEvent(Key, Code, IsMouseInput) -end - -function GearKeyEvent (Lane, IsKeyDown) - local MapLane = Noteskin[Game:GetPlayer(0).Channels].Map[Lane] - - KeyArray[MapLane] = IsKeyDown - - Lightning[MapLane].Object.Alpha = 1 - Lightning[MapLane].CurrentTime = 0 - - if IsKeyDown == 1 then - Key[MapLane].LightenFactor = 1 - else - Key[MapLane].LightenFactor = 0 - end -end - --- Called when the song is over. -function OnSongFinishedEvent() - return 0 -end - -function CreateText() - miniFont = Fonts.TruetypeFont(GetSkinFile("ftb_font.ttf"), 16) - largeFont = Fonts.TruetypeFont(GetSkinFile("ftb_font.ttf"), 36) - xlFont = Fonts.TruetypeFont(GetSkinFile("ftb_font.ttf"), 61) - - print "Creating text." - lblCombo = StringObject2D() - lblCombo.Font = miniFont - lblCombo.Text = "Combo" - lblCombo.X = 45 - lblCombo.Y = 665 - Engine:AddTarget(lblCombo) - lblCombo.Layer = 24 - - - lblmaxCombo = StringObject2D() - lblmaxCombo.Font = miniFont - lblmaxCombo.Text = "MAX COMBO" - lblmaxCombo.X = 230 - lblmaxCombo.Y = 665 - Engine:AddTarget(lblmaxCombo) - - lblmaxCombo.Layer = 24 - - lblcurCombo = StringObject2D() - lblcurCombo.Font = largeFont - lblcurCombo.X = 45 - lblcurCombo.Y = 665 - lblcurCombo.Layer = 24 - Engine:AddTarget(lblcurCombo) - - lblcurMaxCombo = StringObject2D() - lblcurMaxCombo.Font = largeFont - lblcurMaxCombo.Text = "0" - lblcurMaxCombo.Y = 665 - lblcurMaxCombo.Layer = 24 - Engine:AddTarget(lblcurMaxCombo) - - lblScore = StringObject2D() - lblScore.Font = xlFont - lblScore.Y = 680 - lblScore.X = 45 - lblScore.Layer = 24 - Engine:AddTarget(lblScore) - - lblstScore = StringObject2D() - lblstScore.Font = miniFont - lblstScore.Text = "score" - lblstScore.Y = 740 - lblstScore.X = 155 - lblstScore.Layer = 24 - - Engine:AddTarget(lblstScore) -end - -function UpdateText() - local ScoreKeeper = Game:GetPlayer(0).Scorekeeper - lblcurCombo.Text = ScoreKeeper:GetScore(ST_COMBO) - lblcurMaxCombo.Text = ScoreKeeper:GetScore(ST_MAX_COMBO) - lblcurMaxCombo.X = 312 - largeFont:GetLength(lblcurMaxCombo.Text) - - lblScore.Text = string.format("%08d", Game:GetPlayer(0).Score) - local len = xlFont:GetLength(lblScore.Text) - lblScore.X = 42 + math.abs(GearWidth - len) / 2 - -end - -function Update(Delta) - local Beat = Game:GetPlayer(0).Beat - -- Executed every frame. - local beatEffect = Beat - math.floor(Beat) - Glow:SetFraction(beatEffect) - - local dLifebar = Game:GetPlayer(0).LifebarPercent / 100 - HealthBar.ScaleX - HealthBar.ScaleX = HealthBar.ScaleX + dLifebar * Delta - HealthBar.Green = Game:GetPlayer(0).LifebarPercent / 100 - HealthBar.Blue = Game:GetPlayer(0).LifebarPercent / 100 - - UpdateText() - - local SongPercentage = Game:GetPlayer(0).Time / Game:GetPlayer(0).Duration - - SongPosition.Y = ScreenHeight - ScreenHeight * SongPercentage - SongPosition.ScaleY = SongPercentage - SongPosition:SetCropByPixels(0, 4, 404 - 404 * SongPercentage, 404) - - local ScoreKeeper = Game:GetPlayer(0).Scorekeeper - local Acc = ScoreKeeper.JudgedNotes / ScoreKeeper.MaxNotes - Flair.Y = ScreenHeight - ScreenHeight * Acc - Flair.ScaleY = Acc - Flair:SetCropByPixels(0, 4, 404 - 404 * Acc, 404) - - for i=1,7 do - Explosions[i].EffectExplosion:Update(Delta) - - if KeyArray[i] then - Lightning[i].CurrentTime = 0 - end - - Lightning[i]:Update(Delta) - end -end - diff --git a/GameData/Skins/ftb2d/screenloading.lua b/GameData/Skins/ftb2d/screenloading.lua deleted file mode 100644 index b21f151c..00000000 --- a/GameData/Skins/ftb2d/screenloading.lua +++ /dev/null @@ -1,107 +0,0 @@ -game_require "utils" - -IntroDuration = 0.5 -ExitDuration = 0.5 -Centre = (ScreenHeight / 2 - 180) - -Difference = ScreenHeight - Centre - - -function UpdateIntro(frac, delta) - frac = frac * frac - nowLoading.Y = Centre * frac - nowLoading.Alpha = frac - LineL.Alpha = frac - LineR.Alpha = frac - - songTitle.Alpha = frac - songAuthor.Alpha = frac - songTitle.X = (songTitleCenter - songTitleStart) * frac + songTitleStart - songAuthor.X = (songAuthorCenter - songAuthorStart) * frac + songAuthorStart - Update(delta) -end - -function UpdateExit(frac, delta) - frac = 1 - frac - frac = 1 - frac * frac - nowLoading.Y = Centre + Difference * frac - nowLoading.Alpha = 1 - frac - - - songTitle.Alpha = 1 - frac - songAuthor.Alpha = 1 - frac - - songTitle.X = (ScreenWidth - songTitleCenter) * (frac) + songTitleCenter - songAuthor.X = (songAuthorEnd - songAuthorCenter) * (frac) + songAuthorCenter - LineL.Alpha = 1 - frac - LineR.Alpha = 1 - frac - Update(delta) -end - -function Init() - font = Fonts.TruetypeFont(GetSkinFile("ftb_font.ttf"), 100) - fontSmall = Fonts.TruetypeFont(GetSkinFile("ftb_font.ttf"), 60) - fontSmallest = Fonts.TruetypeFont(GetSkinFile("ftb_font.ttf"), 40) - - nowLoading = StringObject2D() - nowLoading.Font = font - nowLoading.Text = "Now loading..." - nowLoading.X = ScreenWidth / 2 - font:GetLength(nowLoading.Text) / 2 - nowLoading.Layer = 12 - - Engine:AddTarget(nowLoading) - - songTitle = StringObject2D() - songTitle.Font = fontSmall - - songTitle.Text = Global:GetSelectedSong().Title - songTitle.Y = ScreenHeight / 2 + 120 - Engine:AddTarget(songTitle) - - local len = fontSmall:GetLength(songTitle.Text) - songTitleStart = -len - songTitleCenter = ScreenWidth / 2 - len / 2 - - songAuthor = StringObject2D() - songAuthor.Font = fontSmallest - songAuthor.Text = Global:GetSelectedSong().Author - songAuthor.Y = songTitle.Y + 85 - - len = fontSmallest:GetLength(songAuthor.Text) - songAuthorStart = ScreenWidth - songAuthorCenter = ScreenWidth / 2 - len / 2 - songAuthorEnd = -len - Engine:AddTarget(songAuthor) - - LineL = Engine:CreateObject() - LineL.Texture = "Global/white.png" - LineL.X = 20 - LineL.Y = 0 - LineL.Width = 1 - LineL.Height = ScreenHeight - - LineR = Engine:CreateObject() - LineR.Texture = "Global/white.png" - LineR.X = ScreenWidth - 20 - LineR.Y = 0 - LineR.Width = 1 - LineR.Height = ScreenHeight -end - -function Cleanup() -end - -function Update(Delta) - LineL.Y = LineL.Y + Delta * 800 - - if LineL.Y > ScreenHeight then - LineL.Y = LineL.Y - ScreenHeight * 2 - end - - LineR.Y = LineR.Y - Delta * 800 - - if LineR.Y < -ScreenHeight then - LineR.Y = LineR.Y + ScreenHeight * 2 - end - -end diff --git a/GameData/Skins/midi-note/Down Hold Body Active (doubleres).png b/GameData/Skins/midi-note/Down Hold Body Active (doubleres).png deleted file mode 100644 index 5a5c758c..00000000 Binary files a/GameData/Skins/midi-note/Down Hold Body Active (doubleres).png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Hold Body Inactive (doubleres).png b/GameData/Skins/midi-note/Down Hold Body Inactive (doubleres).png deleted file mode 100644 index 890e8557..00000000 Binary files a/GameData/Skins/midi-note/Down Hold Body Inactive (doubleres).png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Hold BottomCap Active (doubleres).png b/GameData/Skins/midi-note/Down Hold BottomCap Active (doubleres).png deleted file mode 100644 index c48bca41..00000000 Binary files a/GameData/Skins/midi-note/Down Hold BottomCap Active (doubleres).png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Hold BottomCap Inactive (doubleres).png b/GameData/Skins/midi-note/Down Hold BottomCap Inactive (doubleres).png deleted file mode 100644 index 268a08ea..00000000 Binary files a/GameData/Skins/midi-note/Down Hold BottomCap Inactive (doubleres).png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Hold Head Active.png b/GameData/Skins/midi-note/Down Hold Head Active.png deleted file mode 100644 index 37b14d35..00000000 Binary files a/GameData/Skins/midi-note/Down Hold Head Active.png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Tap Explosion Bright.png b/GameData/Skins/midi-note/Down Tap Explosion Bright.png deleted file mode 100644 index 0b667ff4..00000000 Binary files a/GameData/Skins/midi-note/Down Tap Explosion Bright.png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Tap Explosion Dim.png b/GameData/Skins/midi-note/Down Tap Explosion Dim.png deleted file mode 100644 index 4b0c1e5d..00000000 Binary files a/GameData/Skins/midi-note/Down Tap Explosion Dim.png and /dev/null differ diff --git a/GameData/Skins/midi-note/Down Tap Fake (doubleres).png b/GameData/Skins/midi-note/Down Tap Fake (doubleres).png deleted file mode 100644 index 741e7ad2..00000000 Binary files a/GameData/Skins/midi-note/Down Tap Fake (doubleres).png and /dev/null differ diff --git a/GameData/Skins/midi-note/Left Hold BottomCap Active.png b/GameData/Skins/midi-note/Left Hold BottomCap Active.png deleted file mode 100644 index 9dc1c725..00000000 Binary files a/GameData/Skins/midi-note/Left Hold BottomCap Active.png and /dev/null differ diff --git a/GameData/Skins/midi-note/Receptor.png b/GameData/Skins/midi-note/Receptor.png deleted file mode 100644 index 91b4f6ed..00000000 Binary files a/GameData/Skins/midi-note/Receptor.png and /dev/null differ diff --git a/GameData/Skins/midi-note/ReceptorFlash.png b/GameData/Skins/midi-note/ReceptorFlash.png deleted file mode 100644 index 5055d597..00000000 Binary files a/GameData/Skins/midi-note/ReceptorFlash.png and /dev/null differ diff --git a/GameData/Skins/midi-note/Scripts/Explosions.lua b/GameData/Skins/midi-note/Scripts/Explosions.lua deleted file mode 100644 index c432897c..00000000 --- a/GameData/Skins/midi-note/Scripts/Explosions.lua +++ /dev/null @@ -1,130 +0,0 @@ -if Game:GetPlayer(0).Channels ~= 4 then - fallback_require("Scripts/Explosions") - return -end - -Explosions = {} - --- Changeable parameters -Explosions.Duration = 0.15 -Explosions.KeyupDuration = 0.1 -Explosions.DurationHoldEnd = 0.15 - -function Explosions:Init() - local Channels = Game:GetPlayer(0).Channels - print "Running midi-note explosions script" - self.Receptors = {} - self.ReceptorFlash = {} - self.HoldFlash = {} - - self.Time = {1, 1, 1, 1} - self.GearTime = {1, 1, 1, 1} - self.FlashTime = {1, 1, 1, 1} - rotTable = {90, 0, 180, 270} - - function Locate(Object, i) - Object.Centered = 1 - Object.X = self.Noteskin["Key" .. i .. "X"] - Object.Y = self.Player.JudgmentY - Object.Width = self.Noteskin["Key" .. i .. "Width"] - Object.Height = self.Noteskin.NoteHeight - Object.Rotation = rotTable[i] - end - - for i=1, 4 do - local Object = Engine:CreateObject() - Object.Texture = "Receptor.png" - Locate(Object, i) - - self.Receptors[i] = Object - end - - for i=1, 4 do - local Object = Engine:CreateObject() - Object.Texture = "ReceptorFlash.png" - Object.BlendMode = BlendAdd - Locate(Object, i) - - self.ReceptorFlash[i] = Object - end - - for i=1, 4 do - local Object = Engine:CreateObject() - Object.Texture = "Down Tap Explosion Bright.png" - Object.BlendMode = BlendAdd - Object.Alpha = 0 - Locate(Object, i) - - self.HoldFlash[i] = Object - end - - self.Sprites = {} - for i=1, Channels do - local Object = Engine:CreateObject() - Object.Texture = "Down Tap Explosion Dim.png" - Object.BlendMode = BlendAdd - Object.Lighten = 1 - Object.LightenFactor = 1 - Locate(Object, i) - - Object.Alpha = 0 - self.Sprites[i] = Object - end -end - -librd.make_new(Explosions, Explosions.Init) - -function Explosions:Run(Delta) - for Lane=1, 4 do - - self.Time[Lane] = self.Time[Lane] + Delta - self.GearTime[Lane] = self.GearTime[Lane] + Delta - self.FlashTime[Lane] = self.FlashTime[Lane] + Delta - - if self.Time[Lane] < self.Duration then - -- local newScale = lerp(Explosions.Time[Lane], 0, Explosions.Duration, 0.5, 3) - self.Sprites[Lane].Alpha = lerp(self.Time[Lane], 0, Explosions.Duration, 1, 0) - self.Sprites[Lane].LightenFactor = lerp(self.Time[Lane], 0, Explosions.Duration, 4, 0) - else - self.Sprites[Lane].Alpha = 0 - end - - if self.GearTime[Lane] < self.KeyupDuration then - local newScale = lerp(self.GearTime[Lane], 0, self.KeyupDuration, 0.5, 1) - self.Receptors[Lane]:SetScale(newScale) - self.ReceptorFlash[Lane]:SetScale(newScale) - else - self.Receptors[Lane]:SetScale(1) - self.ReceptorFlash[Lane]:SetScale(1) - end - - local l = self.HoldFlash[Lane] - if self.FlashTime[Lane] < self.DurationHoldEnd then - l.Alpha = self.FlashTime[Lane] / self.DurationHoldEnd - l.LightenFactor = 4 * (self.FlashTime[Lane] / self.DurationHoldEnd) - else - l.Alpha = 0 - end - - local Bt = math.floor(self.Player.Beat * 8) % 8 - if Bt == 0 then - self.ReceptorFlash[Lane].Alpha = 0.4 - else - self.ReceptorFlash[Lane].Alpha = 0 - end - end -end - -function Explosions:GearKeyEvent(Lane, KeyDown) - if KeyDown then - self.GearTime[Lane] = 0 - end -end - -function Explosions:OnHit(j, t, Lane, Kind, IsHold, IsHoldRelease) - if IsHoldRelease == 0 then - self.Time[Lane] = 0 - else - self.FlashTime[Lane] = 0 - end -end diff --git a/GameData/Skins/midi-note/Up Hold BottomCap Active.png b/GameData/Skins/midi-note/Up Hold BottomCap Active.png deleted file mode 100644 index 0e4e6310..00000000 Binary files a/GameData/Skins/midi-note/Up Hold BottomCap Active.png and /dev/null differ diff --git a/GameData/Skins/midi-note/_Down Tap Note 8x8 (doubleres).png b/GameData/Skins/midi-note/_Down Tap Note 8x8 (doubleres).png deleted file mode 100644 index dd8b80d2..00000000 Binary files a/GameData/Skins/midi-note/_Down Tap Note 8x8 (doubleres).png and /dev/null differ diff --git a/GameData/Skins/midi-note/custom_defs.lua b/GameData/Skins/midi-note/custom_defs.lua deleted file mode 100644 index f4c47e3f..00000000 --- a/GameData/Skins/midi-note/custom_defs.lua +++ /dev/null @@ -1,37 +0,0 @@ -require "utils" - -print "Running midi-note custom defs" -Channels4Sizes = { - 100, - 100, - 100, - 100 -} - -Channels4Positions = { - 65, - 165, - 265, - 365 -} - --- Only 4 channels supported in wafles4. -Channels4 = { - NoteHeight = 100, - GearHeight = 100, - GearStartX = 15, - Key1Width = Channels4Sizes[1], - Key2Width = Channels4Sizes[2], - Key3Width = Channels4Sizes[3], - Key4Width = Channels4Sizes[4], - Key1X = Channels4Positions[1], - Key2X = Channels4Positions[2], - Key3X = Channels4Positions[3], - Key4X = Channels4Positions[4], - GearWidth = 400, - BarlineWidth = 400, - NoteHeight = 100 -} - -Noteskin = Noteskin or {} -Noteskin[4] = Channels4 \ No newline at end of file diff --git a/GameData/Skins/midi-note/noteskin.lua b/GameData/Skins/midi-note/noteskin.lua deleted file mode 100644 index 503c248b..00000000 --- a/GameData/Skins/midi-note/noteskin.lua +++ /dev/null @@ -1,204 +0,0 @@ -function doMidiNote() - -- All notes have their origin centered. - - normalNotes = {} - - holdBodiesInactive = {} - holdBodiesActive = {} - - holdTailsInactive = {} - holdTailsActive = {} - - holdHeads = {} - - fTable = {1, 2, 3, 4, 6, 8, 16, 48} - yTable = {} - - xTable = {} - xTableFake = {} - - rotTable = {90, 0, 180, 270} - rotTableNotes = {270, 180, 0, 90} - tapSizePixels = 1024 / 8 - - function setNoteStuff(note, i, rot) - note.Width = Noteskin[Notes.Channels]['Key' .. i .. 'Width'] - note.X = Noteskin[Notes.Channels]['Key' .. i .. 'X'] - note.Height = Noteskin[Notes.Channels].NoteHeight - note.Layer = 14 - note.Lighten = 1 - note.LightenFactor = 0 - - if rot then - note.Rotation = rot[i] - end - end - - function Init() - for i=1, 8 do - yTable[fTable[i]] = { End = (i - 1) * tapSizePixels, Start = i * tapSizePixels } - end - - for i=1, 4 do - local half = 512 - local frame = (i - 1) * 128 - local nextFrame = i * 128 - xTable[i] = { Start = half + frame, End = half + nextFrame } - xTableFake[i] = { Start = frame, End = nextFrame } - end - for i=1,Notes.Channels do - normalNotes[i] = Object2D() - local note = normalNotes[i] - note.Texture = "_Down Tap Note 8x8 (doubleres).png" - setNoteStuff(note, i, rotTableNotes) - - holdBodiesInactive[i] = Object2D() - note = holdBodiesInactive[i] - note.Texture = "Down Hold Body Inactive (doubleres).png" - setNoteStuff(note, i) - - holdBodiesActive[i] = Object2D() - note = holdBodiesActive[i] - note.Texture = "Down Hold Body Active (doubleres).png" - bodyHeight = note.Height - setNoteStuff(note, i) - - holdTailsInactive[i] = Object2D() - note = holdTailsInactive[i] - note.Texture = "Down Hold BottomCap Inactive (doubleres).png" - setNoteStuff(note, i) - - holdTailsActive[i] = Object2D() - note = holdTailsActive[i] - note.Texture = "Down Hold BottomCap Active (doubleres).png" - setNoteStuff(note, i, tailsRot) - - - - holdHeads[i] = Object2D() - note = holdHeads[i] - note.Texture = "Down Hold Head Active.png" - setNoteStuff(note, i, rotTable) - end - - end - - function drawHoldTailInternal(lane, loc, frac, active_level) - local note; - note = holdTailsInactive[lane + 1] - - if active_level == 2 then - note = holdTailsActive[lane + 1] - note.LightenFactor = 1 - else - note.LightenFactor = 0 - end - - if Player.Upscroll then - note.Y = loc + Noteskin[4].NoteHeight / 2 - note.Rotation = 0 - else - note.Y = loc - Noteskin[4].NoteHeight / 2 - note.Rotation = 180 - end - - if active_level ~= 3 then - Notes:Render(note) - end - end - - function Update(delta, beat) - end - - function drawNormalInternal(lane, loc, frac, active_level) - local frame = math.floor(Player.Beat * 4) % 4 + 1 - local note = normalNotes[lane + 1] - local yvalue = yTable[frac] or yTable[48] - local xvalue = xTable[frame] or xTable[1] - note.Y = loc - - if active_level == 2 then - note.LightenFactor = 1 - else - note.LightenFactor = 0 - end - - -- colorize note - note:SetCropByPixels(xvalue.Start, xvalue.End, yvalue.Start, yvalue.End) - if active_level ~= 3 then - Notes:Render(note) - end - end - - -- 1 is enabled. 2 is being pressed. 0 is failed. 3 is succesful hit. - function drawHoldBodyInternal(lane, loc, size, active_level) - function do_draw(lane, loc, size, active_level) - local note = holdBodiesInactive[lane + 1]; - - if active_level == 2 then - note = holdBodiesActive[lane + 1] - note.LightenFactor = 1 - else - note.LightenFactor = 0 - end - - note.Y = loc - note.Height = math.abs(size) - - -- force repeat texture - note:SetCropByPixels(0, 128, 0, size) - if active_level ~= 3 then - Notes:Render(note) - end - end - - do_draw(lane, loc, size, active_level) - end - - function drawHoldHeadInternal(lane, loc, frac, active_level) - - if active_level == 2 then - local note = holdHeads[lane + 1] - note.Y = loc - if active_level ~= 3 then - Notes:Render(note) - end - else - drawNormalInternal(lane, loc, frac, active_level) - end - end - - function drawMineInternal(lane, loc, frac) - -- stub while mines are accounted in the scoring system. - end - - -- From now on, only engine variables are being set. - -- Barline - Notes.BarlineEnabled = false - Notes.BarlineOffset = Noteskin[Notes.Channels].NoteHeight / 2 - Notes.BarlineStartX = Noteskin[Notes.Channels].GearStartX - Notes.BarlineWidth = 400 - Notes.JudgmentY = Noteskin[Notes.Channels].GearHeight - Notes.DecreaseHoldSizeWhenBeingHit = 1 - Notes.DanglingHeads = false - - -- How many extra units do you require so that the whole bounding box is accounted - -- when determining whether to show this note or not. - Notes.NoteScreenSize = 50--NoteHeight / 2 - - DrawNormal = drawNormalInternal - DrawFake = drawNormalInternal - DrawLift = drawNormalInternal - DrawMine = drawMineInternal - - DrawHoldHead = drawHoldHeadInternal - DrawHoldTail = drawHoldTailInternal - DrawHoldBody = drawHoldBodyInternal -end - -if Notes.Channels == 4 then - skin_require("custom_defs") - doMidiNote() -else - fallback_require("noteskin") -end diff --git a/GameData/Skins/midi-note/override.lua b/GameData/Skins/midi-note/override.lua deleted file mode 100644 index c21183d0..00000000 --- a/GameData/Skins/midi-note/override.lua +++ /dev/null @@ -1,8 +0,0 @@ -if Game:GetPlayer(0).Channels == 4 then - Pulse = {} - Jambar = {} - - HitLightning.Enabled = false - JudgeLine = {} - Keys = {} -end \ No newline at end of file diff --git a/GameData/Skins/simple/VeraMono.ttf b/GameData/Skins/simple/VeraMono.ttf deleted file mode 100644 index 139f0b43..00000000 Binary files a/GameData/Skins/simple/VeraMono.ttf and /dev/null differ diff --git a/GameData/Skins/simple/assets/ak1.png b/GameData/Skins/simple/assets/ak1.png deleted file mode 100644 index 66ff3537..00000000 Binary files a/GameData/Skins/simple/assets/ak1.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/ak2.png b/GameData/Skins/simple/assets/ak2.png deleted file mode 100644 index 07a85897..00000000 Binary files a/GameData/Skins/simple/assets/ak2.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/bk1.png b/GameData/Skins/simple/assets/bk1.png deleted file mode 100644 index 547388df..00000000 Binary files a/GameData/Skins/simple/assets/bk1.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/bk2.png b/GameData/Skins/simple/assets/bk2.png deleted file mode 100644 index eb891ed6..00000000 Binary files a/GameData/Skins/simple/assets/bk2.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/bomb.png b/GameData/Skins/simple/assets/bomb.png deleted file mode 100644 index 42610bb6..00000000 Binary files a/GameData/Skins/simple/assets/bomb.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/btt.png b/GameData/Skins/simple/assets/btt.png deleted file mode 100644 index 92554870..00000000 Binary files a/GameData/Skins/simple/assets/btt.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/life.png b/GameData/Skins/simple/assets/life.png deleted file mode 100644 index 89aecfe8..00000000 Binary files a/GameData/Skins/simple/assets/life.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/lk1.png b/GameData/Skins/simple/assets/lk1.png deleted file mode 100644 index b2271a1a..00000000 Binary files a/GameData/Skins/simple/assets/lk1.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/lk2.png b/GameData/Skins/simple/assets/lk2.png deleted file mode 100644 index cf548438..00000000 Binary files a/GameData/Skins/simple/assets/lk2.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/ltt.png b/GameData/Skins/simple/assets/ltt.png deleted file mode 100644 index 6a0a64cb..00000000 Binary files a/GameData/Skins/simple/assets/ltt.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/nk1.png b/GameData/Skins/simple/assets/nk1.png deleted file mode 100644 index 081bf37d..00000000 Binary files a/GameData/Skins/simple/assets/nk1.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/nk2.png b/GameData/Skins/simple/assets/nk2.png deleted file mode 100644 index 80cc1348..00000000 Binary files a/GameData/Skins/simple/assets/nk2.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/ntt.png b/GameData/Skins/simple/assets/ntt.png deleted file mode 100644 index 3159c16c..00000000 Binary files a/GameData/Skins/simple/assets/ntt.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/progress_tick.png b/GameData/Skins/simple/assets/progress_tick.png deleted file mode 100644 index 6784e050..00000000 Binary files a/GameData/Skins/simple/assets/progress_tick.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/stage.png b/GameData/Skins/simple/assets/stage.png deleted file mode 100644 index 4b9168d0..00000000 Binary files a/GameData/Skins/simple/assets/stage.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/stage_back.png b/GameData/Skins/simple/assets/stage_back.png deleted file mode 100644 index 6e282c34..00000000 Binary files a/GameData/Skins/simple/assets/stage_back.png and /dev/null differ diff --git a/GameData/Skins/simple/assets/tt.png b/GameData/Skins/simple/assets/tt.png deleted file mode 100644 index d1a189e2..00000000 Binary files a/GameData/Skins/simple/assets/tt.png and /dev/null differ diff --git a/GameData/Skins/simple/custom_defs.lua b/GameData/Skins/simple/custom_defs.lua deleted file mode 100644 index e825f60b..00000000 --- a/GameData/Skins/simple/custom_defs.lua +++ /dev/null @@ -1,76 +0,0 @@ -if not Game or Game:GetPlayer(0).Channels ~= 8 then - if Notes.Channels ~= 8 then - return - end -end - -require "utils" - -GearStartX = 48 - -NoteHeight = 10 - -Channels8Sizes = { - 78, - 44, - 34, - 44, - 34, - 44, - 34, - 44 -} - -Channels8Positions = {} - -Sizeup(Channels8Positions, Channels8Sizes, 8, 3) -- 3px padding - -Channels8 = { - GearStartX = GearStartX, - GearWidth = GearWidthByChannels[8], - BarlineWidth = GearWidthByChannels[8], - BeamY = 139, - - -- Note Images - Key1Image = "tt", - Key2Image = "k1", - Key3Image = "k2", - Key4Image = "k1", - Key5Image = "k2", - Key6Image = "k1", - Key7Image = "k2", - Key8Image = "k1", - - -- Lane positions - Key1X = Channels8Positions[1], - Key2X = Channels8Positions[2], - Key3X = Channels8Positions[3], - Key4X = Channels8Positions[4], - Key5X = Channels8Positions[5], - Key6X = Channels8Positions[6], - Key7X = Channels8Positions[7], - Key8X = Channels8Positions[8], - - Key1XB = Channels8Positions[1] - Channels8Sizes[1] / 2, - Key2XB = Channels8Positions[2] - Channels8Sizes[2] / 2, - Key3XB = Channels8Positions[3] - Channels8Sizes[3] / 2, - Key4XB = Channels8Positions[4] - Channels8Sizes[4] / 2, - Key5XB = Channels8Positions[5] - Channels8Sizes[5] / 2, - Key6XB = Channels8Positions[6] - Channels8Sizes[6] / 2, - Key7XB = Channels8Positions[7] - Channels8Sizes[7] / 2, - Key8XB = Channels8Positions[8] - Channels8Sizes[8] / 2, - - -- Lane Widths - Key1Width = Channels8Sizes[1], - Key2Width = Channels8Sizes[2], - Key3Width = Channels8Sizes[3], - Key4Width = Channels8Sizes[4], - Key5Width = Channels8Sizes[5], - Key6Width = Channels8Sizes[6], - Key7Width = Channels8Sizes[7], - Key8Width = Channels8Sizes[8], - Map = {1, 2, 3, 4, 5, 6, 7, 8} -} - -Noteskin = Noteskin or {} -Noteskin[8] = Channels8 \ No newline at end of file diff --git a/GameData/Skins/simple/noteskin.lua b/GameData/Skins/simple/noteskin.lua deleted file mode 100644 index 9d104cac..00000000 --- a/GameData/Skins/simple/noteskin.lua +++ /dev/null @@ -1,108 +0,0 @@ -if Notes.Channels ~= 8 then - fallback_require("noteskin") - return -end - -Lanes = Notes.Channels - -skin_require("custom_defs") --- All notes have their origin centered. - -XR = 1360 / 1280 -YR = 768 / 720 -normalNotes = {} -holdBodies = {} - -function setNoteStuff(note, i) - note.Width = Noteskin[Lanes]['Key' .. i .. 'Width'] * XR - note.X = Noteskin[Lanes]['Key' .. i .. 'X'] * XR - note.Height = NoteHeight * YR - note.Layer = 14 - note.Lighten = 1 - note.LightenFactor = 0 -end - -function Init() - for i=1,Lanes do - normalNotes[i] = Object2D() - local note = normalNotes[i] - local image = 'assets/n' .. Noteskin[8]["Key" .. i .. "Image"] .. ".png" - - print ("Set image " .. image) - note.Texture = image - setNoteStuff(note, i) - - holdBodies[i] = Object2D() - note = holdBodies[i] - - image = 'assets/l'.. Noteskin[8]["Key" .. i .. "Image" ] .. ".png" - print ("Set image " .. image) - note.Texture = image - setNoteStuff(note, i) - end -end - -function Update(delta, beat) -end - -function drawNormalInternal(lane, loc, frac, active_level) - local note = normalNotes[lane + 1] - note.Y = loc - - if active_level ~= 3 then - Notes:Render(note) - end -end - --- 1 is enabled. 2 is being pressed. 0 is failed. 3 is succesful hit. -function drawHoldBodyInternal(lane, loc, size, active_level) - local note = holdBodies[lane + 1] - note.Y = loc - note.Height = size - - if active_level == 2 then - note.LightenFactor = 1 - note.Red = 1 - note.Green = 1 - note.Blue = 1 - elseif active_level == 1 then - note.LightenFactor = 0 - note.Red = 1 - note.Green = 1 - note.Blue = 1 - elseif active_level == 0 then - note.LightenFactor = 0 - note.Red = 0.5 - note.Green = 0.5 - note.Blue = 0.5 - end - - Notes:Render(note) -end - -function drawMineInternal(lane, loc, frac) - -- stub while mines are accounted in the scoring system. -end - --- From now on, only engine variables are being set. --- Barline -Notes.BarlineEnabled = 1 -Notes.BarlineOffset = NoteHeight / 2 -Notes.BarlineStartX = GearStartX -Notes.BarlineWidth = Noteskin[Lanes].BarlineWidth * XR -Notes.JudgmentY = ScreenHeight - 485 * YR -Notes.DecreaseHoldSizeWhenBeingHit = 1 -Notes.DanglingHeads = 1 - --- How many extra units do you require so that the whole bounding box is accounted --- when determining whether to show this note or not. -Notes.NoteScreenSize = NoteHeight / 2 - -DrawNormal = drawNormalInternal -DrawFake = drawNormalInternal -DrawLift = drawNormalInternal -DrawMine = drawMineInternal - -DrawHoldHead = drawNormalInternal -DrawHoldTail = drawNormalInternal -DrawHoldBody = drawHoldBodyInternal diff --git a/GameData/Skins/simple/screengameplay7k.lua b/GameData/Skins/simple/screengameplay7k.lua deleted file mode 100644 index be3deaab..00000000 --- a/GameData/Skins/simple/screengameplay7k.lua +++ /dev/null @@ -1,310 +0,0 @@ -if Game:GetPlayer(0).Channels ~= 8 then - fallback_require "screengameplay7k" - return -end - -game_require "TextureAtlas" -game_require "FixedObjects" - -skin_require "custom_defs" - -game_require "utils" -game_require "AnimationFunctions" - --- Set up constants for everyone - - --- Scale from 1280x720 -YR = 768 / 720 -XR = 1360 / 1280 - -GearWidth = 380 -GearHeight = GearHeightCommon - -skin_require "Scripts/ComboDisplay" -skin_require "Scripts/Judgment" -skin_require "Scripts/ScoreDisplay" - -Judgment.Scale = 0.15 -Judgment.ScaleHit = 1.2 -Judgment.ScaleMiss = 0.1 -Judgment.Position = { - y = 400, - x = GearStartX + Noteskin[8].GearWidth / 2 - 50 -} - -Judgment.Tilt = 0 -ComboDisplay.DigitWidth = 25 -ComboDisplay.DigitHeight = 25 -ComboDisplay.Position = { - x = Judgment.Position.x + 200, - y = Judgment.Position.y -} -ComboDisplay.BumpVertically = 0 -ComboDisplay.BumpHorizontally = 0 -ComboDisplay.HeightAddition = 0 -ComboDisplay.HoldBumpFactor = 1 - -ScoreDisplay.X = 500 * XR -ScoreDisplay.Y = 680 * YR -ScoreDisplay.DigitWidth = 30 -ScoreDisplay.DigitWidth = 30 - --- All of these will be loaded in the loading screen instead of --- in the main thread, and will also be unloaded at the end. -Preload = { - "assets/bomb.png", - "assets/life.png", - "assets/progress_tick.png", - "assets/stage.png", - "assets/tt.png", -} - --- Status of a lane being pressed or not. -KeyArray = {} - --- Key overlay. -KeyHold = {} - --- Judgments. -Judgments = {} - --- The keys themselves. -Key = {} - -Lightning = {} -LightingTime = 0.25 - -Bomb = {} -BombTime = 0.2 - -function GenText() - NumFont = Fonts.TruetypeFont(GetSkinFile("VeraMono.ttf"), 45 * YR) - TitleFont = Fonts.TruetypeFont(GetSkinFile("font.ttf"), 15 * YR) - BPMText = StringObject2D() - BPMText.X = 95 * XR - BPMText.Y = 660 * YR - BPMText.Font = NumFont - BPMText.Layer = 21 - - HPText = StringObject2D() - HPText.X = 315 * XR - HPText.Y = 605 * YR - HPText.Font = NumFont - HPText.Layer = 21 - - TitleText = StringObject2D() - TitleText.X = 455 * XR - TitleText.Y = 5 * YR - TitleText.Font = TitleFont - TitleText.Layer = 21 - - sng = Global:GetSelectedSong() - diff = Game:GetPlayer(0).Difficulty - if diff.Author ~= "" then - difftxt = string.format("%s by %s", diff.Name, diff.Author) - else - difftxt = string.format("%s", diff.Name) - end - TitleText.Text = string.format("%s\n%s\n%s", sng.Title, sng.Author, difftxt) - - Engine:AddTarget(BPMText) - Engine:AddTarget(HPText) - Engine:AddTarget(TitleText) -end - -function Init() - FixedObjects.XRatio = XR - FixedObjects.YRatio = YR - AutoadjustBackground({ - x = 540 * XR, - y = 84 * YR, - w = 640 * XR, - h = 480 * YR - }) - - - local tbl = { - Player = Game:GetPlayer(0), - Noteskin = Noteskin[8] - } - - combodisplay = ComboDisplay:new(tbl) - judgment = Judgment:new(tbl) - scoredisplay = ScoreDisplay:new(tbl) - IsFullCombo = false - - Channels = Game:GetPlayer(0).Channels - - GenText() - print "Creating fixed objects." - GameObjects = FixedObjects:new() - GameObjects:CreateFromCSV("simple.csv", Noteskin[8]) - Sprites = GameObjects.Sprites - - SongPosition = Sprites["tick"] - SongPosition.Centered = true - - tabletick = Sprites["tt"] - tabletick.Centered = true - - for i=1, Channels do - if i < 8 then - Key[i] = Sprites["l" .. i] - - Key[i].Alpha = 0 - Key[i].BlendMode = BlendAdd - end - - Lightning[i] = {} - - Lightning[i].CurrentTime = 0 - - Lightning[i].Object = Sprites["beam_k" .. i] - Lightning[i].Object.BlendMode = BlendAdd - Lightning[i].Object.Alpha = 1 - Lightning[i].Object.Centered = 1 - Lightning[i].Object.Y = Lightning[i].Object.Y + Lightning[i].Object.Height / 2 - - Lightning[i].Update = function(self, delta) - self.Object.ScaleX = 1 - math.pow(1 - self.CurrentTime / LightingTime, 0.75) - self.CurrentTime = max(self.CurrentTime - delta, 0) - end - - Bomb[i] = {} - Bomb[i].Object = Sprites["bomb_k" .. i] - Bomb[i].Object.Alpha = 0 - Bomb[i].Object.Centered = 1 - Bomb[i].CurrentTime = 0 - Bomb[i].Update = function(self, delta) - local lerp = self.CurrentTime / BombTime - if lerp > 0.5 then - self.Object.Alpha = lerp - else - self.Object.Alpha = 2 * lerp - end - self.Object.ScaleX = (1 - lerp) * 2 - self.Object.ScaleY = (1 - lerp) * 2 - self.CurrentTime = max(self.CurrentTime - delta, 0) - end - end - - local LifebarValue = Game:GetPlayer(0).LifebarPercent / 100 - HP = Sprites["health"] - HP.ScaleX = LifebarValue - HP:SetCropByPixels(0, 352 * LifebarValue, 0, 29) - Engine:Sort() - - Pulse = Sprites["pulse"] - Pulse.Y = 485 * YR - Pulse.Height - Pulse.Lighten = 1 - Pulse.BlendMode = BlendAdd -end - -function Cleanup() -end - -function OnFullComboEvent() - IsFullCombo = true -end - -function OnFailureEvent() - if Global:GetCurrentGaugeType(0) ~= LT_GROOVE then - DoFailAnimation() - return FailAnimation.Duration - else - -- FadeToBlack() - return 1 --SuccessAnimation.Duration - end -end - --- When 'enter' is pressed and the game starts, this function is called. -function OnActivateEvent() -end - -function HitEvent(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease, pn) - local MapLane = Noteskin[Channels].Map[Lane] - - judgment:OnHit(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease, pn) - combodisplay:OnHit(JudgmentValue, TimeOff, Lane, IsHold, IsHoldRelease, pn) - - Bomb[MapLane].CurrentTime = BombTime -end - -function MissEvent(TimeOff, Lane, IsHold, PlayerNumber) - judgment:OnMiss(TimeOff, Lane, IsHold, PlayerNumber) - combodisplay:OnMiss(TimeOff, Lane, IsHold, PlayerNumber) -end - -function KeyEvent(Key, Code, IsMouseInput) -end - -function GearKeyEvent (Lane, IsKeyDown) - local MapLane = Noteskin[Channels].Map[Lane] - KeyArray[MapLane] = IsKeyDown - - Lightning[MapLane].Object.Alpha = 1 - Lightning[MapLane].CurrentTime = LightingTime - - if MapLane > 1 then - if IsKeyDown == 1 then - Key[MapLane - 1].Alpha = 1 - else - Key[MapLane - 1].Alpha = 0 - end - end -end - --- Called when the song is over. -function OnSongFinishedEvent() - DoSuccessAnimation() - return SuccessAnimation.Duration -end - -function Update(Delta) - -- Executed every frame. - local Beat = Game:GetPlayer(0).Beat - local beatEffect = Beat - math.floor(Beat) - - local SongPercentage = Game:GetPlayer(0).Time / (Game:GetPlayer(0).Duration + 3) - - if Game:GetPlayer(0).Time < 0 then - SongPercentage = math.pow(Game:GetPlayer(0).Time / -1.5, 2) - end - - SongPosition.Y = 53 * YR + (402 - SongPosition.Height / 2) * SongPercentage * YR - - local LifebarValue = Game:GetPlayer(0).LifebarPercent / 100 - HP.ScaleX = math.ceil(LifebarValue * 50) / 50 - HP:SetCropByPixels(0, 352 * math.ceil(LifebarValue * 50) / 50, 0, 29) - - Pulse.Alpha = 1 - clamp(beatEffect, 0.5, 1) - Pulse.LightenFactor = 1 - beatEffect - - if KeyArray[1] == 0 then - tabletick.Rotation = Beat * 360 - else - tabletick.Rotation = - Beat * 360 - end - - for i=1,Channels do - if KeyArray[i] then - Lightning[i].CurrentTime = LightingTime - end - - Lightning[i]:Update(Delta) - Bomb[i]:Update(Delta) - end - - local CurrentBPM = Game:GetPlayer(0).BPM - --if CurrentBPM ~= 0 then - BPMText.Text = string.format("%03d", CurrentBPM) - --end - - HPText.Text = string.format("%03d%%", LifebarValue * 100) - - scoredisplay:Run(Delta) - judgment:Run(Delta) - combodisplay:Run(Delta) - scoredisplay:Run(Delta) -end diff --git a/GameData/Skins/simple/simple.csv b/GameData/Skins/simple/simple.csv deleted file mode 100644 index 2b6b5f86..00000000 --- a/GameData/Skins/simple/simple.csv +++ /dev/null @@ -1,29 +0,0 @@ -assets/stage_back.png,back,0,0,1280,720,1 -assets/stage.png,stage,0,0,1280,720,18 -assets/life.png,health,61,582,352,29,19 -assets/tt.png,tt,65,535,24,24,19 -assets/btt.png,beam_k1,Key1X,BeamY,Key1Width,344,14 -assets/bk1.png,beam_k2,Key2X,BeamY,Key2Width,344,14 -assets/bk2.png,beam_k3,Key3X,BeamY,Key3Width,344,14 -assets/bk1.png,beam_k4,Key4X,BeamY,Key4Width,344,14 -assets/bk2.png,beam_k5,Key5X,BeamY,Key5Width,344,14 -assets/bk1.png,beam_k6,Key6X,BeamY,Key6Width,344,14 -assets/bk2.png,beam_k7,Key7X,BeamY,Key7Width,344,14 -assets/bk1.png,beam_k8,Key8X,BeamY,Key8Width,344,14 -assets/ak1.png,l1,134,499,34,50,19 -assets/ak2.png,l2,180,491,26,45,19 -assets/ak1.png,l3,218,499,34,50,19 -assets/ak2.png,l4,264,491,26,45,19 -assets/ak1.png,l5,302,499,34,50,19 -assets/ak2.png,l6,348,491,26,45,19 -assets/ak1.png,l7,386,499,34,50,19 -assets/bomb.png,bomb_k1,Key1X,485,60,60,20 -assets/bomb.png,bomb_k2,Key2X,485,60,60,20 -assets/bomb.png,bomb_k3,Key3X,485,60,60,20 -assets/bomb.png,bomb_k4,Key4X,485,60,60,20 -assets/bomb.png,bomb_k5,Key5X,485,60,60,20 -assets/bomb.png,bomb_k6,Key6X,485,60,60,20 -assets/bomb.png,bomb_k7,Key7X,485,60,60,20 -assets/bomb.png,bomb_k8,Key8X,485,60,60,20 -assets/progress_tick.png,tick,26,62,12,18,20 -VSRG/pulse_ver.png,pulse,Key1XB,0,GearWidth,70,14 \ No newline at end of file diff --git a/GameData/Skins/wafles4/Down Tap Mine 4x2 (doubleres).png b/GameData/Skins/wafles4/Down Tap Mine 4x2 (doubleres).png deleted file mode 100644 index ebbdecf2..00000000 Binary files a/GameData/Skins/wafles4/Down Tap Mine 4x2 (doubleres).png and /dev/null differ diff --git a/GameData/Skins/wafles4/Receptor.png b/GameData/Skins/wafles4/Receptor.png deleted file mode 100644 index aaee1b90..00000000 Binary files a/GameData/Skins/wafles4/Receptor.png and /dev/null differ diff --git a/GameData/Skins/wafles4/Scripts/Explosions.lua b/GameData/Skins/wafles4/Scripts/Explosions.lua deleted file mode 100644 index ec58ecec..00000000 --- a/GameData/Skins/wafles4/Scripts/Explosions.lua +++ /dev/null @@ -1,85 +0,0 @@ -if Game:GetPlayer(0).Channels ~= 4 then - fallback_require "Scripts/Explosions" - return -end - -Explosions = {} - --- Changeable parameters -Explosions.Duration = 0.15 -Explosions.KeyupDuration = 0.1 - -function Explosions:Init() - self.Receptors = {} - - self.Time = {1, 1, 1, 1} - self.GearTime = {1, 1, 1, 1} - self.rotTable = {90, 0, 180, 270} - print "We're running W4 explosions" - - function Locate(Object, i) - Object.Centered = 1 - Object.X = self.Noteskin["Key" .. i .. "X"] - Object.Y = self.Player.JudgmentY - Object.Width = self.Noteskin["Key" .. i .. "Width"] - Object.Height = self.Noteskin.NoteHeight - Object.Rotation = self.Noteskin.rotTable[i] - end - - for i=1, 4 do - local Object = Engine:CreateObject() - Object.Texture = "Receptor.png" - Locate(Object, i) - - self.Receptors[i] = Object - end - - self.Sprites = {} - for i=1, 4 do - local Object = Engine:CreateObject() - Object.Texture = "explosion.png" - Locate(Object, i) - - Object.Alpha = 0 - Object.Layer = 18 - Object.BlendMode = BlendAdd - self.Sprites[i] = Object - end -end - -librd.make_new(Explosions, Explosions.Init) - -function Explosions:Run(Delta) - for Lane=1, 4 do - - self.Time[Lane] = self.Time[Lane] + Delta - self.GearTime[Lane] = self.GearTime[Lane] + Delta - - if self.Time[Lane] < self.Duration then - local newScale = lerp(self.Time[Lane], 0, self.Duration, 0.5, 3) - self.Sprites[Lane].Alpha = lerp(self.Time[Lane], 0, self.Duration, 1, 0) - self.Sprites[Lane].ScaleX = newScale - self.Sprites[Lane].ScaleY = newScale - else - self.Sprites[Lane].Alpha = 0 - end - - if self.GearTime[Lane] < self.KeyupDuration then - local newScale = lerp(self.GearTime[Lane], 0, self.KeyupDuration, 0.5, 1) - self.Receptors[Lane].ScaleX = newScale - self.Receptors[Lane].ScaleY = newScale - else - self.Receptors[Lane]:SetScale(1) - end - end -end - -function Explosions:GearKeyEvent(Lane, KeyDown) - if KeyDown then - self.GearTime[Lane] = 0 - end -end - -function Explosions:OnHit(a, b, Lane, Kind, IsHold, IsHoldRelease) - self.Time[Lane] = 0 -end diff --git a/GameData/Skins/wafles4/_down tap note 1x8 (doubleres).png b/GameData/Skins/wafles4/_down tap note 1x8 (doubleres).png deleted file mode 100644 index 7f7f0acc..00000000 Binary files a/GameData/Skins/wafles4/_down tap note 1x8 (doubleres).png and /dev/null differ diff --git a/GameData/Skins/wafles4/custom_defs.lua b/GameData/Skins/wafles4/custom_defs.lua deleted file mode 100644 index 4dd1546e..00000000 --- a/GameData/Skins/wafles4/custom_defs.lua +++ /dev/null @@ -1,42 +0,0 @@ -game_require "librd" - -Channels4Sizes = { - 100, - 100, - 100, - 100 -} - -Channels4Positions = { - 0, - 100, - 200, - 300 -} - -local gsx = 140 -Channels4Positions = map(function(x) return x + gsx + 50 end, Channels4Positions) - -GearWidthByChannels = {} - --- Only 4 channels supported in wafles4. -Channels4 = { - GearStartX = gsx, - NoteHeight = 100, - GearHeight = 100, - Key1Width = Channels4Sizes[1], - Key2Width = Channels4Sizes[2], - Key3Width = Channels4Sizes[3], - Key4Width = Channels4Sizes[4], - Key1X = Channels4Positions[1], - Key2X = Channels4Positions[2], - Key3X = Channels4Positions[3], - Key4X = Channels4Positions[4], - GearWidth = 400, - BarlineWidth = 400, - rotTable = {90, 0, 180, 270} -} - -print "Setting custom" -Noteskin = Noteskin or {} -Noteskin[4] = Channels4 diff --git a/GameData/Skins/wafles4/explosion.png b/GameData/Skins/wafles4/explosion.png deleted file mode 100644 index 43ea6b1d..00000000 Binary files a/GameData/Skins/wafles4/explosion.png and /dev/null differ diff --git a/GameData/Skins/wafles4/holdbodyactive.png b/GameData/Skins/wafles4/holdbodyactive.png deleted file mode 100644 index 3593f09b..00000000 Binary files a/GameData/Skins/wafles4/holdbodyactive.png and /dev/null differ diff --git a/GameData/Skins/wafles4/holdbodyinactive.png b/GameData/Skins/wafles4/holdbodyinactive.png deleted file mode 100644 index 4248f255..00000000 Binary files a/GameData/Skins/wafles4/holdbodyinactive.png and /dev/null differ diff --git a/GameData/Skins/wafles4/holdtailactive.png b/GameData/Skins/wafles4/holdtailactive.png deleted file mode 100644 index 797465f1..00000000 Binary files a/GameData/Skins/wafles4/holdtailactive.png and /dev/null differ diff --git a/GameData/Skins/wafles4/holdtailinactive.png b/GameData/Skins/wafles4/holdtailinactive.png deleted file mode 100644 index bb1d67df..00000000 Binary files a/GameData/Skins/wafles4/holdtailinactive.png and /dev/null differ diff --git a/GameData/Skins/wafles4/noteskin.lua b/GameData/Skins/wafles4/noteskin.lua deleted file mode 100644 index 7aac23d2..00000000 --- a/GameData/Skins/wafles4/noteskin.lua +++ /dev/null @@ -1,182 +0,0 @@ - --- Wrap up the noteskin on a function in case we want to call whatever else. -function DoWafles() - -- All notes have their origin centered. - - normalNotes = {} - - holdBodiesInactive = {} - holdBodiesActive = {} - - holdTailsInactive = {} - holdTailsActive = {} - - fTable = {1, 2, 3, 4, 6, 8, 16, 48} - yTable = {} - - rotTable = {270, 180, 0, 90} - tapSizePixels = 1024 / 8 - - for i=1, 8 do - yTable[fTable[i]] = { End = (i - 1) * tapSizePixels, Start = i * tapSizePixels } - end - - function Init() - - function setNoteStuff(note, i) - note.Width = Noteskin[4]['Key' .. i .. 'Width'] - note.X = Noteskin[4]['Key' .. i .. 'X'] - note.Height = Noteskin[4].NoteHeight - note.Layer = 14 - note.Lighten = 1 - note.LightenFactor = 0 - end - - for i=1,Notes.Channels do - normalNotes[i] = Object2D() - local note = normalNotes[i] - note.Texture = "_down tap note 1x8 (doubleres).png" - setNoteStuff(note, i) - - holdBodiesInactive[i] = Object2D() - note = holdBodiesInactive[i] - note.Texture = "holdbodyinactive.png" - setNoteStuff(note, i) - - holdBodiesActive[i] = Object2D() - note = holdBodiesActive[i] - note.Texture = "holdbodyactive.png" - bodyHeight = note.Height - setNoteStuff(note, i) - - holdTailsInactive[i] = Object2D() - note = holdTailsInactive[i] - note.Texture = "holdtailinactive.png" - setNoteStuff(note, i) - - holdTailsActive[i] = Object2D() - note = holdTailsActive[i] - note.Texture = "holdtailactive.png" - setNoteStuff(note, i) - end - end - - function drawHoldTailInternal(lane, loc, frac, active_level) - local note; - note = holdTailsInactive[lane + 1] - - if active_level ~= 0 then - note = holdTailsActive[lane + 1] - end - - if active_level == 2 then - note.LightenFactor = 1 - else - note.LightenFactor = 0 - end - - - if Player.Upscroll then - note.Y = loc + Noteskin[4].NoteHeight / 2 - note.Rotation = 0 - else - note.Y = loc - Noteskin[4].NoteHeight / 2 - note.Rotation = 180 - end - - if active_level ~= 3 then - Notes:Render(note) - end - end - - function Update(delta, beat) - local fraction = 1 - (beat - math.floor(beat)) - for i=1, Notes.Channels do - local note = normalNotes[i] - note.LightenFactor = fraction - end - end - - function drawNormalInternal(lane, loc, frac, active_level) - local note = normalNotes[lane + 1] - note.Y = loc - - value = yTable[frac] or yTable[48] - - -- colorize note - note:SetCropByPixels(0, 128, value.Start, value.End) - note.Rotation = rotTable[lane + 1] - if active_level ~= 3 then - Notes:Render(note) - end - end - - -- 1 is enabled. 2 is being pressed. 0 is failed. 3 is succesful hit. - function drawHoldBodyInternal(lane, loc, size, active_level) - function do_draw(lane, loc, size, active_level) - local note; - - if active_level == 0 then - note = holdBodiesInactive[lane + 1] - note.LightenFactor = 0 - else - note = holdBodiesActive[lane + 1] - end - - if active_level == 2 then - note.LightenFactor = 1 - else - note.LightenFactor = 0 - end - - note.Y = loc - note.Height = math.abs(size) - - -- force repeat texture - note:SetCropByPixels(0, 128, 0, size) - if active_level ~= 3 then - Notes:Render(note) - end - end - - do_draw(lane, loc, size, active_level) - end - - function drawHoldHeadInternal(lane, loc, frac, active_level) - drawNormalInternal(lane, loc, frac, active_level) - end - - function drawMineInternal(lane, loc, frac) - -- stub while mines are accounted in the scoring system. - end - - -- From now on, only engine variables are being set. - -- Barline - Notes.BarlineEnabled = false - Notes.BarlineOffset = Noteskin[Notes.Channels].NoteHeight / 2 - Notes.BarlineStartX = Noteskin[Notes.Channels].GearStartX - Notes.BarlineWidth = Noteskin[Notes.Channels].BarlineWidth - Notes.JudgmentY = Noteskin[Notes.Channels].GearHeight - Notes.DecreaseHoldSizeWhenBeingHit = true - Notes.DanglingHeads = false - - -- How many extra units do you require so that the whole bounding box is accounted - -- when determining whether to show this note or not. - Notes.NoteScreenSize = Noteskin[Notes.Channels].NoteHeight / 2 - - DrawNormal = drawNormalInternal - DrawFake = drawNormalInternal - DrawLift = drawNormalInternal - DrawMine = drawMineInternal - - DrawHoldHead = drawHoldHeadInternal - DrawHoldTail = drawHoldTailInternal - DrawHoldBody = drawHoldBodyInternal -end - -if Notes.Channels == 4 then - skin_require "custom_defs" - DoWafles() -else - fallback_require("noteskin") -end diff --git a/GameData/Skins/wafles4/override.lua b/GameData/Skins/wafles4/override.lua deleted file mode 100644 index dcd1b347..00000000 --- a/GameData/Skins/wafles4/override.lua +++ /dev/null @@ -1,8 +0,0 @@ -if Game:GetPlayer(0).Channels == 4 then - Pulse = {} - Jambar = {} - - JudgeLine = {} - HitLightning.Enabled = false - Keys = {} -end \ No newline at end of file diff --git a/GameData/Skins/wafles4/readme.txt b/GameData/Skins/wafles4/readme.txt deleted file mode 100644 index 105bb2fb..00000000 --- a/GameData/Skins/wafles4/readme.txt +++ /dev/null @@ -1,6 +0,0 @@ -Wafles 3 Noteskin for Stepmania 5 - -. Shapes based off of delta noteskin by Shakesoda. -Completely made from scratch by Wafles. - -This version is the raindrop port. \ No newline at end of file diff --git a/GameData/Skins/wafles4/texparams.rcf b/GameData/Skins/wafles4/texparams.rcf deleted file mode 100644 index f7d50011..00000000 --- a/GameData/Skins/wafles4/texparams.rcf +++ /dev/null @@ -1,14 +0,0 @@ -font.tga { - minfilter: linear; - maxfilter: nearest; - gen-mipmap: false; -} -loading.png { - minfilter: nearest; - maxfilter: nearest; - gen-mipmap: false; -} - -holdbodyactive.png { - wrap-t: repeat; -} \ No newline at end of file diff --git a/README.md b/README.md index 6ba9977a..e081f011 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,54 @@ raindrop ===== -raindrop is a music game project aimed to be a multi-mode rhythm game for home desktops, nevertheless mainly focused on vsrg simulation (such as iidx, o2jam, stepmania, FTB, osu!mania, etc.). -A cytus-like mode is implemented and a flexible VSRG engine is, too. It supports several chart formats and is able to convert between them, including: +raindrop is a music game project aimed to be a multi-mode rhythm game for home desktops. It is a modern VSRG engine that supports warps, scroll speeds, and much more. +Features +===== +* Negative-able BPM/stop support +* #RANDOM BMS +* Hidden/Fake notes +* OJN cover support +* Multiple timing systems + * O2Jam (Near perfect simulation, with scoring) + * Stepmania (excluding chord cohesion) + * Raindrop Standard (with the EXP3 scoring system by Joe Zeng) + * osu!mania (with the osu!mania scoring system) + * EX-score scoring system +* Multiple Gauges + * O2Jam Gauge + * Death/Groove/Survival/ExHard/Easy + * Stepmania gauge +* Hidden scrolling, user-adjustable + * Flashlight, Hidden, Sudden +* Speed classes + * CMod (As Constant) + * Common (Or Mode, based on time rather than beats! The default!) + * Max Speed + * Min Speed + * Green Number support +* Upscroll support +* Autoplay +* Random (By Lanes) +* Failure deactivation +* Lua Skinning with HTML-based librocket UI widgets +* TTF based SDF font support +* Rates, that also respect your desired target speed +* Wheel sorting +* Result Histogram, Grades +* osu!mania storyboard support +* Video playback via FFMPEG +* Previewer commands to connect to bmsone or uBMSC/iBMSC +* MP3/Ogg/WAV support +* JPEG/PNG/BMP/TGA support, with an sRGB aware color space +* Modern formats: BMSON/SM5 support +* Scroll and/or Speed support for osu!, BMS and Stepmania charts +* #PREVIEW extensions for BMS +* #MUSIC extension for BMS +* Automatic Chart Author extraction from #ARTIST tag + +Formats supported +===== * bms/bme/bml/pms (+ raindrop-specific extensions) * bmson * o2jam ojn/ojm @@ -52,6 +97,45 @@ The solution will attempt to grab boost from NuGet first time, though it can sim Building on Linux ===== -The Linux scons file as of right now is outdated. -Not only that, you've got to hunt for the dependencies yourself, even if it wasn't. -As of right now, it still is a decent base to get it to build. +To install the dependencies on Debian, run: +``` +$ sudo apt install \ + clang \ + libavcodec-dev \ + libavformat-dev \ + libboost-all-dev \ + libglew-dev \ + libglfw3-dev \ + libglu1-mesa-dev \ + libjpeg62-turbo-dev \ + liblua5.2-dev \ + libmpg123-dev \ + libogg-dev \ + libpng-dev \ + libsndfile1-dev \ + libsoxr-dev \ + libsqlite3-dev \ + libswscale-dev \ + libvorbis-dev \ + libxi-dev \ + portaudio19-dev \ + zlib1g-dev +``` + +No package exists for libRocket, so you must compile it yourself. +It requires CMake and FreeType. +``` +$ sudo apt install cmake libfreetype6-dev +$ git clone https://github.com/libRocket/libRocket.git +$ cd libRocket/Build +$ mkdir build +$ cd build +$ cmake -DBUILD_LUA_BINDINGS=ON .. +$ make +$ sudo make install +``` + +To compile raindrop, run: +``` +$ scons +``` diff --git a/SConstruct b/SConstruct index d4af3574..fe82f406 100644 --- a/SConstruct +++ b/SConstruct @@ -1,12 +1,20 @@ - -env = Environment(CPPPATH=['src']) +env = Environment(CXX='clang++') +env.Append(CPPPATH=[ + 'src/ext', + 'src', + 'lib/include/LuaBridge', + 'lib/include/lua', + 'lib/include/stb', + 'lib/include/stdex', + 'lib/include' +]) IsDebug = ARGUMENTS.get('release', 0) DisableMP3 = ARGUMENTS.get('nomp3', 0) env.Append(CPPDEFINES=['LINUX'], CXXFLAGS="-std=c++14") -if int(IsDebug) == 0: +if int(IsDebug): env.Append(CCFLAGS=["-g"]) else: env.Append(CCFLAGS=["-O2", "-DNDEBUG", "-fpermissive"]) @@ -17,9 +25,38 @@ if not int(DisableMP3): import sys -env.Append(CPPPATH=['src/ext']) - - -env.Program("dc", source = [Glob('src/*.cpp'), Glob('src/*.c'), Glob('src/ext/*.c'), Glob('src/ext/*.cpp'), Glob('src/ext/SOIL/*.c')]) - -env.Append(LIBS=['rt', 'pthread', 'lua', 'dl', 'sndfile', 'GL', 'GLEW','boost_program_options','boost_filesystem','boost_thread', 'boost_system', 'ogg', 'vorbis', 'vorbisfile', 'png', 'jpeg', 'portaudio', 'soxr', 'glfw', 'sqlite3', 'X11', 'Xrandr', 'Xxf86vm', 'Xi', 'stdc++fs', 'RocketDebugger', 'RocketControls', 'RocketControlsLua', 'RocketCoreLua', 'RocketCore']); +env.Program("dc", source=[ + Glob('src/*.cpp'), + Glob('src/ext/*.c'), + Glob('src/ext/*.cpp') +]) + +env.Append(LIBS=[ + 'avcodec', + 'avformat', + 'avutil', + 'boost_filesystem', + 'boost_program_options', + 'boost_system', + 'GL', + 'GLEW', + 'glfw', + 'jpeg', + 'lua5.2', + 'ogg', + 'png', + 'portaudio', + 'pthread', + 'RocketControls', + 'RocketControlsLua', + 'RocketCore', + 'RocketCoreLua', + 'rt', + 'sndfile', + 'soxr', + 'sqlite3', + 'stdc++fs', + 'swscale', + 'vorbis', + 'vorbisfile' +]); diff --git a/arcadedata b/arcadedata new file mode 160000 index 00000000..42262716 --- /dev/null +++ b/arcadedata @@ -0,0 +1 @@ +Subproject commit 422627169f020d343b164cf58a1010e3f8e4179c diff --git a/config.ini b/config.ini index 64bbf8d6..b3968bf8 100644 --- a/config.ini +++ b/config.ini @@ -1,142 +1,152 @@ -[Global] -Skin = simple -Widescreen = 1 -Preload = 0 -WindowWidth = 0 -WindowHeight = 0 -Fullscreen = 0 -VideoFlush = 0 -VSync = 0 -ControllerNumber = 0 -VSRGEnabled = 1 -KeyProfile4 = Profile4K -KeyProfile5 = Profile5K -KeyProfile6 = Profile6K -KeyProfile7 = Profile7K -KeyProfileSpecial8 = Profile8KS -KeyProfile8 = Profile8K -KeyProfile16 = -Keys = -KeyProfile9 = Profile9K -KeyProfile1 = Profile1K -KeyProfile10 = -KeyProfileSpecial7 = -KeyProfileSpecial4 = -KeyProfile0 = -KeyProfileSpecial12 = -KeyProfileSpecial16 = -KeyProfileSpecial6 = -OffsetKeysounded = 0 -JudgeOffsetMS = -6 -OffsetNonKeysounded = 0 -KeyProfileSpecial5 = -KeyProfileSpecial9 = -DisableKeysounds = 0 -NoFileGrouping = 0 -SeparateBySubtitle = 0 -KeyProfile12 = -InterpolateTime = 1 -UseAudioCompensationKeysounds = 0 -UseAudioCompensationNonKeysounded = 0 -Offset7K = 0 -DisableBGA = 0 -ErrorTolerance = 0 -AwaitKeysoundLoad = 0 -DisableHitsounds = 0 -WaitingTime = 0 - - -[SystemKeys] -Escape = escape -UpArrow = up -DownArrow = down -LeftArrow = left -RightArrow = right -Space = select -Enter = enter -BSPC = bspc -0 = select -1 = select2 -Z = hit -X = hit -F5 = reload -F10 = reloadconfig - - -[Keys7K] -S = 1 -D = 2 -F = 3 -32 = 4 -J = 5 -K = 6 -L = 7 -A = 8 -Z = 8 -59 = 9 -LAlt = 9 -RAlt = 10 -Q = 11 -W = 12 -E = 13 -I = 14 -O = 15 -P = 16 - - -[Profile1K] -Keys = 4 - - -[Profile4K] -Keys = 2,3,5,6 - - -[Profile5K] -Keys = 2,3,4,5,6 - - -[Profile6K] -Keys = 1,2,3,5,6,7 - - -[Profile7K] -Keys = 1,2,3,4,5,6,7 - - -[Profile8KS] -Keys = 8,1,2,3,4,5,6,7 - - -[Profile8K] -Keys = 11,12,13,9,10,14,15,16 - - -[Profile9K] -Keys = 8,1,2,3,4,5,6,7,9 - - -[Audio] -UseWasapi = 1 -UseThreadedDecoder = 1 -RequestedLatency = 0 -UseHighLatency = 0 -WasapiUseSharedMode = 0 - - -[Debug] -ImageLoader = 0 -NoteRender = 0 -OsuLoader = 0 -MeasurePosGen = 0 -OSB = 0 -SkipAudioLoad = 0 - - -[SongDirectories] -Songs = ./songs/ - - -[Speed] -SpeedClass = 0 -SpeedAmount = 200 +[Global] +Skin = +Widescreen = 1 +Preload = 1 +WindowWidth = 0 +WindowHeight = 0 +Fullscreen = 0 +VideoFlush = 0 +VSync = 0 +ControllerNumber = 0 +VSRGEnabled = 1 +KeyProfile4 = Profile4K +KeyProfile5 = Profile5K +KeyProfile6 = Profile6K +KeyProfile7 = Profile7K +KeyProfileSpecial8 = Profile8KS +KeyProfile8 = Profile8K +KeyProfile16 = +Keys = +KeyProfile9 = Profile9K +KeyProfile1 = Profile1K +KeyProfile10 = +KeyProfileSpecial7 = +KeyProfileSpecial4 = +KeyProfile0 = +KeyProfileSpecial12 = +KeyProfileSpecial16 = +KeyProfileSpecial6 = +OffsetKeysounded = 0 +JudgeOffsetMS = 0 +OffsetNonKeysounded = 0 +KeyProfileSpecial5 = +KeyProfileSpecial9 = +DisableKeysounds = 0 +NoFileGrouping = 0 +SeparateBySubtitle = 0 +KeyProfile12 = +InterpolateTime = 1 +UseAudioCompensationKeysounds = 0 +UseAudioCompensationNonKeysounded = 0 +Offset7K = 0 +DisableBGA = 0 +ErrorTolerance = 0 +AwaitKeysoundLoad = 1 +DisableHitsounds = 0 +WaitingTime = 0 + + +[SystemKeys] +Escape = escape +UpArrow = up +DownArrow = down +LeftArrow = left +RightArrow = right +Space = select +Enter = enter +BSPC = bspc +0 = select +1 = select2 +Z = hit +X = hit +F5 = reload +F10 = reloadconfig + + +[Keys7K] +S = 1 +D = 2 +F = 3 +32 = 4 +J = 5 +K = 6 +L = 7 +A = 8 +Z = 8 +59 = 9 +LAlt = 9 +RAlt = 10 +Q = 11 +W = 12 +E = 13 +I = 14 +O = 15 +P = 16 + + +[Profile1K] +Keys = 4 + + +[Profile4K] +Keys = 2,3,5,6 + + +[Profile5K] +Keys = 2,3,4,5,6 + + +[Profile6K] +Keys = 1,2,3,5,6,7 + + +[Profile7K] +Keys = 1,2,3,4,5,6,7 + + +[Profile8KS] +Keys = 8,1,2,3,4,5,6,7 + + +[Profile8K] +Keys = 11,12,13,9,10,14,15,16 + + +[Profile9K] +Keys = 8,1,2,3,4,5,6,7,9 + + +[Audio] +UseWasapi = 1 +UseThreadedDecoder = 1 +RequestedLatency = 0 +UseHighLatency = 0 +WasapiUseSharedMode = 1 + + +[Debug] +ImageLoader = 0 +NoteRender = 0 +OsuLoader = 0 +MeasurePosGen = 0 +OSB = 0 +SkipAudioLoad = 1 + + +[SongDirectories] +AAA = C:\Users\machindramon\Documents\charting +Songs = ../songs/ +BMS = C:\Users\machindramon\Documents\BMS +ARCADE = ../arcade/ + + +[Speed] +SpeedClass = 4 +SpeedAmount = 200 +GreenNumber = 0 + + +[Hidden] +Threshold = 0.5 +HiddenSize = 0.1 +FlashlightSize = 0.25 diff --git a/lib/include/boost/gil/extension/io/formats/png/reader_backend.hpp b/lib/include/boost/gil/extension/io/formats/png/reader_backend.hpp index e0957533..8af93843 100644 --- a/lib/include/boost/gil/extension/io/formats/png/reader_backend.hpp +++ b/lib/include/boost/gil/extension/io/formats/png/reader_backend.hpp @@ -505,12 +505,12 @@ struct reader_backend< Device png_charp scale_width = NULL; png_charp scale_height = NULL; - if( this->_info._valid_scale_factors = png_get_sCAL_s( get_struct() - , get_info() - , &this->_info._scale_unit - , &scale_width - , &scale_height - ) > 0 + if(( this->_info._valid_scale_factors = png_get_sCAL_s( get_struct() + , get_info() + , &this->_info._scale_unit + , &scale_width + , &scale_height + )) > 0 ) { if( scale_width ) diff --git a/lib/include/boost/gil/extension/io/png_tags.hpp b/lib/include/boost/gil/extension/io/png_tags.hpp index 169f9884..2456aca5 100644 --- a/lib/include/boost/gil/extension/io/png_tags.hpp +++ b/lib/include/boost/gil/extension/io/png_tags.hpp @@ -22,8 +22,8 @@ #ifdef BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED #ifdef BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED # error "Cannot set both symbols" -#endif BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED -#endif BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED +#endif // BOOST_GIL_IO_PNG_FIXED_POINT_SUPPORTED +#endif // BOOST_GIL_IO_PNG_FLOATING_POINT_SUPPORTED #ifndef BOOST_GIL_EXTENSION_IO_PNG_C_LIB_COMPILED_AS_CPLUSPLUS extern "C" { diff --git a/lib/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp b/lib/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp index 44c273a0..c93987fa 100644 --- a/lib/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp +++ b/lib/include/boost/gil/extension/toolbox/color_spaces/hsl.hpp @@ -66,7 +66,7 @@ struct default_color_converter_impl< rgb_t, hsl_t > bits32f min_color = (std::min)( temp_red, (std::min)( temp_green, temp_blue )); bits32f max_color = (std::max)( temp_red, (std::max)( temp_green, temp_blue )); - if( abs( min_color - max_color ) < 0.001 ) + if( std::abs( min_color - max_color ) < 0.001 ) { // rgb color is gray @@ -100,14 +100,14 @@ struct default_color_converter_impl< rgb_t, hsl_t > } // hue calculation - if( abs( max_color - temp_red ) < 0.0001f ) + if( std::abs( max_color - temp_red ) < 0.0001f ) { // max_color is red hue = ( temp_green - temp_blue ) / diff; } - else if( abs( max_color - temp_green) < 0.0001f ) + else if( std::abs( max_color - temp_green) < 0.0001f ) { // max_color is green // 2.0 + (b - r) / (maxColor - minColor); @@ -150,7 +150,7 @@ struct default_color_converter_impl bits32f red, green, blue; - if( abs( get_color( src, saturation_t() )) < 0.0001 ) + if( std::abs( get_color( src, saturation_t() )) < 0.0001 ) { // If saturation is 0, the color is a shade of gray red = get_color( src, lightness_t() ); diff --git a/lib/include/stb/stb_truetype.h b/lib/include/stb/stb_truetype.h index bfb1841f..cec24254 100644 --- a/lib/include/stb/stb_truetype.h +++ b/lib/include/stb/stb_truetype.h @@ -1,11 +1,12 @@ -// stb_truetype.h - v1.09 - public domain -// authored from 2009-2015 by Sean Barrett / RAD Game Tools +// stb_truetype.h - v1.17 - public domain +// authored from 2009-2016 by Sean Barrett / RAD Game Tools // // This library processes TrueType files: // parse files // extract glyph metrics // extract glyph shapes // render glyphs to one-channel bitmaps with antialiasing (box filter) +// render glyphs to one-channel SDF bitmaps (signed-distance field/function) // // Todo: // non-MS cmaps @@ -20,9 +21,16 @@ // // Mikko Mononen: compound shape support, more cmap formats // Tor Andersson: kerning, subpixel rendering +// Dougall Johnson: OpenType / Type 2 font handling +// +// Misc other: +// Ryan Gordon +// Simon Glass +// github:IntellectualKitty +// Imanol Celaya // // Bug/warning reports/fixes: -// "Zer" on mollyrocket (with fix) +// "Zer" on mollyrocket // Cass Everitt // stoiko (Haemimont Games) // Brian Hook @@ -44,12 +52,19 @@ // Higor Euripedes // Thomas Fields // Derek Vinyard -// -// Misc other: -// Ryan Gordon +// Cort Stratton +// github:oyvindjam // // VERSION HISTORY // +// 1.17 (2017-07-23) make more arguments const; doc fix +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; @@ -57,20 +72,12 @@ // fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?); // fixed an assert() bug in the new rasterizer // replace assert() with STBTT_assert() in new rasterizer -// 1.06 (2015-07-14) performance improvements (~35% faster on x86 and x64 on test machine) -// also more precise AA rasterizer, except if shapes overlap -// remove need for STBTT_sort -// 1.05 (2015-04-15) fix misplaced definitions for STBTT_STATIC -// 1.04 (2015-04-15) typo in example -// 1.03 (2015-04-12) STBTT_STATIC, fix memory leak in new packing, various fixes // // Full history can be found at the end of this file. // // LICENSE // -// This software is in the public domain. Where that dedication is not -// recognized, you are granted a perpetual, irrevocable license to copy, -// distribute, and modify this file as you see fit. +// See end of file for license information. // // USAGE // @@ -90,14 +97,15 @@ // Improved 3D API (more shippable): // #include "stb_rect_pack.h" -- optional, but you really want it // stbtt_PackBegin() -// stbtt_PackSetOversample() -- for improved quality on small fonts +// stbtt_PackSetOversampling() -- for improved quality on small fonts // stbtt_PackFontRanges() -- pack and renders // stbtt_PackEnd() // stbtt_GetPackedQuad() // // "Load" a font file from a memory buffer (you have to keep the buffer loaded) // stbtt_InitFont() -// stbtt_GetFontOffsetForIndex() -- use for TTC font collections +// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections +// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections // // Render a unicode codepoint to a bitmap // stbtt_GetCodepointBitmap() -- allocates and returns a bitmap @@ -107,6 +115,7 @@ // Character advance/positioning // stbtt_GetCodepointHMetrics() // stbtt_GetFontVMetrics() +// stbtt_GetFontVMetricsOS2() // stbtt_GetCodepointKernAdvance() // // Starting with version 1.06, the rasterizer was replaced with a new, @@ -404,6 +413,23 @@ int main(int arg, char **argv) #ifndef STBTT_sqrt #include #define STBTT_sqrt(x) sqrt(x) + #define STBTT_pow(x,y) pow(x,y) + #endif + + #ifndef STBTT_cos + #include + #define STBTT_cos(x) cos(x) + #define STBTT_acos(x) acos(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) + #endif + + #ifndef STBTT_fabs + #include + #define STBTT_fabs(x) fabs(x) #endif // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h @@ -424,7 +450,7 @@ int main(int arg, char **argv) #endif #ifndef STBTT_memcpy - #include + #include #define STBTT_memcpy memcpy #define STBTT_memset memset #endif @@ -450,6 +476,14 @@ int main(int arg, char **argv) extern "C" { #endif +// private structure +typedef struct +{ + unsigned char *data; + int cursor; + int size; +} stbtt__buf; + ////////////////////////////////////////////////////////////////////////////// // // TEXTURE BAKING API @@ -479,7 +513,7 @@ typedef struct float x1,y1,s1,t1; // bottom-right } stbtt_aligned_quad; -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, // same data as above +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space stbtt_aligned_quad *q, // output: quad to draw @@ -519,7 +553,7 @@ typedef struct stbrp_rect stbrp_rect; STBTT_DEF int stbtt_PackBegin(stbtt_pack_context *spc, unsigned char *pixels, int width, int height, int stride_in_bytes, int padding, void *alloc_context); // Initializes a packing context stored in the passed-in stbtt_pack_context. // Future calls using this context will pack characters into the bitmap passed -// in here: a 1-channel bitmap that is weight x height. stride_in_bytes is +// in here: a 1-channel bitmap that is width * height. stride_in_bytes is // the distance from one row to the next (or 0 to mean they are packed tightly // together). "padding" is the amount of padding to leave between each // character (normally you want '1' for bitmaps you'll use as textures with @@ -532,7 +566,7 @@ STBTT_DEF void stbtt_PackEnd (stbtt_pack_context *spc); #define STBTT_POINT_SIZE(x) (-(x)) -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_char_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range); // Creates character bitmaps from the font_index'th font found in fontdata (use // font_index=0 if you don't know what that is). It creates num_chars_in_range @@ -557,7 +591,7 @@ typedef struct unsigned char h_oversample, v_oversample; // don't set these, they're used internally } stbtt_pack_range; -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges); // Creates character bitmaps from multiple ranges of characters stored in // ranges. This will usually create a better-packed bitmap than multiple // calls to stbtt_PackFontRange. Note that you can call this multiple @@ -579,15 +613,15 @@ STBTT_DEF void stbtt_PackSetOversampling(stbtt_pack_context *spc, unsigned int h // To use with PackFontRangesGather etc., you must set it before calls // call to PackFontRangesGatherRects. -STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, // same data as above +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above int char_index, // character to display float *xpos, float *ypos, // pointers to current position in screen pixel space stbtt_aligned_quad *q, // output: quad to draw int align_to_integer); -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect *rects, int num_rects); -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects); // Calling these functions in sequence is roughly equivalent to calling // stbtt_PackFontRanges(). If you more control over the packing of multiple // fonts, or if you want to pack custom data into a font texture, take a look @@ -618,18 +652,23 @@ struct stbtt_pack_context { // // +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data); +// This function will determine the number of fonts in a font file. TrueType +// collection (.ttc) files may contain multiple fonts, while TrueType font +// (.ttf) files only contain one font. The number of fonts can be used for +// indexing with the previous function where the index is between zero and one +// less than the total fonts. If an error occurs, -1 is returned. + STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index); // Each .ttf/.ttc file may have more than one font. Each font has a sequential // index number starting from 0. Call this function to get the font offset for // a given index; it returns -1 if the index is out of range. A regular .ttf // file will only define one font and it always be at offset 0, so it will -// return '0' for index 0, and -1 for all other indices. You can just skip -// this step if you know it's that kind of font. - +// return '0' for index 0, and -1 for all other indices. // The following structure is defined publically so you can declare one on // the stack or as a global or etc, but you should treat it as opaque. -typedef struct stbtt_fontinfo +struct stbtt_fontinfo { void * userdata; unsigned char * data; // pointer to .ttf file @@ -640,7 +679,14 @@ typedef struct stbtt_fontinfo int loca,head,glyf,hhea,hmtx,kern; // table locations as offset from start of .ttf int index_map; // a cmap mapping for our chosen character encoding int indexToLocFormat; // format needed to map from glyph index to glyph -} stbtt_fontinfo; + + stbtt__buf cff; // cff font data + stbtt__buf charstrings; // the charstring index + stbtt__buf gsubrs; // global charstring subroutines index + stbtt__buf subrs; // private charstring subroutines index + stbtt__buf fontdicts; // array of font dicts + stbtt__buf fdselect; // map from glyph to fontdict +}; STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset); // Given an offset into the file that defines a font, this function builds @@ -687,6 +733,12 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in // these are expressed in unscaled coordinates, so you must multiply by // the scale factor for a given size +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap); +// analogous to GetFontVMetrics, but returns the "typographic" values from the OS/2 +// table (specific to MS/Windows TTF files). +// +// Returns 1 on success (table present), 0 on failure. + STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1); // the bounding box around all possible characters @@ -717,7 +769,8 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in enum { STBTT_vmove=1, STBTT_vline, - STBTT_vcurve + STBTT_vcurve, + STBTT_vcubic }; #endif @@ -726,7 +779,7 @@ STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, in #define stbtt_vertex_type short // can't use stbtt_int16 because that's not visible in the header file typedef struct { - stbtt_vertex_type x,y,cx,cy; + stbtt_vertex_type x,y,cx,cy,cx1,cy1; unsigned char type,padding; } stbtt_vertex; #endif @@ -780,6 +833,10 @@ STBTT_DEF void stbtt_MakeCodepointBitmapSubpixel(const stbtt_fontinfo *info, uns // same as stbtt_MakeCodepointBitmap, but you can specify a subpixel // shift for the character +STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint); +// same as stbtt_MakeCodepointBitmapSubpixel, but prefiltering +// is performed (see stbtt_PackSetOversampling) + STBTT_DEF void stbtt_GetCodepointBitmapBox(const stbtt_fontinfo *font, int codepoint, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); // get the bbox of the bitmap centered around the glyph origin; so the // bitmap width is ix1-ix0, height is iy1-iy0, and location to place @@ -797,6 +854,7 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int glyph, int *width, int *height, int *xoff, int *yoff); STBTT_DEF void stbtt_MakeGlyphBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int glyph); STBTT_DEF void stbtt_MakeGlyphBitmapSubpixel(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int glyph); +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int glyph); STBTT_DEF void stbtt_GetGlyphBitmapBox(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y, int *ix0, int *iy0, int *ix1, int *iy1); STBTT_DEF void stbtt_GetGlyphBitmapBoxSubpixel(const stbtt_fontinfo *font, int glyph, float scale_x, float scale_y,float shift_x, float shift_y, int *ix0, int *iy0, int *ix1, int *iy1); @@ -819,6 +877,64 @@ STBTT_DEF void stbtt_Rasterize(stbtt__bitmap *result, // 1-channel bitmap int invert, // if non-zero, vertically flip shape void *userdata); // context for to STBTT_MALLOC +////////////////////////////////////////////////////////////////////////////// +// +// Signed Distance Function (or Field) rendering + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata); +// frees the SDF bitmap allocated below + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); +// These functions compute a discretized SDF field for a single character, suitable for storing +// in a single-channel texture, sampling with bilinear filtering, and testing against +// larger than some threshhold to produce scalable fonts. +// info -- the font +// scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap +// glyph/codepoint -- the character to generate the SDF for +// padding -- extra "pixels" around the character which are filled with the distance to the character (not 0), +// which allows effects like bit outlines +// onedge_value -- value 0-255 to test the SDF against to reconstruct the character (i.e. the isocontour of the character) +// pixel_dist_scale -- what value the SDF should increase by when moving one SDF "pixel" away from the edge (on the 0..255 scale) +// if positive, > onedge_value is inside; if negative, < onedge_value is inside +// width,height -- output height & width of the SDF bitmap (including padding) +// xoff,yoff -- output origin of the character +// return value -- a 2D array of bytes 0..255, width*height in size +// +// pixel_dist_scale & onedge_value are a scale & bias that allows you to make +// optimal use of the limited 0..255 for your application, trading off precision +// and special effects. SDF values outside the range 0..255 are clamped to 0..255. +// +// Example: +// scale = stbtt_ScaleForPixelHeight(22) +// padding = 5 +// onedge_value = 180 +// pixel_dist_scale = 180/5.0 = 36.0 +// +// This will create an SDF bitmap in which the character is about 22 pixels +// high but the whole bitmap is about 22+5+5=32 pixels high. To produce a filled +// shape, sample the SDF at each pixel and fill the pixel if the SDF value +// is greater than or equal to 180/255. (You'll actually want to antialias, +// which is beyond the scope of this example.) Additionally, you can compute +// offset outlines (e.g. to stroke the character border inside & outside, +// or only outside). For example, to fill outside the character up to 3 SDF +// pixels, you would compare against (180-36.0*3)/255 = 72/255. The above +// choice of variables maps a range from 5 pixels outside the shape to +// 2 pixels inside the shape to 0..255; this is intended primarily for apply +// outside effects only (the interior range is needed to allow proper +// antialiasing of the font at *smaller* sizes) +// +// The function computes the SDF analytically at each SDF pixel, not by e.g. +// building a higher-res bitmap and approximating it. In theory the quality +// should be as high as possible for an SDF of this size & representation, but +// unclear if this is true in practice (perhaps building a higher-res bitmap +// and computing from that can allow drop-out prevention). +// +// The algorithm has not been optimized at all, so expect it to be slow +// if computing lots of characters or very large sizes. + + + ////////////////////////////////////////////////////////////////////////////// // // Finding the right font... @@ -942,6 +1058,158 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS #define STBTT_RASTERIZER_VERSION 2 #endif +#ifdef _MSC_VER +#define STBTT__NOTUSED(v) (void)(v) +#else +#define STBTT__NOTUSED(v) (void)sizeof(v) +#endif + +////////////////////////////////////////////////////////////////////////// +// +// stbtt__buf helpers to parse data from file +// + +static stbtt_uint8 stbtt__buf_get8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor++]; +} + +static stbtt_uint8 stbtt__buf_peek8(stbtt__buf *b) +{ + if (b->cursor >= b->size) + return 0; + return b->data[b->cursor]; +} + +static void stbtt__buf_seek(stbtt__buf *b, int o) +{ + STBTT_assert(!(o > b->size || o < 0)); + b->cursor = (o > b->size || o < 0) ? b->size : o; +} + +static void stbtt__buf_skip(stbtt__buf *b, int o) +{ + stbtt__buf_seek(b, b->cursor + o); +} + +static stbtt_uint32 stbtt__buf_get(stbtt__buf *b, int n) +{ + stbtt_uint32 v = 0; + int i; + STBTT_assert(n >= 1 && n <= 4); + for (i = 0; i < n; i++) + v = (v << 8) | stbtt__buf_get8(b); + return v; +} + +static stbtt__buf stbtt__new_buf(const void *p, size_t size) +{ + stbtt__buf r; + STBTT_assert(size < 0x40000000); + r.data = (stbtt_uint8*) p; + r.size = (int) size; + r.cursor = 0; + return r; +} + +#define stbtt__buf_get16(b) stbtt__buf_get((b), 2) +#define stbtt__buf_get32(b) stbtt__buf_get((b), 4) + +static stbtt__buf stbtt__buf_range(const stbtt__buf *b, int o, int s) +{ + stbtt__buf r = stbtt__new_buf(NULL, 0); + if (o < 0 || s < 0 || o > b->size || s > b->size - o) return r; + r.data = b->data + o; + r.size = s; + return r; +} + +static stbtt__buf stbtt__cff_get_index(stbtt__buf *b) +{ + int count, start, offsize; + start = b->cursor; + count = stbtt__buf_get16(b); + if (count) { + offsize = stbtt__buf_get8(b); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(b, offsize * count); + stbtt__buf_skip(b, stbtt__buf_get(b, offsize) - 1); + } + return stbtt__buf_range(b, start, b->cursor - start); +} + +static stbtt_uint32 stbtt__cff_int(stbtt__buf *b) +{ + int b0 = stbtt__buf_get8(b); + if (b0 >= 32 && b0 <= 246) return b0 - 139; + else if (b0 >= 247 && b0 <= 250) return (b0 - 247)*256 + stbtt__buf_get8(b) + 108; + else if (b0 >= 251 && b0 <= 254) return -(b0 - 251)*256 - stbtt__buf_get8(b) - 108; + else if (b0 == 28) return stbtt__buf_get16(b); + else if (b0 == 29) return stbtt__buf_get32(b); + STBTT_assert(0); + return 0; +} + +static void stbtt__cff_skip_operand(stbtt__buf *b) { + int v, b0 = stbtt__buf_peek8(b); + STBTT_assert(b0 >= 28); + if (b0 == 30) { + stbtt__buf_skip(b, 1); + while (b->cursor < b->size) { + v = stbtt__buf_get8(b); + if ((v & 0xF) == 0xF || (v >> 4) == 0xF) + break; + } + } else { + stbtt__cff_int(b); + } +} + +static stbtt__buf stbtt__dict_get(stbtt__buf *b, int key) +{ + stbtt__buf_seek(b, 0); + while (b->cursor < b->size) { + int start = b->cursor, end, op; + while (stbtt__buf_peek8(b) >= 28) + stbtt__cff_skip_operand(b); + end = b->cursor; + op = stbtt__buf_get8(b); + if (op == 12) op = stbtt__buf_get8(b) | 0x100; + if (op == key) return stbtt__buf_range(b, start, end-start); + } + return stbtt__buf_range(b, 0, 0); +} + +static void stbtt__dict_get_ints(stbtt__buf *b, int key, int outcount, stbtt_uint32 *out) +{ + int i; + stbtt__buf operands = stbtt__dict_get(b, key); + for (i = 0; i < outcount && operands.cursor < operands.size; i++) + out[i] = stbtt__cff_int(&operands); +} + +static int stbtt__cff_index_count(stbtt__buf *b) +{ + stbtt__buf_seek(b, 0); + return stbtt__buf_get16(b); +} + +static stbtt__buf stbtt__cff_index_get(stbtt__buf b, int i) +{ + int count, offsize, start, end; + stbtt__buf_seek(&b, 0); + count = stbtt__buf_get16(&b); + offsize = stbtt__buf_get8(&b); + STBTT_assert(i >= 0 && i < count); + STBTT_assert(offsize >= 1 && offsize <= 4); + stbtt__buf_skip(&b, i*offsize); + start = stbtt__buf_get(&b, offsize); + end = stbtt__buf_get(&b, offsize); + return stbtt__buf_range(&b, 2+(count+1)*offsize+start, end - start); +} + ////////////////////////////////////////////////////////////////////////// // // accessors to parse data from file @@ -954,32 +1222,22 @@ typedef int stbtt__test_oversample_pow2[(STBTT_MAX_OVERSAMPLE & (STBTT_MAX_OVERS #define ttCHAR(p) (* (stbtt_int8 *) (p)) #define ttFixed(p) ttLONG(p) -#if defined(STB_TRUETYPE_BIGENDIAN) && !defined(ALLOW_UNALIGNED_TRUETYPE) - - #define ttUSHORT(p) (* (stbtt_uint16 *) (p)) - #define ttSHORT(p) (* (stbtt_int16 *) (p)) - #define ttULONG(p) (* (stbtt_uint32 *) (p)) - #define ttLONG(p) (* (stbtt_int32 *) (p)) - -#else - - static stbtt_uint16 ttUSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_int16 ttSHORT(const stbtt_uint8 *p) { return p[0]*256 + p[1]; } - static stbtt_uint32 ttULONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - static stbtt_int32 ttLONG(const stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } - -#endif +static stbtt_uint16 ttUSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_int16 ttSHORT(stbtt_uint8 *p) { return p[0]*256 + p[1]; } +static stbtt_uint32 ttULONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } +static stbtt_int32 ttLONG(stbtt_uint8 *p) { return (p[0]<<24) + (p[1]<<16) + (p[2]<<8) + p[3]; } #define stbtt_tag4(p,c0,c1,c2,c3) ((p)[0] == (c0) && (p)[1] == (c1) && (p)[2] == (c2) && (p)[3] == (c3)) #define stbtt_tag(p,str) stbtt_tag4(p,str[0],str[1],str[2],str[3]) -static int stbtt__isfont(const stbtt_uint8 *font) +static int stbtt__isfont(stbtt_uint8 *font) { // check the version number if (stbtt_tag4(font, '1',0,0,0)) return 1; // TrueType 1 if (stbtt_tag(font, "typ1")) return 1; // TrueType with type 1 font -- we don't support this! if (stbtt_tag(font, "OTTO")) return 1; // OpenType with CFF if (stbtt_tag4(font, 0,1,0,0)) return 1; // OpenType 1.0 + if (stbtt_tag(font, "true")) return 1; // Apple specification for TrueType fonts return 0; } @@ -997,7 +1255,7 @@ static stbtt_uint32 stbtt__find_table(stbtt_uint8 *data, stbtt_uint32 fontstart, return 0; } -STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, int index) +static int stbtt_GetFontOffsetForIndex_internal(unsigned char *font_collection, int index) { // if it's just a font, there's only one valid index if (stbtt__isfont(font_collection)) @@ -1016,14 +1274,43 @@ STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *font_collection, return -1; } -STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, int fontstart) +static int stbtt_GetNumberOfFonts_internal(unsigned char *font_collection) +{ + // if it's just a font, there's only one valid font + if (stbtt__isfont(font_collection)) + return 1; + + // check if it's a TTC + if (stbtt_tag(font_collection, "ttcf")) { + // version 1? + if (ttULONG(font_collection+4) == 0x00010000 || ttULONG(font_collection+4) == 0x00020000) { + return ttLONG(font_collection+8); + } + } + return 0; +} + +static stbtt__buf stbtt__get_subrs(stbtt__buf cff, stbtt__buf fontdict) +{ + stbtt_uint32 subrsoff = 0, private_loc[2] = { 0, 0 }; + stbtt__buf pdict; + stbtt__dict_get_ints(&fontdict, 18, 2, private_loc); + if (!private_loc[1] || !private_loc[0]) return stbtt__new_buf(NULL, 0); + pdict = stbtt__buf_range(&cff, private_loc[1], private_loc[0]); + stbtt__dict_get_ints(&pdict, 19, 1, &subrsoff); + if (!subrsoff) return stbtt__new_buf(NULL, 0); + stbtt__buf_seek(&cff, private_loc[1]+subrsoff); + return stbtt__cff_get_index(&cff); +} + +static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) { - stbtt_uint8 *data = (stbtt_uint8 *) data2; stbtt_uint32 cmap, t; stbtt_int32 i,numTables; info->data = data; info->fontstart = fontstart; + info->cff = stbtt__new_buf(NULL, 0); cmap = stbtt__find_table(data, fontstart, "cmap"); // required info->loca = stbtt__find_table(data, fontstart, "loca"); // required @@ -1032,8 +1319,61 @@ STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data2, i info->hhea = stbtt__find_table(data, fontstart, "hhea"); // required info->hmtx = stbtt__find_table(data, fontstart, "hmtx"); // required info->kern = stbtt__find_table(data, fontstart, "kern"); // not required - if (!cmap || !info->loca || !info->head || !info->glyf || !info->hhea || !info->hmtx) + + if (!cmap || !info->head || !info->hhea || !info->hmtx) return 0; + if (info->glyf) { + // required for truetype + if (!info->loca) return 0; + } else { + // initialization for CFF / Type2 fonts (OTF) + stbtt__buf b, topdict, topdictidx; + stbtt_uint32 cstype = 2, charstrings = 0, fdarrayoff = 0, fdselectoff = 0; + stbtt_uint32 cff; + + cff = stbtt__find_table(data, fontstart, "CFF "); + if (!cff) return 0; + + info->fontdicts = stbtt__new_buf(NULL, 0); + info->fdselect = stbtt__new_buf(NULL, 0); + + // @TODO this should use size from table (not 512MB) + info->cff = stbtt__new_buf(data+cff, 512*1024*1024); + b = info->cff; + + // read the header + stbtt__buf_skip(&b, 2); + stbtt__buf_seek(&b, stbtt__buf_get8(&b)); // hdrsize + + // @TODO the name INDEX could list multiple fonts, + // but we just use the first one. + stbtt__cff_get_index(&b); // name INDEX + topdictidx = stbtt__cff_get_index(&b); + topdict = stbtt__cff_index_get(topdictidx, 0); + stbtt__cff_get_index(&b); // string INDEX + info->gsubrs = stbtt__cff_get_index(&b); + + stbtt__dict_get_ints(&topdict, 17, 1, &charstrings); + stbtt__dict_get_ints(&topdict, 0x100 | 6, 1, &cstype); + stbtt__dict_get_ints(&topdict, 0x100 | 36, 1, &fdarrayoff); + stbtt__dict_get_ints(&topdict, 0x100 | 37, 1, &fdselectoff); + info->subrs = stbtt__get_subrs(b, topdict); + + // we only support Type 2 charstrings + if (cstype != 2) return 0; + if (charstrings == 0) return 0; + + if (fdarrayoff) { + // looks like a CID font + if (!fdselectoff) return 0; + stbtt__buf_seek(&b, fdarrayoff); + info->fontdicts = stbtt__cff_get_index(&b); + info->fdselect = stbtt__buf_range(&b, fdselectoff, b.size-fdselectoff); + } + + stbtt__buf_seek(&b, charstrings); + info->charstrings = stbtt__cff_get_index(&b); + } t = stbtt__find_table(data, fontstart, "maxp"); if (t) @@ -1184,6 +1524,8 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) { int g1,g2; + STBTT_assert(!info->cff.size); + if (glyph_index >= info->numGlyphs) return -1; // glyph index out of range if (info->indexToLocFormat >= 2) return -1; // unknown index->glyph map format @@ -1198,15 +1540,21 @@ static int stbtt__GetGlyfOffset(const stbtt_fontinfo *info, int glyph_index) return g1==g2 ? -1 : g1; // if length is 0, return -1 } +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); + STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) { - int g = stbtt__GetGlyfOffset(info, glyph_index); - if (g < 0) return 0; + if (info->cff.size) { + stbtt__GetGlyphInfoT2(info, glyph_index, x0, y0, x1, y1); + } else { + int g = stbtt__GetGlyfOffset(info, glyph_index); + if (g < 0) return 0; - if (x0) *x0 = ttSHORT(info->data + g + 2); - if (y0) *y0 = ttSHORT(info->data + g + 4); - if (x1) *x1 = ttSHORT(info->data + g + 6); - if (y1) *y1 = ttSHORT(info->data + g + 8); + if (x0) *x0 = ttSHORT(info->data + g + 2); + if (y0) *y0 = ttSHORT(info->data + g + 4); + if (x1) *x1 = ttSHORT(info->data + g + 6); + if (y1) *y1 = ttSHORT(info->data + g + 8); + } return 1; } @@ -1218,7 +1566,10 @@ STBTT_DEF int stbtt_GetCodepointBox(const stbtt_fontinfo *info, int codepoint, i STBTT_DEF int stbtt_IsGlyphEmpty(const stbtt_fontinfo *info, int glyph_index) { stbtt_int16 numberOfContours; - int g = stbtt__GetGlyfOffset(info, glyph_index); + int g; + if (info->cff.size) + return stbtt__GetGlyphInfoT2(info, glyph_index, NULL, NULL, NULL, NULL) == 0; + g = stbtt__GetGlyfOffset(info, glyph_index); if (g < 0) return 1; numberOfContours = ttSHORT(info->data + g); return numberOfContours == 0; @@ -1240,7 +1591,7 @@ static int stbtt__close_shape(stbtt_vertex *vertices, int num_vertices, int was_ return num_vertices; } -STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +static int stbtt__GetGlyphShapeTT(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) { stbtt_int16 numberOfContours; stbtt_uint8 *endPtsOfContours; @@ -1466,6 +1817,416 @@ STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, s return num_vertices; } +typedef struct +{ + int bounds; + int started; + float first_x, first_y; + float x, y; + stbtt_int32 min_x, max_x, min_y, max_y; + + stbtt_vertex *pvertices; + int num_vertices; +} stbtt__csctx; + +#define STBTT__CSCTX_INIT(bounds) {bounds,0, 0,0, 0,0, 0,0,0,0, NULL, 0} + +static void stbtt__track_vertex(stbtt__csctx *c, stbtt_int32 x, stbtt_int32 y) +{ + if (x > c->max_x || !c->started) c->max_x = x; + if (y > c->max_y || !c->started) c->max_y = y; + if (x < c->min_x || !c->started) c->min_x = x; + if (y < c->min_y || !c->started) c->min_y = y; + c->started = 1; +} + +static void stbtt__csctx_v(stbtt__csctx *c, stbtt_uint8 type, stbtt_int32 x, stbtt_int32 y, stbtt_int32 cx, stbtt_int32 cy, stbtt_int32 cx1, stbtt_int32 cy1) +{ + if (c->bounds) { + stbtt__track_vertex(c, x, y); + if (type == STBTT_vcubic) { + stbtt__track_vertex(c, cx, cy); + stbtt__track_vertex(c, cx1, cy1); + } + } else { + stbtt_setvertex(&c->pvertices[c->num_vertices], type, x, y, cx, cy); + c->pvertices[c->num_vertices].cx1 = (stbtt_int16) cx1; + c->pvertices[c->num_vertices].cy1 = (stbtt_int16) cy1; + } + c->num_vertices++; +} + +static void stbtt__csctx_close_shape(stbtt__csctx *ctx) +{ + if (ctx->first_x != ctx->x || ctx->first_y != ctx->y) + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->first_x, (int)ctx->first_y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rmove_to(stbtt__csctx *ctx, float dx, float dy) +{ + stbtt__csctx_close_shape(ctx); + ctx->first_x = ctx->x = ctx->x + dx; + ctx->first_y = ctx->y = ctx->y + dy; + stbtt__csctx_v(ctx, STBTT_vmove, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rline_to(stbtt__csctx *ctx, float dx, float dy) +{ + ctx->x += dx; + ctx->y += dy; + stbtt__csctx_v(ctx, STBTT_vline, (int)ctx->x, (int)ctx->y, 0, 0, 0, 0); +} + +static void stbtt__csctx_rccurve_to(stbtt__csctx *ctx, float dx1, float dy1, float dx2, float dy2, float dx3, float dy3) +{ + float cx1 = ctx->x + dx1; + float cy1 = ctx->y + dy1; + float cx2 = cx1 + dx2; + float cy2 = cy1 + dy2; + ctx->x = cx2 + dx3; + ctx->y = cy2 + dy3; + stbtt__csctx_v(ctx, STBTT_vcubic, (int)ctx->x, (int)ctx->y, (int)cx1, (int)cy1, (int)cx2, (int)cy2); +} + +static stbtt__buf stbtt__get_subr(stbtt__buf idx, int n) +{ + int count = stbtt__cff_index_count(&idx); + int bias = 107; + if (count >= 33900) + bias = 32768; + else if (count >= 1240) + bias = 1131; + n += bias; + if (n < 0 || n >= count) + return stbtt__new_buf(NULL, 0); + return stbtt__cff_index_get(idx, n); +} + +static stbtt__buf stbtt__cid_get_glyph_subrs(const stbtt_fontinfo *info, int glyph_index) +{ + stbtt__buf fdselect = info->fdselect; + int nranges, start, end, v, fmt, fdselector = -1, i; + + stbtt__buf_seek(&fdselect, 0); + fmt = stbtt__buf_get8(&fdselect); + if (fmt == 0) { + // untested + stbtt__buf_skip(&fdselect, glyph_index); + fdselector = stbtt__buf_get8(&fdselect); + } else if (fmt == 3) { + nranges = stbtt__buf_get16(&fdselect); + start = stbtt__buf_get16(&fdselect); + for (i = 0; i < nranges; i++) { + v = stbtt__buf_get8(&fdselect); + end = stbtt__buf_get16(&fdselect); + if (glyph_index >= start && glyph_index < end) { + fdselector = v; + break; + } + start = end; + } + } + if (fdselector == -1) stbtt__new_buf(NULL, 0); + return stbtt__get_subrs(info->cff, stbtt__cff_index_get(info->fontdicts, fdselector)); +} + +static int stbtt__run_charstring(const stbtt_fontinfo *info, int glyph_index, stbtt__csctx *c) +{ + int in_header = 1, maskbits = 0, subr_stack_height = 0, sp = 0, v, i, b0; + int has_subrs = 0, clear_stack; + float s[48]; + stbtt__buf subr_stack[10], subrs = info->subrs, b; + float f; + +#define STBTT__CSERR(s) (0) + + // this currently ignores the initial width value, which isn't needed if we have hmtx + b = stbtt__cff_index_get(info->charstrings, glyph_index); + while (b.cursor < b.size) { + i = 0; + clear_stack = 1; + b0 = stbtt__buf_get8(&b); + switch (b0) { + // @TODO implement hinting + case 0x13: // hintmask + case 0x14: // cntrmask + if (in_header) + maskbits += (sp / 2); // implicit "vstem" + in_header = 0; + stbtt__buf_skip(&b, (maskbits + 7) / 8); + break; + + case 0x01: // hstem + case 0x03: // vstem + case 0x12: // hstemhm + case 0x17: // vstemhm + maskbits += (sp / 2); + break; + + case 0x15: // rmoveto + in_header = 0; + if (sp < 2) return STBTT__CSERR("rmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-2], s[sp-1]); + break; + case 0x04: // vmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("vmoveto stack"); + stbtt__csctx_rmove_to(c, 0, s[sp-1]); + break; + case 0x16: // hmoveto + in_header = 0; + if (sp < 1) return STBTT__CSERR("hmoveto stack"); + stbtt__csctx_rmove_to(c, s[sp-1], 0); + break; + + case 0x05: // rlineto + if (sp < 2) return STBTT__CSERR("rlineto stack"); + for (; i + 1 < sp; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + // hlineto/vlineto and vhcurveto/hvcurveto alternate horizontal and vertical + // starting from a different place. + + case 0x07: // vlineto + if (sp < 1) return STBTT__CSERR("vlineto stack"); + goto vlineto; + case 0x06: // hlineto + if (sp < 1) return STBTT__CSERR("hlineto stack"); + for (;;) { + if (i >= sp) break; + stbtt__csctx_rline_to(c, s[i], 0); + i++; + vlineto: + if (i >= sp) break; + stbtt__csctx_rline_to(c, 0, s[i]); + i++; + } + break; + + case 0x1F: // hvcurveto + if (sp < 4) return STBTT__CSERR("hvcurveto stack"); + goto hvcurveto; + case 0x1E: // vhcurveto + if (sp < 4) return STBTT__CSERR("vhcurveto stack"); + for (;;) { + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, 0, s[i], s[i+1], s[i+2], s[i+3], (sp - i == 5) ? s[i + 4] : 0.0f); + i += 4; + hvcurveto: + if (i + 3 >= sp) break; + stbtt__csctx_rccurve_to(c, s[i], 0, s[i+1], s[i+2], (sp - i == 5) ? s[i+4] : 0.0f, s[i+3]); + i += 4; + } + break; + + case 0x08: // rrcurveto + if (sp < 6) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x18: // rcurveline + if (sp < 8) return STBTT__CSERR("rcurveline stack"); + for (; i + 5 < sp - 2; i += 6) + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + if (i + 1 >= sp) return STBTT__CSERR("rcurveline stack"); + stbtt__csctx_rline_to(c, s[i], s[i+1]); + break; + + case 0x19: // rlinecurve + if (sp < 8) return STBTT__CSERR("rlinecurve stack"); + for (; i + 1 < sp - 6; i += 2) + stbtt__csctx_rline_to(c, s[i], s[i+1]); + if (i + 5 >= sp) return STBTT__CSERR("rlinecurve stack"); + stbtt__csctx_rccurve_to(c, s[i], s[i+1], s[i+2], s[i+3], s[i+4], s[i+5]); + break; + + case 0x1A: // vvcurveto + case 0x1B: // hhcurveto + if (sp < 4) return STBTT__CSERR("(vv|hh)curveto stack"); + f = 0.0; + if (sp & 1) { f = s[i]; i++; } + for (; i + 3 < sp; i += 4) { + if (b0 == 0x1B) + stbtt__csctx_rccurve_to(c, s[i], f, s[i+1], s[i+2], s[i+3], 0.0); + else + stbtt__csctx_rccurve_to(c, f, s[i], s[i+1], s[i+2], 0.0, s[i+3]); + f = 0.0; + } + break; + + case 0x0A: // callsubr + if (!has_subrs) { + if (info->fdselect.size) + subrs = stbtt__cid_get_glyph_subrs(info, glyph_index); + has_subrs = 1; + } + // fallthrough + case 0x1D: // callgsubr + if (sp < 1) return STBTT__CSERR("call(g|)subr stack"); + v = (int) s[--sp]; + if (subr_stack_height >= 10) return STBTT__CSERR("recursion limit"); + subr_stack[subr_stack_height++] = b; + b = stbtt__get_subr(b0 == 0x0A ? subrs : info->gsubrs, v); + if (b.size == 0) return STBTT__CSERR("subr not found"); + b.cursor = 0; + clear_stack = 0; + break; + + case 0x0B: // return + if (subr_stack_height <= 0) return STBTT__CSERR("return outside subr"); + b = subr_stack[--subr_stack_height]; + clear_stack = 0; + break; + + case 0x0E: // endchar + stbtt__csctx_close_shape(c); + return 1; + + case 0x0C: { // two-byte escape + float dx1, dx2, dx3, dx4, dx5, dx6, dy1, dy2, dy3, dy4, dy5, dy6; + float dx, dy; + int b1 = stbtt__buf_get8(&b); + switch (b1) { + // @TODO These "flex" implementations ignore the flex-depth and resolution, + // and always draw beziers. + case 0x22: // hflex + if (sp < 7) return STBTT__CSERR("hflex stack"); + dx1 = s[0]; + dx2 = s[1]; + dy2 = s[2]; + dx3 = s[3]; + dx4 = s[4]; + dx5 = s[5]; + dx6 = s[6]; + stbtt__csctx_rccurve_to(c, dx1, 0, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, -dy2, dx6, 0); + break; + + case 0x23: // flex + if (sp < 13) return STBTT__CSERR("flex stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = s[10]; + dy6 = s[11]; + //fd is s[12] + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + case 0x24: // hflex1 + if (sp < 9) return STBTT__CSERR("hflex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dx4 = s[5]; + dx5 = s[6]; + dy5 = s[7]; + dx6 = s[8]; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, 0); + stbtt__csctx_rccurve_to(c, dx4, 0, dx5, dy5, dx6, -(dy1+dy2+dy5)); + break; + + case 0x25: // flex1 + if (sp < 11) return STBTT__CSERR("flex1 stack"); + dx1 = s[0]; + dy1 = s[1]; + dx2 = s[2]; + dy2 = s[3]; + dx3 = s[4]; + dy3 = s[5]; + dx4 = s[6]; + dy4 = s[7]; + dx5 = s[8]; + dy5 = s[9]; + dx6 = dy6 = s[10]; + dx = dx1+dx2+dx3+dx4+dx5; + dy = dy1+dy2+dy3+dy4+dy5; + if (STBTT_fabs(dx) > STBTT_fabs(dy)) + dy6 = -dy; + else + dx6 = -dx; + stbtt__csctx_rccurve_to(c, dx1, dy1, dx2, dy2, dx3, dy3); + stbtt__csctx_rccurve_to(c, dx4, dy4, dx5, dy5, dx6, dy6); + break; + + default: + return STBTT__CSERR("unimplemented"); + } + } break; + + default: + if (b0 != 255 && b0 != 28 && (b0 < 32 || b0 > 254)) + return STBTT__CSERR("reserved operator"); + + // push immediate + if (b0 == 255) { + f = (float)stbtt__buf_get32(&b) / 0x10000; + } else { + stbtt__buf_skip(&b, -1); + f = (float)(stbtt_int16)stbtt__cff_int(&b); + } + if (sp >= 48) return STBTT__CSERR("push stack overflow"); + s[sp++] = f; + clear_stack = 0; + break; + } + if (clear_stack) sp = 0; + } + return STBTT__CSERR("no endchar"); + +#undef STBTT__CSERR +} + +static int stbtt__GetGlyphShapeT2(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + // runs the charstring twice, once to count and once to output (to avoid realloc) + stbtt__csctx count_ctx = STBTT__CSCTX_INIT(1); + stbtt__csctx output_ctx = STBTT__CSCTX_INIT(0); + if (stbtt__run_charstring(info, glyph_index, &count_ctx)) { + *pvertices = (stbtt_vertex*)STBTT_malloc(count_ctx.num_vertices*sizeof(stbtt_vertex), info->userdata); + output_ctx.pvertices = *pvertices; + if (stbtt__run_charstring(info, glyph_index, &output_ctx)) { + STBTT_assert(output_ctx.num_vertices == count_ctx.num_vertices); + return output_ctx.num_vertices; + } + } + *pvertices = NULL; + return 0; +} + +static int stbtt__GetGlyphInfoT2(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1) +{ + stbtt__csctx c = STBTT__CSCTX_INIT(1); + int r = stbtt__run_charstring(info, glyph_index, &c); + if (x0) { + *x0 = r ? c.min_x : 0; + *y0 = r ? c.min_y : 0; + *x1 = r ? c.max_x : 0; + *y1 = r ? c.max_y : 0; + } + return r ? c.num_vertices : 0; +} + +STBTT_DEF int stbtt_GetGlyphShape(const stbtt_fontinfo *info, int glyph_index, stbtt_vertex **pvertices) +{ + if (!info->cff.size) + return stbtt__GetGlyphShapeTT(info, glyph_index, pvertices); + else + return stbtt__GetGlyphShapeT2(info, glyph_index, pvertices); +} + STBTT_DEF void stbtt_GetGlyphHMetrics(const stbtt_fontinfo *info, int glyph_index, int *advanceWidth, int *leftSideBearing) { stbtt_uint16 numOfLongHorMetrics = ttUSHORT(info->data+info->hhea + 34); @@ -1527,6 +2288,17 @@ STBTT_DEF void stbtt_GetFontVMetrics(const stbtt_fontinfo *info, int *ascent, in if (lineGap) *lineGap = ttSHORT(info->data+info->hhea + 8); } +STBTT_DEF int stbtt_GetFontVMetricsOS2(const stbtt_fontinfo *info, int *typoAscent, int *typoDescent, int *typoLineGap) +{ + int tab = stbtt__find_table(info->data, info->fontstart, "OS/2"); + if (!tab) + return 0; + if (typoAscent ) *typoAscent = ttSHORT(info->data+tab + 68); + if (typoDescent) *typoDescent = ttSHORT(info->data+tab + 70); + if (typoLineGap) *typoLineGap = ttSHORT(info->data+tab + 72); + return 1; +} + STBTT_DEF void stbtt_GetFontBoundingBox(const stbtt_fontinfo *info, int *x0, int *y0, int *x1, int *y1) { *x0 = ttSHORT(info->data + info->head + 36); @@ -1993,7 +2765,7 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, } y_crossing += dy * (x2 - (x1+1)); - STBTT_assert(fabs(area) <= 1.01f); + STBTT_assert(STBTT_fabs(area) <= 1.01f); scanline[x2] += area + sign * (1-((x2-x2)+(x_bottom-x2))/2) * (sy1-y_crossing); @@ -2019,19 +2791,18 @@ static void stbtt__fill_active_edges_new(float *scanline, float *scanline_fill, // from the other y segment, and it might ignored as an empty segment. to avoid // that, we need to explicitly produce segments based on x positions. - // rename variables to clear pairs + // rename variables to clearly-defined pairs float y0 = y_top; float x1 = (float) (x); float x2 = (float) (x+1); float x3 = xb; float y3 = y_bottom; - float y1,y2; // x = e->x + e->dx * (y-y_top) // (y-y_top) = (x - e->x) / e->dx // y = (x - e->x) / e->dx + y_top - y1 = (x - x0) / dx + y_top; - y2 = (x+1 - x0) / dx + y_top; + float y1 = (x - x0) / dx + y_top; + float y2 = (x+1 - x0) / dx + y_top; if (x0 < x1 && x3 > x2) { // three segments descending down-right stbtt__handle_clipped_edge(scanline,x,e, x0,y0, x1,y1); @@ -2071,6 +2842,8 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int y,j=0, i; float scanline_data[129], *scanline, *scanline2; + STBTT__NOTUSED(vsubsample); + if (result->w > 64) scanline = (float *) STBTT_malloc((result->w*2+1) * sizeof(float), userdata); else @@ -2129,7 +2902,7 @@ static void stbtt__rasterize_sorted_edges(stbtt__bitmap *result, stbtt__edge *e, int m; sum += scanline2[i]; k = scanline[i] + sum; - k = (float) fabs(k)*255 + 0.5f; + k = (float) STBTT_fabs(k)*255 + 0.5f; m = (int) k; if (m > 255) m = 255; result->pixels[j*result->stride + i] = (unsigned char) m; @@ -2334,6 +3107,48 @@ static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x return 1; } +static void stbtt__tesselate_cubic(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float x3, float y3, float objspace_flatness_squared, int n) +{ + // @TODO this "flatness" calculation is just made-up nonsense that seems to work well enough + float dx0 = x1-x0; + float dy0 = y1-y0; + float dx1 = x2-x1; + float dy1 = y2-y1; + float dx2 = x3-x2; + float dy2 = y3-y2; + float dx = x3-x0; + float dy = y3-y0; + float longlen = (float) (STBTT_sqrt(dx0*dx0+dy0*dy0)+STBTT_sqrt(dx1*dx1+dy1*dy1)+STBTT_sqrt(dx2*dx2+dy2*dy2)); + float shortlen = (float) STBTT_sqrt(dx*dx+dy*dy); + float flatness_squared = longlen*longlen-shortlen*shortlen; + + if (n > 16) // 65536 segments on one curve better be enough! + return; + + if (flatness_squared > objspace_flatness_squared) { + float x01 = (x0+x1)/2; + float y01 = (y0+y1)/2; + float x12 = (x1+x2)/2; + float y12 = (y1+y2)/2; + float x23 = (x2+x3)/2; + float y23 = (y2+y3)/2; + + float xa = (x01+x12)/2; + float ya = (y01+y12)/2; + float xb = (x12+x23)/2; + float yb = (y12+y23)/2; + + float mx = (xa+xb)/2; + float my = (ya+yb)/2; + + stbtt__tesselate_cubic(points, num_points, x0,y0, x01,y01, xa,ya, mx,my, objspace_flatness_squared,n+1); + stbtt__tesselate_cubic(points, num_points, mx,my, xb,yb, x23,y23, x3,y3, objspace_flatness_squared,n+1); + } else { + stbtt__add_point(points, *num_points,x3,y3); + *num_points = *num_points+1; + } +} + // returns number of contours static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, float objspace_flatness, int **contour_lengths, int *num_contours, void *userdata) { @@ -2390,6 +3205,14 @@ static stbtt__point *stbtt_FlattenCurves(stbtt_vertex *vertices, int num_verts, objspace_flatness_squared, 0); x = vertices[i].x, y = vertices[i].y; break; + case STBTT_vcubic: + stbtt__tesselate_cubic(points, &num_points, x,y, + vertices[i].cx, vertices[i].cy, + vertices[i].cx1, vertices[i].cy1, + vertices[i].x, vertices[i].y, + objspace_flatness_squared, 0); + x = vertices[i].x, y = vertices[i].y; + break; } } (*contour_lengths)[n] = num_points - start; @@ -2430,7 +3253,10 @@ STBTT_DEF unsigned char *stbtt_GetGlyphBitmapSubpixel(const stbtt_fontinfo *info if (scale_x == 0) scale_x = scale_y; if (scale_y == 0) { - if (scale_x == 0) return NULL; + if (scale_x == 0) { + STBTT_free(vertices, info->userdata); + return NULL; + } scale_y = scale_x; } @@ -2513,7 +3339,7 @@ STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned ch // // This is SUPER-CRAPPY packing to keep source code small -STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) +static int stbtt_BakeFontBitmap_internal(unsigned char *data, int offset, // font location (use offset=0 for plain .ttf) float pixel_height, // height of font in pixels unsigned char *pixels, int pw, int ph, // bitmap to be filled in int first_char, int num_chars, // characters to bake @@ -2559,11 +3385,11 @@ STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, // fo return bottom_y; } -STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) +STBTT_DEF void stbtt_GetBakedQuad(const stbtt_bakedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int opengl_fillrule) { float d3d_bias = opengl_fillrule ? 0 : -0.5f; float ipw = 1.0f / pw, iph = 1.0f / ph; - stbtt_bakedchar *b = chardata + char_index; + const stbtt_bakedchar *b = chardata + char_index; int round_x = STBTT_ifloor((*xpos + b->xoff) + 0.5f); int round_y = STBTT_ifloor((*ypos + b->yoff) + 0.5f); @@ -2586,11 +3412,6 @@ STBTT_DEF void stbtt_GetBakedQuad(stbtt_bakedchar *chardata, int pw, int ph, int // #ifndef STB_RECT_PACK_VERSION -#ifdef _MSC_VER -#define STBTT__NOTUSED(v) (void)(v) -#else -#define STBTT__NOTUSED(v) (void)sizeof(v) -#endif typedef int stbrp_coord; @@ -2848,7 +3669,7 @@ static float stbtt__oversample_shift(int oversample) } // rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k; @@ -2876,8 +3697,31 @@ STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, stbtt_fon return k; } +STBTT_DEF void stbtt_MakeGlyphBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int prefilter_x, int prefilter_y, float *sub_x, float *sub_y, int glyph) +{ + stbtt_MakeGlyphBitmapSubpixel(info, + output, + out_w - (prefilter_x - 1), + out_h - (prefilter_y - 1), + out_stride, + scale_x, + scale_y, + shift_x, + shift_y, + glyph); + + if (prefilter_x > 1) + stbtt__h_prefilter(output, out_w, out_h, out_stride, prefilter_x); + + if (prefilter_y > 1) + stbtt__v_prefilter(output, out_w, out_h, out_stride, prefilter_y); + + *sub_x = stbtt__oversample_shift(prefilter_x); + *sub_y = stbtt__oversample_shift(prefilter_y); +} + // rects array must be big enough to accommodate all characters in the given ranges -STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) +STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) { int i,j,k, return_value = 1; @@ -2964,7 +3808,7 @@ STBTT_DEF void stbtt_PackFontRangesPackRects(stbtt_pack_context *spc, stbrp_rect stbrp_pack_rects((stbrp_context *) spc->pack_info, rects, num_rects); } -STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) +STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, stbtt_pack_range *ranges, int num_ranges) { stbtt_fontinfo info; int i,j,n, return_value = 1; @@ -3000,7 +3844,7 @@ STBTT_DEF int stbtt_PackFontRanges(stbtt_pack_context *spc, unsigned char *fontd return return_value; } -STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontdata, int font_index, float font_size, +STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, const unsigned char *fontdata, int font_index, float font_size, int first_unicode_codepoint_in_range, int num_chars_in_range, stbtt_packedchar *chardata_for_range) { stbtt_pack_range range; @@ -3012,10 +3856,10 @@ STBTT_DEF int stbtt_PackFontRange(stbtt_pack_context *spc, unsigned char *fontda return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); } -STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) +STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) { float ipw = 1.0f / pw, iph = 1.0f / ph; - stbtt_packedchar *b = chardata + char_index; + const stbtt_packedchar *b = chardata + char_index; if (align_to_integer) { float x = (float) STBTT_ifloor((*xpos + b->xoff) + 0.5f); @@ -3039,6 +3883,387 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i *xpos += b->xadvance; } +////////////////////////////////////////////////////////////////////////////// +// +// sdf computation +// + +#define STBTT_min(a,b) ((a) < (b) ? (a) : (b)) +#define STBTT_max(a,b) ((a) < (b) ? (b) : (a)) + +static int stbtt__ray_intersect_bezier(float orig[2], float ray[2], float q0[2], float q1[2], float q2[2], float hits[2][2]) +{ + float q0perp = q0[1]*ray[0] - q0[0]*ray[1]; + float q1perp = q1[1]*ray[0] - q1[0]*ray[1]; + float q2perp = q2[1]*ray[0] - q2[0]*ray[1]; + float roperp = orig[1]*ray[0] - orig[0]*ray[1]; + + float a = q0perp - 2*q1perp + q2perp; + float b = q1perp - q0perp; + float c = q0perp - roperp; + + float s0 = 0., s1 = 0.; + int num_s = 0; + + if (a != 0.0) { + float discr = b*b - a*c; + if (discr > 0.0) { + float rcpna = -1 / a; + float d = (float) sqrt(discr); + s0 = (b+d) * rcpna; + s1 = (b-d) * rcpna; + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + if (d > 0.0 && s1 >= 0.0 && s1 <= 1.0) { + if (num_s == 0) s0 = s1; + ++num_s; + } + } + } else { + // 2*b*s + c = 0 + // s = -c / (2*b) + s0 = c / (-2 * b); + if (s0 >= 0.0 && s0 <= 1.0) + num_s = 1; + } + + if (num_s == 0) + return 0; + else { + float rcp_len2 = 1 / (ray[0]*ray[0] + ray[1]*ray[1]); + float rayn_x = ray[0] * rcp_len2, rayn_y = ray[1] * rcp_len2; + + float q0d = q0[0]*rayn_x + q0[1]*rayn_y; + float q1d = q1[0]*rayn_x + q1[1]*rayn_y; + float q2d = q2[0]*rayn_x + q2[1]*rayn_y; + float rod = orig[0]*rayn_x + orig[1]*rayn_y; + + float q10d = q1d - q0d; + float q20d = q2d - q0d; + float q0rd = q0d - rod; + + hits[0][0] = q0rd + s0*(2.0f - 2.0f*s0)*q10d + s0*s0*q20d; + hits[0][1] = a*s0+b; + + if (num_s > 1) { + hits[1][0] = q0rd + s1*(2.0f - 2.0f*s1)*q10d + s1*s1*q20d; + hits[1][1] = a*s1+b; + return 2; + } else { + return 1; + } + } +} + +static int equal(float *a, float *b) +{ + return (a[0] == b[0] && a[1] == b[1]); +} + +static int stbtt__compute_crossings_x(float x, float y, int nverts, stbtt_vertex *verts) +{ + int i; + float orig[2], ray[2] = { 1, 0 }; + float y_frac; + int winding = 0; + + orig[0] = x; + orig[1] = y; + + // make sure y never passes through a vertex of the shape + y_frac = (float) fmod(y, 1.0f); + if (y_frac < 0.01f) + y += 0.01f; + else if (y_frac > 0.99f) + y -= 0.01f; + orig[1] = y; + + // test a ray from (-infinity,y) to (x,y) + for (i=0; i < nverts; ++i) { + if (verts[i].type == STBTT_vline) { + int x0 = (int) verts[i-1].x, y0 = (int) verts[i-1].y; + int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } + if (verts[i].type == STBTT_vcurve) { + int x0 = (int) verts[i-1].x , y0 = (int) verts[i-1].y ; + int x1 = (int) verts[i ].cx, y1 = (int) verts[i ].cy; + int x2 = (int) verts[i ].x , y2 = (int) verts[i ].y ; + int ax = STBTT_min(x0,STBTT_min(x1,x2)), ay = STBTT_min(y0,STBTT_min(y1,y2)); + int by = STBTT_max(y0,STBTT_max(y1,y2)); + if (y > ay && y < by && x > ax) { + float q0[2],q1[2],q2[2]; + float hits[2][2]; + q0[0] = (float)x0; + q0[1] = (float)y0; + q1[0] = (float)x1; + q1[1] = (float)y1; + q2[0] = (float)x2; + q2[1] = (float)y2; + if (equal(q0,q1) || equal(q1,q2)) { + x0 = (int)verts[i-1].x; + y0 = (int)verts[i-1].y; + x1 = (int)verts[i ].x; + y1 = (int)verts[i ].y; + if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { + float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; + if (x_inter < x) + winding += (y0 < y1) ? 1 : -1; + } + } else { + int num_hits = stbtt__ray_intersect_bezier(orig, ray, q0, q1, q2, hits); + if (num_hits >= 1) + if (hits[0][0] < 0) + winding += (hits[0][1] < 0 ? -1 : 1); + if (num_hits >= 2) + if (hits[1][0] < 0) + winding += (hits[1][1] < 0 ? -1 : 1); + } + } + } + } + return winding; +} + +static float stbtt__cuberoot( float x ) +{ + if (x<0) + return -(float) STBTT_pow(-x,1.0f/3.0f); + else + return (float) STBTT_pow( x,1.0f/3.0f); +} + +// x^3 + c*x^2 + b*x + a = 0 +static int stbtt__solve_cubic(float a, float b, float c, float* r) +{ + float s = -a / 3; + float p = b - a*a / 3; + float q = a * (2*a*a - 9*b) / 27 + c; + float p3 = p*p*p; + float d = q*q + 4*p3 / 27; + if (d >= 0) { + float z = (float) STBTT_sqrt(d); + float u = (-q + z) / 2; + float v = (-q - z) / 2; + u = stbtt__cuberoot(u); + v = stbtt__cuberoot(v); + r[0] = s + u + v; + return 1; + } else { + float u = (float) STBTT_sqrt(-p/3); + float v = (float) STBTT_acos(-STBTT_sqrt(-27/p3) * q / 2) / 3; // p3 must be negative, since d is negative + float m = (float) STBTT_cos(v); + float n = (float) STBTT_cos(v-3.141592/2)*1.732050808f; + r[0] = s + u * 2 * m; + r[1] = s - u * (m + n); + r[2] = s - u * (m - n); + + //STBTT_assert( STBTT_fabs(((r[0]+a)*r[0]+b)*r[0]+c) < 0.05f); // these asserts may not be safe at all scales, though they're in bezier t parameter units so maybe? + //STBTT_assert( STBTT_fabs(((r[1]+a)*r[1]+b)*r[1]+c) < 0.05f); + //STBTT_assert( STBTT_fabs(((r[2]+a)*r[2]+b)*r[2]+c) < 0.05f); + return 3; + } +} + +STBTT_DEF unsigned char * stbtt_GetGlyphSDF(const stbtt_fontinfo *info, float scale, int glyph, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + float scale_x = scale, scale_y = scale; + int ix0,iy0,ix1,iy1; + int w,h; + unsigned char *data; + + // if one scale is 0, use same scale for both + if (scale_x == 0) scale_x = scale_y; + if (scale_y == 0) { + if (scale_x == 0) return NULL; // if both scales are 0, return NULL + scale_y = scale_x; + } + + stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); + + // if empty, return NULL + if (ix0 == ix1 || iy0 == iy1) + return NULL; + + ix0 -= padding; + iy0 -= padding; + ix1 += padding; + iy1 += padding; + + w = (ix1 - ix0); + h = (iy1 - iy0); + + if (width ) *width = w; + if (height) *height = h; + if (xoff ) *xoff = ix0; + if (yoff ) *yoff = iy0; + + // invert for y-downwards bitmaps + scale_y = -scale_y; + + { + int x,y,i,j; + float *precompute; + stbtt_vertex *verts; + int num_verts = stbtt_GetGlyphShape(info, glyph, &verts); + data = (unsigned char *) STBTT_malloc(w * h, info->userdata); + precompute = (float *) STBTT_malloc(num_verts * sizeof(float), info->userdata); + + for (i=0,j=num_verts-1; i < num_verts; j=i++) { + if (verts[i].type == STBTT_vline) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + float x1 = verts[j].x*scale_x, y1 = verts[j].y*scale_y; + float dist = (float) STBTT_sqrt((x1-x0)*(x1-x0) + (y1-y0)*(y1-y0)); + precompute[i] = (dist == 0) ? 0.0f : 1.0f / dist; + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[j].x *scale_x, y2 = verts[j].y *scale_y; + float x1 = verts[i].cx*scale_x, y1 = verts[i].cy*scale_y; + float x0 = verts[i].x *scale_x, y0 = verts[i].y *scale_y; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float len2 = bx*bx + by*by; + if (len2 != 0.0f) + precompute[i] = 1.0f / (bx*bx + by*by); + else + precompute[i] = 0.0f; + } else + precompute[i] = 0.0f; + } + + for (y=iy0; y < iy1; ++y) { + for (x=ix0; x < ix1; ++x) { + float val; + float min_dist = 999999.0f; + float sx = (float) x + 0.5f; + float sy = (float) y + 0.5f; + float x_gspace = (sx / scale_x); + float y_gspace = (sy / scale_y); + + int winding = stbtt__compute_crossings_x(x_gspace, y_gspace, num_verts, verts); // @OPTIMIZE: this could just be a rasterization, but needs to be line vs. non-tesselated curves so a new path + + for (i=0; i < num_verts; ++i) { + float x0 = verts[i].x*scale_x, y0 = verts[i].y*scale_y; + + // check against every point here rather than inside line/curve primitives -- @TODO: wrong if multiple 'moves' in a row produce a garbage point, and given culling, probably more efficient to do within line/curve + float dist2 = (x0-sx)*(x0-sx) + (y0-sy)*(y0-sy); + if (dist2 < min_dist*min_dist) + min_dist = (float) STBTT_sqrt(dist2); + + if (verts[i].type == STBTT_vline) { + float x1 = verts[i-1].x*scale_x, y1 = verts[i-1].y*scale_y; + + // coarse culling against bbox + //if (sx > STBTT_min(x0,x1)-min_dist && sx < STBTT_max(x0,x1)+min_dist && + // sy > STBTT_min(y0,y1)-min_dist && sy < STBTT_max(y0,y1)+min_dist) + float dist = (float) STBTT_fabs((x1-x0)*(y0-sy) - (y1-y0)*(x0-sx)) * precompute[i]; + STBTT_assert(i != 0); + if (dist < min_dist) { + // check position along line + // x' = x0 + t*(x1-x0), y' = y0 + t*(y1-y0) + // minimize (x'-sx)*(x'-sx)+(y'-sy)*(y'-sy) + float dx = x1-x0, dy = y1-y0; + float px = x0-sx, py = y0-sy; + // minimize (px+t*dx)^2 + (py+t*dy)^2 = px*px + 2*px*dx*t + t^2*dx*dx + py*py + 2*py*dy*t + t^2*dy*dy + // derivative: 2*px*dx + 2*py*dy + (2*dx*dx+2*dy*dy)*t, set to 0 and solve + float t = -(px*dx + py*dy) / (dx*dx + dy*dy); + if (t >= 0.0f && t <= 1.0f) + min_dist = dist; + } + } else if (verts[i].type == STBTT_vcurve) { + float x2 = verts[i-1].x *scale_x, y2 = verts[i-1].y *scale_y; + float x1 = verts[i ].cx*scale_x, y1 = verts[i ].cy*scale_y; + float box_x0 = STBTT_min(STBTT_min(x0,x1),x2); + float box_y0 = STBTT_min(STBTT_min(y0,y1),y2); + float box_x1 = STBTT_max(STBTT_max(x0,x1),x2); + float box_y1 = STBTT_max(STBTT_max(y0,y1),y2); + // coarse culling against bbox to avoid computing cubic unnecessarily + if (sx > box_x0-min_dist && sx < box_x1+min_dist && sy > box_y0-min_dist && sy < box_y1+min_dist) { + int num=0; + float ax = x1-x0, ay = y1-y0; + float bx = x0 - 2*x1 + x2, by = y0 - 2*y1 + y2; + float mx = x0 - sx, my = y0 - sy; + float res[3],px,py,t,it; + float a_inv = precompute[i]; + if (a_inv == 0.0) { // if a_inv is 0, it's 2nd degree so use quadratic formula + float a = 3*(ax*bx + ay*by); + float b = 2*(ax*ax + ay*ay) + (mx*bx+my*by); + float c = mx*ax+my*ay; + if (a == 0.0) { // if a is 0, it's linear + if (b != 0.0) { + res[num++] = -c/b; + } + } else { + float discriminant = b*b - 4*a*c; + if (discriminant < 0) + num = 0; + else { + float root = (float) STBTT_sqrt(discriminant); + res[0] = (-b - root)/(2*a); + res[1] = (-b + root)/(2*a); + num = 2; // don't bother distinguishing 1-solution case, as code below will still work + } + } + } else { + float b = 3*(ax*bx + ay*by) * a_inv; // could precompute this as it doesn't depend on sample point + float c = (2*(ax*ax + ay*ay) + (mx*bx+my*by)) * a_inv; + float d = (mx*ax+my*ay) * a_inv; + num = stbtt__solve_cubic(b, c, d, res); + } + if (num >= 1 && res[0] >= 0.0f && res[0] <= 1.0f) { + t = res[0], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 2 && res[1] >= 0.0f && res[1] <= 1.0f) { + t = res[1], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + if (num >= 3 && res[2] >= 0.0f && res[2] <= 1.0f) { + t = res[2], it = 1.0f - t; + px = it*it*x0 + 2*t*it*x1 + t*t*x2; + py = it*it*y0 + 2*t*it*y1 + t*t*y2; + dist2 = (px-sx)*(px-sx) + (py-sy)*(py-sy); + if (dist2 < min_dist * min_dist) + min_dist = (float) STBTT_sqrt(dist2); + } + } + } + } + if (winding == 0) + min_dist = -min_dist; // if outside the shape, value is negative + val = onedge_value + pixel_dist_scale * min_dist; + if (val < 0) + val = 0; + else if (val > 255) + val = 255; + data[(y-iy0)*w+(x-ix0)] = (unsigned char) val; + } + } + STBTT_free(precompute, info->userdata); + STBTT_free(verts, info->userdata); + } + return data; +} + +STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) +{ + return stbtt_GetGlyphSDF(info, scale, stbtt_FindGlyphIndex(info, codepoint), padding, onedge_value, pixel_dist_scale, width, height, xoff, yoff); +} + +STBTT_DEF void stbtt_FreeSDF(unsigned char *bitmap, void *userdata) +{ + STBTT_free(bitmap, userdata); +} ////////////////////////////////////////////////////////////////////////////// // @@ -3046,7 +4271,7 @@ STBTT_DEF void stbtt_GetPackedQuad(stbtt_packedchar *chardata, int pw, int ph, i // // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string -static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 *s1, stbtt_int32 len1, const stbtt_uint8 *s2, stbtt_int32 len2) +static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) { stbtt_int32 i=0; @@ -3085,9 +4310,9 @@ static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(const stbtt_uint8 return i; } -STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) { - return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((const stbtt_uint8*) s1, len1, (const stbtt_uint8*) s2, len2); + return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); } // returns results in whatever encoding you request... but note that 2-byte encodings @@ -3143,7 +4368,7 @@ static int stbtt__matchpair(stbtt_uint8 *fc, stbtt_uint32 nm, stbtt_uint8 *name, return 1; } else if (matchlen < nlen && name[matchlen] == ' ') { ++matchlen; - if (stbtt_CompareUTF8toUTF16_bigendian((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) + if (stbtt_CompareUTF8toUTF16_bigendian_internal((char*) (name+matchlen), nlen-matchlen, (char*)(fc+stringOffset+off),slen)) return 1; } } else { @@ -3189,7 +4414,7 @@ static int stbtt__matches(stbtt_uint8 *fc, stbtt_uint32 offset, stbtt_uint8 *nam return 0; } -STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const char *name_utf8, stbtt_int32 flags) +static int stbtt_FindMatchingFont_internal(unsigned char *font_collection, char *name_utf8, stbtt_int32 flags) { stbtt_int32 i; for (i=0;;++i) { @@ -3200,11 +4425,61 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const } } +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +STBTT_DEF int stbtt_BakeFontBitmap(const unsigned char *data, int offset, + float pixel_height, unsigned char *pixels, int pw, int ph, + int first_char, int num_chars, stbtt_bakedchar *chardata) +{ + return stbtt_BakeFontBitmap_internal((unsigned char *) data, offset, pixel_height, pixels, pw, ph, first_char, num_chars, chardata); +} + +STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) +{ + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); +} + +STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) +{ + return stbtt_GetNumberOfFonts_internal((unsigned char *) data); +} + +STBTT_DEF int stbtt_InitFont(stbtt_fontinfo *info, const unsigned char *data, int offset) +{ + return stbtt_InitFont_internal(info, (unsigned char *) data, offset); +} + +STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *fontdata, const char *name, int flags) +{ + return stbtt_FindMatchingFont_internal((unsigned char *) fontdata, (char *) name, flags); +} + +STBTT_DEF int stbtt_CompareUTF8toUTF16_bigendian(const char *s1, int len1, const char *s2, int len2) +{ + return stbtt_CompareUTF8toUTF16_bigendian_internal((char *) s1, len1, (char *) s2, len2); +} + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + #endif // STB_TRUETYPE_IMPLEMENTATION // FULL VERSION HISTORY // +// 1.16 (2017-07-12) SDF support +// 1.15 (2017-03-03) make more arguments const +// 1.14 (2017-01-16) num-fonts-in-TTC function +// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts +// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual +// 1.11 (2016-04-02) fix unused-variable warning +// 1.10 (2016-04-02) allow user-defined fabs() replacement +// fix memory leak if fontsize=0.0 +// fix warning from duplicate typedef // 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use alloc userdata for PackFontRanges // 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges // 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints; @@ -3247,3 +4522,45 @@ STBTT_DEF int stbtt_FindMatchingFont(const unsigned char *font_collection, const // 0.2 (2009-03-11) Fix unsigned/signed char warnings // 0.1 (2009-03-09) First public release // + +/* +------------------------------------------------------------------------------ +This software is available under 2 licenses -- choose whichever you prefer. +------------------------------------------------------------------------------ +ALTERNATIVE A - MIT License +Copyright (c) 2017 Sean Barrett +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +------------------------------------------------------------------------------ +ALTERNATIVE B - Public Domain (www.unlicense.org) +This is free and unencumbered software released into the public domain. +Anyone is free to copy, modify, publish, use, compile, sell, or distribute this +software, either in source code form or as a compiled binary, for any purpose, +commercial or non-commercial, and by any means. +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and successors. We intend this dedication to be an +overt act of relinquishment in perpetuity of all present and future rights to +this software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +------------------------------------------------------------------------------ +*/ diff --git a/msvc/packages.config b/msvc/packages.config index e4a801d0..e930e701 100644 --- a/msvc/packages.config +++ b/msvc/packages.config @@ -1,4 +1,5 @@  - + + \ No newline at end of file diff --git a/msvc/raindrop.vcxproj b/msvc/raindrop.vcxproj index a8d7735b..ad0cd6b9 100644 --- a/msvc/raindrop.vcxproj +++ b/msvc/raindrop.vcxproj @@ -1,5 +1,5 @@  - + Debug @@ -16,20 +16,21 @@ raindrop .\ true + 10.0.15063.0 Application true Unicode - v140 + v141 Application false true Unicode - v140_xp + v141 @@ -45,7 +46,7 @@ $(ProjectDir)..\ PATH=%PATH%;$(ProjectDir)..\lib\$(Configuration) WindowsLocalDebugger - ..\lib\include;..\lib\include\LuaBridge;..\lib\include\lua;..\lib\include\zlib;..\lib\include\libmpg123;..\lib\include\libsndfile;..\lib\include\sqlite3;..\lib\include\stb;..\lib\include\soxr;..\lib\include\libjpeg-turbo;..\lib\include\stdex;..\lib\include\libpng;..\lib\include\portaudio;$(WindowsSDK_IncludePath)\include;$(WindowsSdkDir)\include;$(FrameworkSDKDir)\include;$(VCInstallDir)include;$(UniversalCRT_IncludePath) + ..\lib\include;..\lib\include\LuaBridge;..\lib\include\lua;..\lib\include\zlib;..\lib\include\libmpg123;..\lib\include\libsndfile;..\lib\include\sqlite3;..\lib\include\stb;..\lib\include\soxr;..\lib\include\libjpeg-turbo;..\lib\include\stdex;..\lib\include\libpng;..\lib\include\portaudio;$(WindowsSDK_IncludePath)\include;$(WindowsSdkDir)\include;$(FrameworkSDKDir)\include;$(VCInstallDir)include;$(UniversalCRT_IncludePath);$(VC_IncludePath) ..\lib\$(Configuration);..\lib\Any;$(LibraryPath) @@ -53,7 +54,7 @@ $(ProjectDir)..\ PATH=%PATH%;$(ProjectDir)..\lib\$(Configuration) WindowsLocalDebugger - ..\lib\include;..\lib\include\LuaBridge;..\lib\include\lua;..\lib\include\zlib;..\lib\include\libmpg123;..\lib\include\libsndfile;..\lib\include\sqlite3;..\lib\include\stb;..\lib\include\soxr;..\lib\include\libjpeg-turbo;..\lib\include\stdex;..\lib\include\libpng;..\lib\include\portaudio;$(WindowsSDK_IncludePath)\include;$(WindowsSdkDir)\include;$(FrameworkSDKDir)\include;$(VCInstallDir)include;$(UniversalCRT_IncludePath) + ..\lib\include;..\lib\include\LuaBridge;..\lib\include\lua;..\lib\include\zlib;..\lib\include\libmpg123;..\lib\include\libsndfile;..\lib\include\sqlite3;..\lib\include\stb;..\lib\include\soxr;..\lib\include\libjpeg-turbo;..\lib\include\stdex;..\lib\include\libpng;..\lib\include\portaudio;$(WindowsSDK_IncludePath)\include;$(WindowsSdkDir)\include;$(FrameworkSDKDir)\include;$(VCInstallDir)include;$(UniversalCRT_IncludePath);$(VC_IncludePath) ..\lib\$(Configuration);..\lib\Any;$(LibraryPath) @@ -162,6 +163,7 @@ NotUsing + @@ -186,12 +188,14 @@ + + @@ -209,6 +213,7 @@ + @@ -255,6 +260,7 @@ + @@ -264,6 +270,7 @@ + @@ -275,6 +282,7 @@ + @@ -328,12 +336,14 @@ - + + - This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - + + \ No newline at end of file diff --git a/msvc/raindrop.vcxproj.filters b/msvc/raindrop.vcxproj.filters index 467ff36b..5167f597 100644 --- a/msvc/raindrop.vcxproj.filters +++ b/msvc/raindrop.vcxproj.filters @@ -85,9 +85,6 @@ Source Files\game global\game status - - Source Files\game global\game status - Source Files\game global\game status @@ -247,6 +244,21 @@ Source Files\game global\screens + + Source Files\backend\render\fonts + + + Source Files\backend\render\fonts + + + Source Files\backend\render\textures + + + Source Files\vsrg\gameplay + + + Source Files\game global\song interface + @@ -441,6 +453,15 @@ Header Files\game global + + Header Files\backend\render + + + Header Files\backend\render + + + Header Files\backend\render\textures + diff --git a/sjis.txt b/sjis.txt new file mode 100644 index 00000000..5e9a79e6 --- /dev/null +++ b/sjis.txt @@ -0,0 +1 @@ + !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[¥]^_`abcdefghijklmnopqrstuvwxyz{|}~ 、。,.・:;?!゛゜´`¨^ ̄_ヽヾゝゞ〃仝々〆〇ー―‐/\〜∥|…‥‘’“”()〔〕[]{}〈〉《》「」『』【】+−±×÷=≠<>≦≧∞∴♂♀°′″℃¥$¢£%#&*@§☆★○●◎◇◆□■△▲▽▼※〒→←↑↓〓∈∋⊆⊇⊂⊃∪∩∧∨¬⇒⇔∀∃∠⊥⌒∂∇≡≒≪≫√∽∝∵∫∬ʼn♯♭♪†‡¶◯0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzぁあぃいぅうぇえぉおかがきぎくぐけげこごさざしじすずせぜそぞただちぢっつづてでとどなにぬねのはばぱひびぴふぶぷへべぺほぼぽまみむめもゃやゅゆょよらりるれろゎわゐゑをんァアィイゥウェエォオカガキギクグケゲコゴサザシジスズセゼソゾタダチヂッツヅテデトドナニヌネノハバパヒビピフブプヘベペホボポマミムメモャヤュユョヨラリルレロヮワヰヱヲンヴヵヶΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩαβγδεζηθικλμνξοπρστυφχψωАБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя─│┌┐┘└├┬┤┴┼━┃┏┓┛┗┣┳┫┻╋┠┯┨┷┿┝┰┥┸╂亜唖娃阿哀愛挨姶逢葵茜穐悪握渥旭葦芦鯵梓圧斡扱宛姐虻飴絢綾鮎或粟袷安庵按暗案闇鞍杏以伊位依偉囲夷委威尉惟意慰易椅為畏異移維緯胃萎衣謂違遺医井亥域育郁磯一壱溢逸稲茨芋鰯允印咽員因姻引飲淫胤蔭院陰隠韻吋右宇烏羽迂雨卯鵜窺丑碓臼渦嘘唄欝蔚鰻姥厩浦瓜閏噂云運雲荏餌叡営嬰影映曳栄永泳洩瑛盈穎頴英衛詠鋭液疫益駅悦謁越閲榎厭円園堰奄宴延怨掩援沿演炎焔煙燕猿縁艶苑薗遠鉛鴛塩於汚甥凹央奥往応押旺横欧殴王翁襖鴬鴎黄岡沖荻億屋憶臆桶牡乙俺卸恩温穏音下化仮何伽価佳加可嘉夏嫁家寡科暇果架歌河火珂禍禾稼箇花苛茄荷華菓蝦課嘩貨迦過霞蚊俄峨我牙画臥芽蛾賀雅餓駕介会解回塊壊廻快怪悔恢懐戒拐改魁晦械海灰界皆絵芥蟹開階貝凱劾外咳害崖慨概涯碍蓋街該鎧骸浬馨蛙垣柿蛎鈎劃嚇各廓拡撹格核殻獲確穫覚角赫較郭閣隔革学岳楽額顎掛笠樫橿梶鰍潟割喝恰括活渇滑葛褐轄且鰹叶椛樺鞄株兜竃蒲釜鎌噛鴨栢茅萱粥刈苅瓦乾侃冠寒刊勘勧巻喚堪姦完官寛干幹患感慣憾換敢柑桓棺款歓汗漢澗潅環甘監看竿管簡緩缶翰肝艦莞観諌貫還鑑間閑関陥韓館舘丸含岸巌玩癌眼岩翫贋雁頑顔願企伎危喜器基奇嬉寄岐希幾忌揮机旗既期棋棄機帰毅気汽畿祈季稀紀徽規記貴起軌輝飢騎鬼亀偽儀妓宜戯技擬欺犠疑祇義蟻誼議掬菊鞠吉吃喫桔橘詰砧杵黍却客脚虐逆丘久仇休及吸宮弓急救朽求汲泣灸球究窮笈級糾給旧牛去居巨拒拠挙渠虚許距鋸漁禦魚亨享京供侠僑兇競共凶協匡卿叫喬境峡強彊怯恐恭挟教橋況狂狭矯胸脅興蕎郷鏡響饗驚仰凝尭暁業局曲極玉桐粁僅勤均巾錦斤欣欽琴禁禽筋緊芹菌衿襟謹近金吟銀九倶句区狗玖矩苦躯駆駈駒具愚虞喰空偶寓遇隅串櫛釧屑屈掘窟沓靴轡窪熊隈粂栗繰桑鍬勲君薫訓群軍郡卦袈祁係傾刑兄啓圭珪型契形径恵慶慧憩掲携敬景桂渓畦稽系経継繋罫茎荊蛍計詣警軽頚鶏芸迎鯨劇戟撃激隙桁傑欠決潔穴結血訣月件倹倦健兼券剣喧圏堅嫌建憲懸拳捲検権牽犬献研硯絹県肩見謙賢軒遣鍵険顕験鹸元原厳幻弦減源玄現絃舷言諺限乎個古呼固姑孤己庫弧戸故枯湖狐糊袴股胡菰虎誇跨鈷雇顧鼓五互伍午呉吾娯後御悟梧檎瑚碁語誤護醐乞鯉交佼侯候倖光公功効勾厚口向后喉坑垢好孔孝宏工巧巷幸広庚康弘恒慌抗拘控攻昂晃更杭校梗構江洪浩港溝甲皇硬稿糠紅紘絞綱耕考肯肱腔膏航荒行衡講貢購郊酵鉱砿鋼閤降項香高鴻剛劫号合壕拷濠豪轟麹克刻告国穀酷鵠黒獄漉腰甑忽惚骨狛込此頃今困坤墾婚恨懇昏昆根梱混痕紺艮魂些佐叉唆嵯左差査沙瑳砂詐鎖裟坐座挫債催再最哉塞妻宰彩才採栽歳済災采犀砕砦祭斎細菜裁載際剤在材罪財冴坂阪堺榊肴咲崎埼碕鷺作削咋搾昨朔柵窄策索錯桜鮭笹匙冊刷察拶撮擦札殺薩雑皐鯖捌錆鮫皿晒三傘参山惨撒散桟燦珊産算纂蚕讃賛酸餐斬暫残仕仔伺使刺司史嗣四士始姉姿子屍市師志思指支孜斯施旨枝止死氏獅祉私糸紙紫肢脂至視詞詩試誌諮資賜雌飼歯事似侍児字寺慈持時次滋治爾璽痔磁示而耳自蒔辞汐鹿式識鴫竺軸宍雫七叱執失嫉室悉湿漆疾質実蔀篠偲柴芝屡蕊縞舎写射捨赦斜煮社紗者謝車遮蛇邪借勺尺杓灼爵酌釈錫若寂弱惹主取守手朱殊狩珠種腫趣酒首儒受呪寿授樹綬需囚収周宗就州修愁拾洲秀秋終繍習臭舟蒐衆襲讐蹴輯週酋酬集醜什住充十従戎柔汁渋獣縦重銃叔夙宿淑祝縮粛塾熟出術述俊峻春瞬竣舜駿准循旬楯殉淳準潤盾純巡遵醇順処初所暑曙渚庶緒署書薯藷諸助叙女序徐恕鋤除傷償勝匠升召哨商唱嘗奨妾娼宵将小少尚庄床廠彰承抄招掌捷昇昌昭晶松梢樟樵沼消渉湘焼焦照症省硝礁祥称章笑粧紹肖菖蒋蕉衝裳訟証詔詳象賞醤鉦鍾鐘障鞘上丈丞乗冗剰城場壌嬢常情擾条杖浄状畳穣蒸譲醸錠嘱埴飾拭植殖燭織職色触食蝕辱尻伸信侵唇娠寝審心慎振新晋森榛浸深申疹真神秦紳臣芯薪親診身辛進針震人仁刃塵壬尋甚尽腎訊迅陣靭笥諏須酢図厨逗吹垂帥推水炊睡粋翠衰遂酔錐錘随瑞髄崇嵩数枢趨雛据杉椙菅頗雀裾澄摺寸世瀬畝是凄制勢姓征性成政整星晴棲栖正清牲生盛精聖声製西誠誓請逝醒青静斉税脆隻席惜戚斥昔析石積籍績脊責赤跡蹟碩切拙接摂折設窃節説雪絶舌蝉仙先千占宣専尖川戦扇撰栓栴泉浅洗染潜煎煽旋穿箭線繊羨腺舛船薦詮賎践選遷銭銑閃鮮前善漸然全禅繕膳糎噌塑岨措曾曽楚狙疏疎礎祖租粗素組蘇訴阻遡鼠僧創双叢倉喪壮奏爽宋層匝惣想捜掃挿掻操早曹巣槍槽漕燥争痩相窓糟総綜聡草荘葬蒼藻装走送遭鎗霜騒像増憎臓蔵贈造促側則即息捉束測足速俗属賊族続卒袖其揃存孫尊損村遜他多太汰詑唾堕妥惰打柁舵楕陀駄騨体堆対耐岱帯待怠態戴替泰滞胎腿苔袋貸退逮隊黛鯛代台大第醍題鷹滝瀧卓啄宅托択拓沢濯琢託鐸濁諾茸凧蛸只叩但達辰奪脱巽竪辿棚谷狸鱈樽誰丹単嘆坦担探旦歎淡湛炭短端箪綻耽胆蛋誕鍛団壇弾断暖檀段男談値知地弛恥智池痴稚置致蜘遅馳築畜竹筑蓄逐秩窒茶嫡着中仲宙忠抽昼柱注虫衷註酎鋳駐樗瀦猪苧著貯丁兆凋喋寵帖帳庁弔張彫徴懲挑暢朝潮牒町眺聴脹腸蝶調諜超跳銚長頂鳥勅捗直朕沈珍賃鎮陳津墜椎槌追鎚痛通塚栂掴槻佃漬柘辻蔦綴鍔椿潰坪壷嬬紬爪吊釣鶴亭低停偵剃貞呈堤定帝底庭廷弟悌抵挺提梯汀碇禎程締艇訂諦蹄逓邸鄭釘鼎泥摘擢敵滴的笛適鏑溺哲徹撤轍迭鉄典填天展店添纏甜貼転顛点伝殿澱田電兎吐堵塗妬屠徒斗杜渡登菟賭途都鍍砥砺努度土奴怒倒党冬凍刀唐塔塘套宕島嶋悼投搭東桃梼棟盗淘湯涛灯燈当痘祷等答筒糖統到董蕩藤討謄豆踏逃透鐙陶頭騰闘働動同堂導憧撞洞瞳童胴萄道銅峠鴇匿得徳涜特督禿篤毒独読栃橡凸突椴届鳶苫寅酉瀞噸屯惇敦沌豚遁頓呑曇鈍奈那内乍凪薙謎灘捺鍋楢馴縄畷南楠軟難汝二尼弐迩匂賑肉虹廿日乳入如尿韮任妊忍認濡禰祢寧葱猫熱年念捻撚燃粘乃廼之埜嚢悩濃納能脳膿農覗蚤巴把播覇杷波派琶破婆罵芭馬俳廃拝排敗杯盃牌背肺輩配倍培媒梅楳煤狽買売賠陪這蝿秤矧萩伯剥博拍柏泊白箔粕舶薄迫曝漠爆縛莫駁麦函箱硲箸肇筈櫨幡肌畑畠八鉢溌発醗髪伐罰抜筏閥鳩噺塙蛤隼伴判半反叛帆搬斑板氾汎版犯班畔繁般藩販範釆煩頒飯挽晩番盤磐蕃蛮匪卑否妃庇彼悲扉批披斐比泌疲皮碑秘緋罷肥被誹費避非飛樋簸備尾微枇毘琵眉美鼻柊稗匹疋髭彦膝菱肘弼必畢筆逼桧姫媛紐百謬俵彪標氷漂瓢票表評豹廟描病秒苗錨鋲蒜蛭鰭品彬斌浜瀕貧賓頻敏瓶不付埠夫婦富冨布府怖扶敷斧普浮父符腐膚芙譜負賦赴阜附侮撫武舞葡蕪部封楓風葺蕗伏副復幅服福腹複覆淵弗払沸仏物鮒分吻噴墳憤扮焚奮粉糞紛雰文聞丙併兵塀幣平弊柄並蔽閉陛米頁僻壁癖碧別瞥蔑箆偏変片篇編辺返遍便勉娩弁鞭保舗鋪圃捕歩甫補輔穂募墓慕戊暮母簿菩倣俸包呆報奉宝峰峯崩庖抱捧放方朋法泡烹砲縫胞芳萌蓬蜂褒訪豊邦鋒飽鳳鵬乏亡傍剖坊妨帽忘忙房暴望某棒冒紡肪膨謀貌貿鉾防吠頬北僕卜墨撲朴牧睦穆釦勃没殆堀幌奔本翻凡盆摩磨魔麻埋妹昧枚毎哩槙幕膜枕鮪柾鱒桝亦俣又抹末沫迄侭繭麿万慢満漫蔓味未魅巳箕岬密蜜湊蓑稔脈妙粍民眠務夢無牟矛霧鵡椋婿娘冥名命明盟迷銘鳴姪牝滅免棉綿緬面麺摸模茂妄孟毛猛盲網耗蒙儲木黙目杢勿餅尤戻籾貰問悶紋門匁也冶夜爺耶野弥矢厄役約薬訳躍靖柳薮鑓愉愈油癒諭輸唯佑優勇友宥幽悠憂揖有柚湧涌猶猷由祐裕誘遊邑郵雄融夕予余与誉輿預傭幼妖容庸揚揺擁曜楊様洋溶熔用窯羊耀葉蓉要謡踊遥陽養慾抑欲沃浴翌翼淀羅螺裸来莱頼雷洛絡落酪乱卵嵐欄濫藍蘭覧利吏履李梨理璃痢裏裡里離陸律率立葎掠略劉流溜琉留硫粒隆竜龍侶慮旅虜了亮僚両凌寮料梁涼猟療瞭稜糧良諒遼量陵領力緑倫厘林淋燐琳臨輪隣鱗麟瑠塁涙累類令伶例冷励嶺怜玲礼苓鈴隷零霊麗齢暦歴列劣烈裂廉恋憐漣煉簾練聯蓮連錬呂魯櫓炉賂路露労婁廊弄朗楼榔浪漏牢狼篭老聾蝋郎六麓禄肋録論倭和話歪賄脇惑枠鷲亙亘鰐詫藁蕨椀湾碗腕弌丐丕个丱丶丼丿乂乖乘亂亅豫亊舒弍于亞亟亠亢亰亳亶从仍仄仆仂仗仞仭仟价伉佚估佛佝佗佇佶侈侏侘佻佩佰侑佯來侖儘俔俟俎俘俛俑俚俐俤俥倚倨倔倪倥倅伜俶倡倩倬俾俯們倆偃假會偕偐偈做偖偬偸傀傚傅傴傲僉僊傳僂僖僞僥僭僣僮價僵儉儁儂儖儕儔儚儡儺儷儼儻儿兀兒兌兔兢竸兩兪兮冀冂囘册冉冏冑冓冕冖冤冦冢冩冪冫决冱冲冰况冽凅凉凛几處凩凭凰凵凾刄刋刔刎刧刪刮刳刹剏剄剋剌剞剔剪剴剩剳剿剽劍劔劒剱劈劑辨辧劬劭劼劵勁勍勗勞勣勦飭勠勳勵勸勹匆匈甸匍匐匏匕匚匣匯匱匳匸區卆卅丗卉卍凖卞卩卮夘卻卷厂厖厠厦厥厮厰厶參簒雙叟曼燮叮叨叭叺吁吽呀听吭吼吮吶吩吝呎咏呵咎呟呱呷呰咒呻咀呶咄咐咆哇咢咸咥咬哄哈咨咫哂咤咾咼哘哥哦唏唔哽哮哭哺哢唹啀啣啌售啜啅啖啗唸唳啝喙喀咯喊喟啻啾喘喞單啼喃喩喇喨嗚嗅嗟嗄嗜嗤嗔嘔嗷嘖嗾嗽嘛嗹噎噐營嘴嘶嘲嘸噫噤嘯噬噪嚆嚀嚊嚠嚔嚏嚥嚮嚶嚴囂嚼囁囃囀囈囎囑囓囗囮囹圀囿圄圉圈國圍圓團圖嗇圜圦圷圸坎圻址坏坩埀垈坡坿垉垓垠垳垤垪垰埃埆埔埒埓堊埖埣堋堙堝塲堡塢塋塰毀塒堽塹墅墹墟墫墺壞墻墸墮壅壓壑壗壙壘壥壜壤壟壯壺壹壻壼壽夂夊夐夛梦夥夬夭夲夸夾竒奕奐奎奚奘奢奠奧奬奩奸妁妝佞侫妣妲姆姨姜妍姙姚娥娟娑娜娉娚婀婬婉娵娶婢婪媚媼媾嫋嫂媽嫣嫗嫦嫩嫖嫺嫻嬌嬋嬖嬲嫐嬪嬶嬾孃孅孀孑孕孚孛孥孩孰孳孵學斈孺宀它宦宸寃寇寉寔寐寤實寢寞寥寫寰寶寳尅將專對尓尠尢尨尸尹屁屆屎屓屐屏孱屬屮乢屶屹岌岑岔妛岫岻岶岼岷峅岾峇峙峩峽峺峭嶌峪崋崕崗嵜崟崛崑崔崢崚崙崘嵌嵒嵎嵋嵬嵳嵶嶇嶄嶂嶢嶝嶬嶮嶽嶐嶷嶼巉巍巓巒巖巛巫已巵帋帚帙帑帛帶帷幄幃幀幎幗幔幟幢幤幇幵并幺麼广庠廁廂廈廐廏廖廣廝廚廛廢廡廨廩廬廱廳廰廴廸廾弃弉彝彜弋弑弖弩弭弸彁彈彌彎弯彑彖彗彙彡彭彳彷徃徂彿徊很徑徇從徙徘徠徨徭徼忖忻忤忸忱忝悳忿怡恠怙怐怩怎怱怛怕怫怦怏怺恚恁恪恷恟恊恆恍恣恃恤恂恬恫恙悁悍惧悃悚悄悛悖悗悒悧悋惡悸惠惓悴忰悽惆悵惘慍愕愆惶惷愀惴惺愃愡惻惱愍愎慇愾愨愧慊愿愼愬愴愽慂慄慳慷慘慙慚慫慴慯慥慱慟慝慓慵憙憖憇憬憔憚憊憑憫憮懌懊應懷懈懃懆憺懋罹懍懦懣懶懺懴懿懽懼懾戀戈戉戍戌戔戛戞戡截戮戰戲戳扁扎扞扣扛扠扨扼抂抉找抒抓抖拔抃抔拗拑抻拏拿拆擔拈拜拌拊拂拇抛拉挌拮拱挧挂挈拯拵捐挾捍搜捏掖掎掀掫捶掣掏掉掟掵捫捩掾揩揀揆揣揉插揶揄搖搴搆搓搦搶攝搗搨搏摧摯摶摎攪撕撓撥撩撈撼據擒擅擇撻擘擂擱擧舉擠擡抬擣擯攬擶擴擲擺攀擽攘攜攅攤攣攫攴攵攷收攸畋效敖敕敍敘敞敝敲數斂斃變斛斟斫斷旃旆旁旄旌旒旛旙无旡旱杲昊昃旻杳昵昶昴昜晏晄晉晁晞晝晤晧晨晟晢晰暃暈暎暉暄暘暝曁暹曉暾暼曄暸曖曚曠昿曦曩曰曵曷朏朖朞朦朧霸朮朿朶杁朸朷杆杞杠杙杣杤枉杰枩杼杪枌枋枦枡枅枷柯枴柬枳柩枸柤柞柝柢柮枹柎柆柧檜栞框栩桀桍栲桎梳栫桙档桷桿梟梏梭梔條梛梃檮梹桴梵梠梺椏梍桾椁棊椈棘椢椦棡椌棍棔棧棕椶椒椄棗棣椥棹棠棯椨椪椚椣椡棆楹楷楜楸楫楔楾楮椹楴椽楙椰楡楞楝榁楪榲榮槐榿槁槓榾槎寨槊槝榻槃榧樮榑榠榜榕榴槞槨樂樛槿權槹槲槧樅榱樞槭樔槫樊樒櫁樣樓橄樌橲樶橸橇橢橙橦橈樸樢檐檍檠檄檢檣檗蘗檻櫃櫂檸檳檬櫞櫑櫟檪櫚櫪櫻欅蘖櫺欒欖鬱欟欸欷盜欹飮歇歃歉歐歙歔歛歟歡歸歹歿殀殄殃殍殘殕殞殤殪殫殯殲殱殳殷殼毆毋毓毟毬毫毳毯麾氈氓气氛氤氣汞汕汢汪沂沍沚沁沛汾汨汳沒沐泄泱泓沽泗泅泝沮沱沾沺泛泯泙泪洟衍洶洫洽洸洙洵洳洒洌浣涓浤浚浹浙涎涕濤涅淹渕渊涵淇淦涸淆淬淞淌淨淒淅淺淙淤淕淪淮渭湮渮渙湲湟渾渣湫渫湶湍渟湃渺湎渤滿渝游溂溪溘滉溷滓溽溯滄溲滔滕溏溥滂溟潁漑灌滬滸滾漿滲漱滯漲滌。「」、・ヲァィゥェォャュョッーアイウエオカキクケコサシスセソタチツテトナニヌネノハヒフヘホマミムメモヤユヨラリルレロワン゙゚漾漓滷澆潺潸澁澀潯潛濳潭澂潼潘澎澑濂潦澳澣澡澤澹濆澪濟濕濬濔濘濱濮濛瀉瀋濺瀑瀁瀏濾瀛瀚潴瀝瀘瀟瀰瀾瀲灑灣炙炒炯烱炬炸炳炮烟烋烝烙焉烽焜焙煥煕熈煦煢煌煖煬熏燻熄熕熨熬燗熹熾燒燉燔燎燠燬燧燵燼燹燿爍爐爛爨爭爬爰爲爻爼爿牀牆牋牘牴牾犂犁犇犒犖犢犧犹犲狃狆狄狎狒狢狠狡狹狷倏猗猊猜猖猝猴猯猩猥猾獎獏默獗獪獨獰獸獵獻獺珈玳珎玻珀珥珮珞璢琅瑯琥珸琲琺瑕琿瑟瑙瑁瑜瑩瑰瑣瑪瑶瑾璋璞璧瓊瓏瓔珱瓠瓣瓧瓩瓮瓲瓰瓱瓸瓷甄甃甅甌甎甍甕甓甞甦甬甼畄畍畊畉畛畆畚畩畤畧畫畭畸當疆疇畴疊疉疂疔疚疝疥疣痂疳痃疵疽疸疼疱痍痊痒痙痣痞痾痿痼瘁痰痺痲痳瘋瘍瘉瘟瘧瘠瘡瘢瘤瘴瘰瘻癇癈癆癜癘癡癢癨癩癪癧癬癰癲癶癸發皀皃皈皋皎皖皓皙皚皰皴皸皹皺盂盍盖盒盞盡盥盧盪蘯盻眈眇眄眩眤眞眥眦眛眷眸睇睚睨睫睛睥睿睾睹瞎瞋瞑瞠瞞瞰瞶瞹瞿瞼瞽瞻矇矍矗矚矜矣矮矼砌砒礦砠礪硅碎硴碆硼碚碌碣碵碪碯磑磆磋磔碾碼磅磊磬磧磚磽磴礇礒礑礙礬礫祀祠祗祟祚祕祓祺祿禊禝禧齋禪禮禳禹禺秉秕秧秬秡秣稈稍稘稙稠稟禀稱稻稾稷穃穗穉穡穢穩龝穰穹穽窈窗窕窘窖窩竈窰窶竅竄窿邃竇竊竍竏竕竓站竚竝竡竢竦竭竰笂笏笊笆笳笘笙笞笵笨笶筐筺笄筍笋筌筅筵筥筴筧筰筱筬筮箝箘箟箍箜箚箋箒箏筝箙篋篁篌篏箴篆篝篩簑簔篦篥籠簀簇簓篳篷簗簍篶簣簧簪簟簷簫簽籌籃籔籏籀籐籘籟籤籖籥籬籵粃粐粤粭粢粫粡粨粳粲粱粮粹粽糀糅糂糘糒糜糢鬻糯糲糴糶糺紆紂紜紕紊絅絋紮紲紿紵絆絳絖絎絲絨絮絏絣經綉絛綏絽綛綺綮綣綵緇綽綫總綢綯緜綸綟綰緘緝緤緞緻緲緡縅縊縣縡縒縱縟縉縋縢繆繦縻縵縹繃縷縲縺繧繝繖繞繙繚繹繪繩繼繻纃緕繽辮繿纈纉續纒纐纓纔纖纎纛纜缸缺罅罌罍罎罐网罕罔罘罟罠罨罩罧罸羂羆羃羈羇羌羔羞羝羚羣羯羲羹羮羶羸譱翅翆翊翕翔翡翦翩翳翹飜耆耄耋耒耘耙耜耡耨耿耻聊聆聒聘聚聟聢聨聳聲聰聶聹聽聿肄肆肅肛肓肚肭冐肬胛胥胙胝胄胚胖脉胯胱脛脩脣脯腋隋腆脾腓腑胼腱腮腥腦腴膃膈膊膀膂膠膕膤膣腟膓膩膰膵膾膸膽臀臂膺臉臍臑臙臘臈臚臟臠臧臺臻臾舁舂舅與舊舍舐舖舩舫舸舳艀艙艘艝艚艟艤艢艨艪艫舮艱艷艸艾芍芒芫芟芻芬苡苣苟苒苴苳苺莓范苻苹苞茆苜茉苙茵茴茖茲茱荀茹荐荅茯茫茗茘莅莚莪莟莢莖茣莎莇莊荼莵荳荵莠莉莨菴萓菫菎菽萃菘萋菁菷萇菠菲萍萢萠莽萸蔆菻葭萪萼蕚蒄葷葫蒭葮蒂葩葆萬葯葹萵蓊葢蒹蒿蒟蓙蓍蒻蓚蓐蓁蓆蓖蒡蔡蓿蓴蔗蔘蔬蔟蔕蔔蓼蕀蕣蕘蕈蕁蘂蕋蕕薀薤薈薑薊薨蕭薔薛藪薇薜蕷蕾薐藉薺藏薹藐藕藝藥藜藹蘊蘓蘋藾藺蘆蘢蘚蘰蘿虍乕虔號虧虱蚓蚣蚩蚪蚋蚌蚶蚯蛄蛆蚰蛉蠣蚫蛔蛞蛩蛬蛟蛛蛯蜒蜆蜈蜀蜃蛻蜑蜉蜍蛹蜊蜴蜿蜷蜻蜥蜩蜚蝠蝟蝸蝌蝎蝴蝗蝨蝮蝙蝓蝣蝪蠅螢螟螂螯蟋螽蟀蟐雖螫蟄螳蟇蟆螻蟯蟲蟠蠏蠍蟾蟶蟷蠎蟒蠑蠖蠕蠢蠡蠱蠶蠹蠧蠻衄衂衒衙衞衢衫袁衾袞衵衽袵衲袂袗袒袮袙袢袍袤袰袿袱裃裄裔裘裙裝裹褂裼裴裨裲褄褌褊褓襃褞褥褪褫襁襄褻褶褸襌褝襠襞襦襤襭襪襯襴襷襾覃覈覊覓覘覡覩覦覬覯覲覺覽覿觀觚觜觝觧觴觸訃訖訐訌訛訝訥訶詁詛詒詆詈詼詭詬詢誅誂誄誨誡誑誥誦誚誣諄諍諂諚諫諳諧諤諱謔諠諢諷諞諛謌謇謚諡謖謐謗謠謳鞫謦謫謾謨譁譌譏譎證譖譛譚譫譟譬譯譴譽讀讌讎讒讓讖讙讚谺豁谿豈豌豎豐豕豢豬豸豺貂貉貅貊貍貎貔豼貘戝貭貪貽貲貳貮貶賈賁賤賣賚賽賺賻贄贅贊贇贏贍贐齎贓賍贔贖赧赭赱赳趁趙跂趾趺跏跚跖跌跛跋跪跫跟跣跼踈踉跿踝踞踐踟蹂踵踰踴蹊蹇蹉蹌蹐蹈蹙蹤蹠踪蹣蹕蹶蹲蹼躁躇躅躄躋躊躓躑躔躙躪躡躬躰軆躱躾軅軈軋軛軣軼軻軫軾輊輅輕輒輙輓輜輟輛輌輦輳輻輹轅轂輾轌轉轆轎轗轜轢轣轤辜辟辣辭辯辷迚迥迢迪迯邇迴逅迹迺逑逕逡逍逞逖逋逧逶逵逹迸遏遐遑遒逎遉逾遖遘遞遨遯遶隨遲邂遽邁邀邊邉邏邨邯邱邵郢郤扈郛鄂鄒鄙鄲鄰酊酖酘酣酥酩酳酲醋醉醂醢醫醯醪醵醴醺釀釁釉釋釐釖釟釡釛釼釵釶鈞釿鈔鈬鈕鈑鉞鉗鉅鉉鉤鉈銕鈿鉋鉐銜銖銓銛鉚鋏銹銷鋩錏鋺鍄錮錙錢錚錣錺錵錻鍜鍠鍼鍮鍖鎰鎬鎭鎔鎹鏖鏗鏨鏥鏘鏃鏝鏐鏈鏤鐚鐔鐓鐃鐇鐐鐶鐫鐵鐡鐺鑁鑒鑄鑛鑠鑢鑞鑪鈩鑰鑵鑷鑽鑚鑼鑾钁鑿閂閇閊閔閖閘閙閠閨閧閭閼閻閹閾闊濶闃闍闌闕闔闖關闡闥闢阡阨阮阯陂陌陏陋陷陜陞陝陟陦陲陬隍隘隕隗險隧隱隲隰隴隶隸隹雎雋雉雍襍雜霍雕雹霄霆霈霓霎霑霏霖霙霤霪霰霹霽霾靄靆靈靂靉靜靠靤靦靨勒靫靱靹鞅靼鞁靺鞆鞋鞏鞐鞜鞨鞦鞣鞳鞴韃韆韈韋韜韭齏韲竟韶韵頏頌頸頤頡頷頽顆顏顋顫顯顰顱顴顳颪颯颱颶飄飃飆飩飫餃餉餒餔餘餡餝餞餤餠餬餮餽餾饂饉饅饐饋饑饒饌饕馗馘馥馭馮馼駟駛駝駘駑駭駮駱駲駻駸騁騏騅駢騙騫騷驅驂驀驃騾驕驍驛驗驟驢驥驤驩驫驪骭骰骼髀髏髑髓體髞髟髢髣髦髯髫髮髴髱髷髻鬆鬘鬚鬟鬢鬣鬥鬧鬨鬩鬪鬮鬯鬲魄魃魏魍魎魑魘魴鮓鮃鮑鮖鮗鮟鮠鮨鮴鯀鯊鮹鯆鯏鯑鯒鯣鯢鯤鯔鯡鰺鯲鯱鯰鰕鰔鰉鰓鰌鰆鰈鰒鰊鰄鰮鰛鰥鰤鰡鰰鱇鰲鱆鰾鱚鱠鱧鱶鱸鳧鳬鳰鴉鴈鳫鴃鴆鴪鴦鶯鴣鴟鵄鴕鴒鵁鴿鴾鵆鵈鵝鵞鵤鵑鵐鵙鵲鶉鶇鶫鵯鵺鶚鶤鶩鶲鷄鷁鶻鶸鶺鷆鷏鷂鷙鷓鷸鷦鷭鷯鷽鸚鸛鸞鹵鹹鹽麁麈麋麌麒麕麑麝麥麩麸麪麭靡黌黎黏黐黔黜點黝黠黥黨黯黴黶黷黹黻黼黽鼇鼈皷鼕鼡鼬鼾齊齒齔齣齟齠齡齦齧齬齪齷齲齶龕龜龠堯槇遙瑤凜熙 \ No newline at end of file diff --git a/src/ActorBarline.cpp b/src/ActorBarline.cpp deleted file mode 100644 index 12faffb7..00000000 --- a/src/ActorBarline.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "ScreenGameplay.h" -#include "ActorBarline.h" -#include "GameWindow.h" - -#define BLUE_CONSTANT (200.0 / 255.0) - -namespace Game { - namespace dotcur { - - ActorBarline::ActorBarline(ScreenGameplay *_Parent) : Sprite() - { - Parent = _Parent; - Centered = true; - ColorInvert = false; - AnimationTime = 0; - AnimationProgress = 0; - AffectedByLightning = true; - } - - void ActorBarline::Init(float Offset) - { - AnimationProgress = AnimationTime = Offset; - - SetPosition(Parent->GetScreenOffset(0.5).x, ScreenOffset + 3 * PlayfieldWidth / 4); - SetWidth(PlayfieldWidth); - - Red = Green = 0; - Blue = 200.f / 255.f; - Alpha = 0; - } - -#define RadioThreshold 0.9 - - void ActorBarline::Run(double TimeDelta, double Ratio) - { - if (AnimationProgress > 0) - { - float PosY = pow(AnimationProgress / AnimationTime, 2) * PlayfieldHeight + ScreenOffset; - - Alpha = 1 - AnimationProgress; - - SetPositionY(PosY); - AnimationProgress -= TimeDelta; - - if (AnimationProgress <= 0) - AnimationProgress = 0; - - return; - } - - if (Parent->GetMeasure() % 2) - { - Red = 1; - Blue = Green = 0; - - if (Ratio > RadioThreshold) // Red to blue - { - double diff = Ratio - RadioThreshold; - double duration = (1 - RadioThreshold); - - Red = LerpRatio(1.0, 0.0, diff, duration); - Blue = LerpRatio(0.0, BLUE_CONSTANT, diff, duration); - } - } - else - { - Red = 0.0; - Blue = 200.f / 255.f; - - if (Ratio > RadioThreshold) // Blue to red - { - float diff = Ratio - RadioThreshold; - double duration = (1 - RadioThreshold); - - Red = LerpRatio(0.0, 1.0, diff, duration); - Blue = LerpRatio(BLUE_CONSTANT, 0.0, diff, duration); - } - } - - SetPositionY(Ratio * (float)PlayfieldHeight); - - if (Parent->GetMeasure() % 2) - SetPositionY(PlayfieldHeight - GetPosition().y); - - SetPositionY(GetPosition().y + ScreenOffset); - - // assert(GetPosition().y > ScreenOffset); - } - } -} \ No newline at end of file diff --git a/src/ActorBarline.h b/src/ActorBarline.h deleted file mode 100644 index c8c7e181..00000000 --- a/src/ActorBarline.h +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - - -namespace Game { - namespace dotcur { - - class ScreenGameplay; - - class ActorBarline : public Sprite - { - ScreenGameplay* Parent; - float AnimationTime, AnimationProgress; - public: - ActorBarline(ScreenGameplay *_Parent); - void Run(double TimeDelta, double Ratio); - void Init(float Offset); - }; - } -} \ No newline at end of file diff --git a/src/ActorJudgment.cpp b/src/ActorJudgment.cpp deleted file mode 100644 index c9705435..00000000 --- a/src/ActorJudgment.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "GameState.h" -#include "Sprite.h" -#include "ActorJudgment.h" -#include "ImageLoader.h" - -#define AnimDuration 0.3f - -namespace Game { - namespace dotcur { - - ActorJudgment::ActorJudgment() - { - Centered = Configuration::GetSkinConfigf("Centered", "Judgment") != 0; - SetRotation(Configuration::GetSkinConfigf("Rotation", "Judgment")); - Alpha = 0; - SetImage(GameState::GetInstance().GetSkinImage("judge-perfect.png")); - SetPosition(Configuration::GetSkinConfigf("X", "Judgment"), Configuration::GetSkinConfigf("Y", "Judgment")); - AnimTime = 0; - AffectedByLightning = true; - } - - void ActorJudgment::ChangeJudgment(Judgment New) - { - AnimTime = AnimDuration; - SetScale(1.3f); - Alpha = 1; - - switch (New) - { - case J_EXCELLENT: - SetImage(GameState::GetInstance().GetSkinImage("judge-excellent.png")); - break; - case J_PERFECT: - SetImage(GameState::GetInstance().GetSkinImage("judge-perfect.png")); - break; - case J_GREAT: - SetImage(GameState::GetInstance().GetSkinImage("judge-great.png")); - break; - case Bad: - SetImage(GameState::GetInstance().GetSkinImage("judge-bad.png")); - break; - case Miss: - SetImage(GameState::GetInstance().GetSkinImage("judge-miss.png")); - break; - case None: - break; - } - } - - void ActorJudgment::Run(double delta) - { - if (AnimTime > 0) - { - SetScale(Vec2(LerpRatio(GetScale().x, 1.0f, AnimDuration - AnimTime, AnimDuration), - LerpRatio(GetScale().y, 1.0f, AnimDuration - AnimTime, AnimDuration))); - } - - AnimTime -= delta; - - if (AnimTime < -1) - { - Alpha -= delta; // Fade out in one second - } - } - } -} \ No newline at end of file diff --git a/src/ActorJudgment.h b/src/ActorJudgment.h deleted file mode 100644 index 4b07273e..00000000 --- a/src/ActorJudgment.h +++ /dev/null @@ -1,15 +0,0 @@ -#pragma once - -namespace Game { - namespace dotcur { - class ActorJudgment : public Sprite - { - float AnimTime; - public: - // I can't find anything more obvious of a name. - ActorJudgment(); - void ChangeJudgment(Judgment New); - void Run(double delta); - }; - } -} \ No newline at end of file diff --git a/src/ActorLifebar.cpp b/src/ActorLifebar.cpp deleted file mode 100644 index 7c8820f4..00000000 --- a/src/ActorLifebar.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "GameState.h" -#include "Configuration.h" -#include "Sprite.h" -#include "ActorLifebar.h" -#include "ImageLoader.h" -#include "GameWindow.h" - -namespace Game { - namespace dotcur { - - ActorLifebar::ActorLifebar() : Sprite() - { - Health = 50; // Out of 100! - pending_health = 0; - time = 0; - - SetImage(GameState::GetInstance().GetSkinImage("healthbar.png")); - - SetWidth(PlayfieldHeight / 2); - SetHeight(Configuration::GetSkinConfigf("Height", "Lifebar")); - Centered = Configuration::GetSkinConfigf("Centered", "Lifebar") != 0; - SetPosition(Configuration::GetSkinConfigf("X", "Lifebar"), Configuration::GetSkinConfigf("Y", "Lifebar")); - SetRotation(Configuration::GetSkinConfigf("Rotation", "Lifebar")); - AffectedByLightning = true; - - UpdateHealth(); - } - - void ActorLifebar::UpdateHealth() - { - if (Health < 0) - { - Health = 0; - } - - SetCrop2(Vec2(Health / 100, 1)); - Red = Green = Blue = Health / 100; - SetWidth((Health / 100) * PlayfieldHeight); - } - - void ActorLifebar::HitJudgment(Judgment Hit) - { - switch (Hit) - { - case J_EXCELLENT: - pending_health += 2; - break; - case J_PERFECT: - pending_health += 1; - break; - case J_GREAT: - break; - case Bad: - pending_health -= 3; - break; - case Miss: - case NG: - pending_health -= 12; - } - } - - void ActorLifebar::Run(double delta) - { - time += delta; - if (pending_health != 0) - { - Health += pending_health * delta; - pending_health -= pending_health * delta; - - if (pending_health > 200) // only up to 2x health - pending_health = 200; - - /* Accomulate health. Better you do, more forviging to mistakes. */ - if (Health > 100) - { - pending_health += Health - 100; - Health = 100; - } - } - - UpdateHealth(); - } - } -} \ No newline at end of file diff --git a/src/ActorLifebar.h b/src/ActorLifebar.h deleted file mode 100644 index 80bc4645..00000000 --- a/src/ActorLifebar.h +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -namespace Game { - namespace dotcur { - class ActorLifebar : public Sprite - { - float pending_health, time; - public: - // I can't find anything more obvious of a name. - ActorLifebar(); - float Health; - void HitJudgment(Judgment Hit); - void Run(double delta); - void UpdateHealth(); - }; - } -} \ No newline at end of file diff --git a/src/Application.cpp b/src/Application.cpp index f22883f1..ccceaed0 100644 --- a/src/Application.cpp +++ b/src/Application.cpp @@ -3,7 +3,7 @@ #include "GameGlobal.h" #include "Logging.h" #include "Screen.h" -#include "Configuration.h" + #include "Audio.h" #include "Application.h" #include "Sprite.h" @@ -27,6 +27,8 @@ #include "ScreenVideoTest.h" +#include "TruetypeFont.h" + void RunRaindropTests(); bool Auto = false; bool DoRun = false; @@ -57,7 +59,7 @@ void Application::ParseArgs(int argc, char **argv) ("output,o", po::value(), "Output File") ("gencache,c", - "Generate Cache") + "Generate Song Cache") ("format,g", po::value(), "Target Format") ("measure,m", po::value()->default_value(0), @@ -73,6 +75,10 @@ void Application::ParseArgs(int argc, char **argv) ("L,L", po::value(), "Load Custom Scene") ("test,T", "Run test suite") + ("fontcache,F", po::value(), + "Generate Font Cache") + ("config,x", po::value(), + "Set config file") ; po::variables_map vm; @@ -109,11 +115,21 @@ void Application::ParseArgs(int argc, char **argv) OutFile = vm["output"].as(); } + if (vm.count("config")) + { + Configuration::SetConfigFile(vm["config"].as()); + } + if (vm.count("gencache")) { - RunMode = MODE_GENCACHE; + RunMode = MODE_GENSONGCACHE; } + if (vm.count("fontcache")) { + RunMode = MODE_GENFONTCACHE; + InFontTextFile = vm["fontcache"].as(); + } + if (vm.count("format")) { ConvertMode = std::map{ @@ -242,15 +258,13 @@ void Application::SetupPreviewMode() // Load the song. auto song = LoadSong7KFromFilename(InFile); + if (!song || !song->Difficulties.size()) { - Log::Printf("File %s could not be loaded for preview. (%d/%d)\n", InFile.c_str(), (long long int)song.get(), song ? song->Difficulties.size() : 0); + Log::Printf("File %ls could not be loaded for preview. (ptr %d/diffcnt %d)\n", InFile.c_str(), (long long int)song.get(), song ? song->Difficulties.size() : 0); return; } - // Avoid a crash... - GameState::GetInstance().SetSelectedSong(song); - // Create loading screen and gameplay screen. auto game = std::make_shared(); auto LoadScreen = std::make_shared(game); @@ -259,13 +273,15 @@ void Application::SetupPreviewMode() song->SongDirectory = std::filesystem::absolute(InFile.parent_path()); /* - Game::VSRG::Parameters param; + Game::VSRG::PlayscreenParameters param; param.Upscroll = Upscroll; param.StartMeasure = Measure; param.Preloaded = true; param.Auto = Auto; - */ + */ + + GameState::GetInstance().SetSelectedSong(song); GameState::GetInstance().GetParameters(0)->Auto = Auto; game->Init(song); LoadScreen->Init(); @@ -401,7 +417,7 @@ void Application::Run() RunLoop = false; } - else if (RunMode == MODE_GENCACHE) + else if (RunMode == MODE_GENSONGCACHE) { Log::Printf("Generating cache...\n"); Game::GameState::GetInstance().Initialize(); @@ -429,7 +445,15 @@ void Application::Run() auto s = Utility::ToU8(InFile.wstring()); auto scr = std::make_shared(GameState::GetInstance().GetSkinFile(s)); Game = scr; - } + } + else if (RunMode == MODE_GENFONTCACHE) + { + Log::Printf("Generating font cache for provided file...\n"); + + TruetypeFont::GenerateFontCache(InFontTextFile, InFile); + + RunLoop = false; + } Log::Printf("Time: %fs\n", glfwGetTime() - T1); diff --git a/src/Application.h b/src/Application.h index 8cae3c0e..76dc6fd4 100644 --- a/src/Application.h +++ b/src/Application.h @@ -10,16 +10,17 @@ class Application MODE_NULL, MODE_PLAY, MODE_CONVERT, - MODE_GENCACHE, + MODE_GENSONGCACHE, MODE_VSRGPREVIEW, MODE_STOPPREVIEW, MODE_CUSTOMSCREEN, + MODE_GENFONTCACHE, MODE_TEST }RunMode; void ParseArgs(int, char **); - std::filesystem::path InFile, OutFile; + std::filesystem::path InFile, OutFile, InFontTextFile; // VSRG-Specific enum class CONVERTMODE diff --git a/src/ArcadeMechanics.cpp b/src/ArcadeMechanics.cpp index 19e14b81..4e2797d8 100644 --- a/src/ArcadeMechanics.cpp +++ b/src/ArcadeMechanics.cpp @@ -22,7 +22,7 @@ namespace Game { } bool RaindropArcadeMechanics::CanHitNoteHead(double time, TrackNote * note) { - return (abs(time - note->GetStartTime()) < score_keeper->getJudgmentCutoff()) && note->IsHeadEnabled() && !note->WasHit(); + return (abs(time - note->GetStartTime()) < PlayerScoreKeeper->getJudgmentCutoff()) && note->IsHeadEnabled() && !note->WasHit(); } bool RaindropArcadeMechanics::CanHitNoteTail(double time, TrackNote * note) { @@ -82,7 +82,7 @@ namespace Game { double tdev = (Note->GetEndTime() - SongTime) * 1000.; // Tail is within judge window, and head was hit - if ( abs(tdev) < score_keeper->getJudgmentWindow(SKJ_W3) + if ( abs(tdev) < PlayerScoreKeeper->getJudgmentWindow(SKJ_W3) && Note->WasHit()) { Note->Hit(); HitNotify(tdev, Lane, Note->IsHold(), true); @@ -105,7 +105,7 @@ namespace Game { { if (!Note->IsEnabled()) return false; - double miss_time = score_keeper->getMissCutoffMS(); + double miss_time = PlayerScoreKeeper->getJudgmentWindow(SKJ_W3); double dev = (SongTime - Note->GetStartTime()) * 1000.; double tail_dev = (SongTime - Note->GetEndTime()) * 1000.; diff --git a/src/Audio.cpp b/src/Audio.cpp index 572f5005..4fcee23b 100644 --- a/src/Audio.cpp +++ b/src/Audio.cpp @@ -1,7 +1,7 @@ #include "pch.h" #include "Audio.h" -#include "Configuration.h" + #include "Logging.h" float VolumeSFX = 1; @@ -30,7 +30,12 @@ PaError OpenStream(PaStream **mStream, PaDeviceIndex Device, void* Sound, double CfgVar RequestedLatency("RequestedLatency", "Audio"); CfgVar UseHighLatency("UseHighLatency", "Audio"); - if (!RequestedLatency) { +#ifdef WIN32 + bool useAuto = (RequestedLatency < 0 && UseWasapi) || (RequestedLatency <= 0 && !UseWasapi); +#else + bool useAuto = RequestedLatency <= 0; +#endif + if ( useAuto || UseHighLatency) { if (!UseHighLatency) outputParams.suggestedLatency = Pa_GetDeviceInfo(outputParams.device)->defaultLowOutputLatency; else @@ -60,13 +65,13 @@ PaError OpenStream(PaStream **mStream, PaDeviceIndex Device, void* Sound, double if (!UseSharedMode) { Log::Logf("AUDIO: Attempting to use exclusive mode WASAPI\n"); - StreamInfo.threadPriority = eThreadPriorityGames; + StreamInfo.threadPriority = eThreadPriorityProAudio; StreamInfo.flags = paWinWasapiExclusive | paWinWasapiThreadPriority; } else { Log::Logf("AUDIO: Attempting to use shared mode WASAPI\n"); - StreamInfo.threadPriority = eThreadPriorityAudio; + StreamInfo.threadPriority = eThreadPriorityGames; StreamInfo.flags = 0; } @@ -85,6 +90,12 @@ PaError OpenStream(PaStream **mStream, PaDeviceIndex Device, void* Sound, double DSStreamInfo.flags = 0; } else { + if (Pa_GetHostApiInfo(Pa_GetDeviceInfo(Device)->hostApi)->type == paMME) { + Log::Logf("AUDIO: Attempting to use MME\n"); + } + else { + Log::Logf("AUDIO: Opening host API with identifier: %d\n", Pa_GetHostApiInfo(Pa_GetDeviceInfo(Device)->hostApi)->type); + } outputParams.hostApiSpecificStreamInfo = nullptr; } } @@ -99,7 +110,7 @@ PaError OpenStream(PaStream **mStream, PaDeviceIndex Device, void* Sound, double if (Err) { - Log::Logf("%ls\n", Utility::Widen(Pa_GetErrorText(Err)).c_str()); + Log::Logf("Audio: Failed opening device, portaudio reports \"%ls\"\n", Utility::Widen(Pa_GetErrorText(Err)).c_str()); } #ifdef LINUX else @@ -182,9 +193,14 @@ class PaMixer if (!Stream) { // This was a Wasapi problem. Retry without it. - Log::Logf("AUDIO: Problem initializing WASAPI. Falling back to default API.\n"); + Log::Logf("AUDIO: Problem initializing WASAPI. Falling back to DirectSound API.\n"); UseWasapi = false; - OpenStream(&Stream, Pa_GetDefaultOutputDevice(), static_cast(this), Latency, Mix); + OpenStream(&Stream, DefaultDSDevice, static_cast(this), Latency, Mix); + + if (!Stream) { + Log::Logf("AUDIO: Problem initializing DirectSound API. Falling back to default API.\n"); + OpenStream(&Stream, Pa_GetDefaultOutputDevice(), static_cast(this), Latency, Mix); + } } } else @@ -383,6 +399,7 @@ void GetAudioInfo() for (PaDeviceIndex i = 0; i < DevCount; i++) { const PaDeviceInfo *Info = Pa_GetDeviceInfo(i); + if (Info->maxOutputChannels == 0) continue; // Skip input devices. Log::Logf("(%d): %s\n", i, Info->name); Log::Logf("\thighLat: %f ms, lowLat: %f ma\n", Info->defaultHighOutputLatency * 1000, Info->defaultLowOutputLatency * 1000); Log::Logf("\tsampleRate: %f, hostApi: %d\n", Info->defaultSampleRate, Info->hostApi); diff --git a/src/AudioSourceOJM.cpp b/src/AudioSourceOJM.cpp index 21a64cf5..646bef93 100644 --- a/src/AudioSourceOJM.cpp +++ b/src/AudioSourceOJM.cpp @@ -572,7 +572,7 @@ bool AudioSourceOJM::Open(std::filesystem::path f) if (!ifile->is_open()) { - Log::Printf("AudioSourceOJM: unable to load %s.\n", f); + Log::Printf("AudioSourceOJM: unable to load %s.\n", f.c_str()); return false; } diff --git a/src/Audiofile.cpp b/src/Audiofile.cpp index 46e80d13..833da1d3 100644 --- a/src/Audiofile.cpp +++ b/src/Audiofile.cpp @@ -2,7 +2,6 @@ #include "Logging.h" #include "Audio.h" -#include "Audiofile.h" #include "AudioSourceSFM.h" #include "AudioSourceOGG.h" @@ -295,6 +294,11 @@ uint32_t AudioSample::Read(float* buffer, size_t count) return 0; } +double AudioSample::GetDuration() +{ + return mAudioEnd - mAudioStart; +} + bool AudioSample::IsPlaying() { return mIsPlaying; @@ -417,6 +421,8 @@ void AudioSample::SeekTime(float Second) { mCounter = mRate * Second * Channels; + if (!mData) return; + if (mCounter >= mData->size()) mCounter = mData->size(); } @@ -612,7 +618,7 @@ uint32_t AudioStream::Update() mSource->SetLooping(IsLooping()); - if (ReadTotal = mSource->Read(tbuf, eCount)) + if ((ReadTotal = mSource->Read(tbuf, eCount))) { PaUtil_WriteRingBuffer(&mRingBuf, tbuf, ReadTotal); } diff --git a/src/Audiofile.h b/src/Audiofile.h index c7e432c0..ea90d7bb 100644 --- a/src/Audiofile.h +++ b/src/Audiofile.h @@ -74,6 +74,9 @@ class AudioSample : public Sound bool AwaitLoad(); + // returns duration in seconds + double GetDuration(); + bool IsPlaying() override; void Slice(float audio_start, float audio_end); std::shared_ptr CopySlice(); diff --git a/src/BackgroundAnimation.cpp b/src/BackgroundAnimation.cpp index c64002ed..b91ceca4 100644 --- a/src/BackgroundAnimation.cpp +++ b/src/BackgroundAnimation.cpp @@ -5,7 +5,6 @@ #include "Song.h" #include "BackgroundAnimation.h" #include "Song7K.h" -#include "SongDC.h" #include "ImageLoader.h" #include "ImageList.h" #include "Logging.h" @@ -39,7 +38,8 @@ std::string videoextensions[] = { ".mpg", ".mpeg", ".mpv", - ".flv" + ".flv", + ".webm" }; bool IsVideoPath(std::filesystem::path path) @@ -72,6 +72,8 @@ class BMSBackground : public BackgroundAnimation Game::VSRG::Difficulty* Difficulty; bool Validated; bool BlackToTransparent; + int MaxWidth, MaxHeight; + bool IsBMSON; public: BMSBackground(Interruptible* parent, Game::VSRG::Difficulty* Difficulty, Game::VSRG::Song* Song) : BackgroundAnimation(parent), List(this) { @@ -80,9 +82,12 @@ class BMSBackground : public BackgroundAnimation Validated = false; MissTime = 0; + MaxWidth = MaxHeight = 256; + bool BtoT = false; if (Difficulty->Data->TimingInfo->GetType() == Game::VSRG::TI_BMS) { + IsBMSON = std::dynamic_pointer_cast(Difficulty->Data->TimingInfo)->IsBMSON; if (!std::dynamic_pointer_cast(Difficulty->Data->TimingInfo)->IsBMSON) BtoT = true; } @@ -104,7 +109,8 @@ class BMSBackground : public BackgroundAnimation EventsLayer2 = Difficulty->Data->BMPEvents->BMPEventsLayer2; for (auto v : Difficulty->Data->BMPEvents->BMPList) { - std::filesystem::path path = Song->SongDirectory / v.second; + std::filesystem::path vs = v.second; + std::filesystem::path path = Song->SongDirectory / vs; if (IsVideoPath(path)) { auto vid = new VideoPlayback(); @@ -112,12 +118,15 @@ class BMSBackground : public BackgroundAnimation vid->StartDecodeThread(); List.AddToListIndex(vid, v.first); Videos[v.first] = vid; + MaxWidth = std::max(MaxWidth, vid->w); + MaxHeight = std::max(MaxHeight, vid->h); } else delete vid; } else List.AddToListIndex(path, v.first); + } List.AddToList(Song->BackgroundFilename, Song->SongDirectory); @@ -148,11 +157,10 @@ class BMSBackground : public BackgroundAnimation LayerMiss->SetImage(List.GetFromIndex(0), true); Layer0->SetImage(List.GetFromIndex(1), true); - Layer0->SetWidth(Layer0->GetWidth() / Layer0->GetHeight()); - Layer0->SetHeight(1); - auto x = Layer0->GetWidth(); - Layer0->SetPositionX( (1 - x) / 2 ); + auto ratio = Layer0->GetWidth() / Layer0->GetHeight(); + Layer0->SetWidth(1); + Layer0->SetHeight(1); sort(EventsLayer0.begin(), EventsLayer0.end()); sort(EventsLayerMiss.begin(), EventsLayerMiss.end()); @@ -169,7 +177,9 @@ class BMSBackground : public BackgroundAnimation EventsLayerMiss.push_back(bmp); } - Transform.SetWidth(256); + + + Transform.SetWidth(256 * ratio); Transform.SetHeight(256); Validated = true; @@ -287,11 +297,6 @@ std::unique_ptr CreateBGAforVSRG(Game::VSRG::Song &input, u return nullptr; } -std::unique_ptr CreateBGAforDotcur(Game::dotcur::Song &input, uint8_t DifficultyIndex) -{ - return std::make_unique(nullptr, GetSongBackground(input)); -} - BackgroundAnimation::BackgroundAnimation(Interruptible* parent) : Interruptible(parent) { } @@ -333,18 +338,8 @@ std::unique_ptr BackgroundAnimation::CreateBGAFromSong(uint { std::unique_ptr ret = nullptr; - switch (Input.Mode) - { - case MODE_VSRG: - ret = CreateBGAforVSRG(static_cast (Input), DifficultyIndex, context); - break; - case MODE_DOTCUR: - ret = CreateBGAforDotcur(static_cast (Input), DifficultyIndex); - break; - default: - break; - } - + ret = CreateBGAforVSRG(static_cast (Input), DifficultyIndex, context); + if (LoadNow) { ret->Load(); diff --git a/src/BackgroundAnimation.h b/src/BackgroundAnimation.h index 3e1824c9..ff0c820f 100644 --- a/src/BackgroundAnimation.h +++ b/src/BackgroundAnimation.h @@ -27,4 +27,6 @@ class BackgroundAnimation : public Interruptible Transformation& GetTransformation(); /* Can only be called from main thread if LoadNow = true! */ static std::unique_ptr CreateBGAFromSong(uint8_t DifficultyIndex, Game::Song& Input, Interruptible* context, bool LoadNow = false); -}; \ No newline at end of file +}; + +bool IsVideoPath(std::filesystem::path path); \ No newline at end of file diff --git a/src/BitmapFont.h b/src/BitmapFont.h index ea8693c6..cb0bb47f 100644 --- a/src/BitmapFont.h +++ b/src/BitmapFont.h @@ -17,7 +17,7 @@ class BitmapFont : public Font public: BitmapFont(); - void Render(const std::string &Text, const Vec2 &Position, const Mat4& Transform = Mat4()) override; + void Render(const std::string &Text, const Vec2 &Position, const Mat4& Transform = Mat4(), const Vec2 &Scale = Vec2(1,1)) override; void LoadFontImage(std::filesystem::path Name, Vec2 _CharSize, Vec2 _CellSize, Vec2 _RenderSize = Vec2(1, 1), char FontStart = 0); void LoadSkinFontImage(std::filesystem::path Name, Vec2 _CharSize, Vec2 _CellSize, Vec2 _RenderSize = Vec2(1, 1), char FontStart = 0); void SetAffectedByLightning(bool Lightning); diff --git a/src/Configuration.cpp b/src/Configuration.cpp index fef81e07..6b7301ce 100644 --- a/src/Configuration.cpp +++ b/src/Configuration.cpp @@ -1,7 +1,7 @@ #include "pch.h" -#include "GameGlobal.h" -#include "Configuration.h" + + #define SI_CONVERT_GENERIC #include "GameState.h" @@ -14,6 +14,8 @@ using namespace Configuration; LuaManager *SkinCfgLua; CSimpleIniA *Config; int IsWidescreen; +std::string ConfigFile = "config.ini"; + const std::string GlobalNamespace = "Global"; @@ -28,21 +30,26 @@ class ConfigurationException : std::exception ConfigurationException CfgNotLoaded("Configuration not loaded yet."); +void Configuration::SetConfigFile(std::string cfg) +{ + ConfigFile = cfg; +} + void Configuration::Initialize() { Config = new CSimpleIniA; - Config->LoadFile("config.ini"); + Config->LoadFile(ConfigFile.c_str()); SkinCfgLua = new LuaManager(); + AddRDLuaGlobal(SkinCfgLua); + if (Configuration::GetConfigs("Skin").length()) GameState::GetInstance().SetSkin(Configuration::GetConfigs("Skin")); IsWidescreen = Configuration::GetConfigf("Widescreen"); SkinCfgLua->SetGlobal("Widescreen", IsWidescreen); - SkinCfgLua->SetGlobal("ScreenWidth", ScreenWidth); - SkinCfgLua->SetGlobal("ScreenHeight", ScreenHeight); - + GameState::GetInstance().InitializeLua(SkinCfgLua->GetState()); SkinCfgLua->RunScript(GameState::GetInstance().GetSkinFile("skin.lua")); @@ -52,7 +59,7 @@ void Configuration::Initialize() void Configuration::Cleanup() { if (Config) - Config->SaveFile("config.ini"); + Config->SaveFile(ConfigFile.c_str()); delete Config; delete SkinCfgLua; @@ -230,18 +237,15 @@ bool Configuration::ListExists(std::string Name) uint32_t Configuration::CfgScreenHeight() { - if (IsWidescreen) - return ScreenHeightWidescreen; - else - return ScreenHeightDefault; + return ScreenHeightDefault; } uint32_t Configuration::CfgScreenWidth() { - if (IsWidescreen == 1) - return ScreenWidthWidescreen; - else if (IsWidescreen == 2) - return 1230; - else - return ScreenWidthDefault; + if (IsWidescreen == 1) // 16:9 + return 16.0 / 9.0 * ScreenHeightDefault; + else if (IsWidescreen == 2) // 16:10 + return 16.0 / 10.0 * ScreenHeightDefault; + else + return 4.0 / 3.0 * ScreenHeightDefault; } diff --git a/src/Configuration.h b/src/Configuration.h index 30ced84e..938d1802 100644 --- a/src/Configuration.h +++ b/src/Configuration.h @@ -2,6 +2,8 @@ namespace Configuration { + + void SetConfigFile(std::string cfgfile); void Initialize(); void Reload(); std::string GetConfigs(std::string Name, std::string Namespace = ""); @@ -30,16 +32,17 @@ namespace Configuration class ConfigurationVariable { +protected: std::string nm, ns; public: ConfigurationVariable(std::string _nm, std::string _ns = "") : nm(_nm), ns(_ns) {}; ~ConfigurationVariable() = default; - std::string str() const + virtual std::string str() const { return Configuration::GetConfigs(nm, ns); }; - float flt() const + virtual float flt() const { return Configuration::GetConfigf(nm, ns); } @@ -72,6 +75,20 @@ class ConfigurationVariable } }; +class SkinMetric : public ConfigurationVariable { +public: + SkinMetric(std::string _nm, std::string _ns = "") : ConfigurationVariable(_nm, _ns) {} + std::string str() const override + { + return Configuration::GetSkinConfigs(nm, ns); + } + + float flt() const override + { + return Configuration::GetSkinConfigf(nm, ns); + } +}; + typedef ConfigurationVariable CfgVar; #define ScreenHeight Configuration::CfgScreenHeight() diff --git a/src/Convert.cpp b/src/Convert.cpp index 8f32048a..aebdd58f 100644 --- a/src/Convert.cpp +++ b/src/Convert.cpp @@ -1,7 +1,6 @@ #include "pch.h" #include "GameGlobal.h" -#include "Directory.h" #include "Song7K.h" #include "Converter.h" #include "Logging.h" @@ -44,7 +43,7 @@ void ConvertToOM(Game::VSRG::Song *Sng, std::filesystem::path PathOut, std::stri return; } - Game::VSRG::GameChartData data = Game::VSRG::GameChartData::FromDifficulty(Difficulty.get()); + Game::VSRG::PlayerChartState data = Game::VSRG::PlayerChartState::FromDifficulty(Difficulty.get()); // First, convert metadata. out @@ -154,7 +153,7 @@ void ConvertToOM(Game::VSRG::Song *Sng, std::filesystem::path PathOut, std::stri void ConvertToSMTiming(Game::VSRG::Song *Sng, std::filesystem::path PathOut) { Game::VSRG::Difficulty* Diff = Sng->Difficulties[0].get(); - Game::VSRG::GameChartData data = Game::VSRG::GameChartData::FromDifficulty(Diff); + Game::VSRG::PlayerChartState data = Game::VSRG::PlayerChartState::FromDifficulty(Diff); std::ofstream out(PathOut.string()); // Technically, stepmania's #OFFSET is actually #GAP, not #OFFSET. diff --git a/src/ConvertToBMS.cpp b/src/ConvertToBMS.cpp index c8336cac..728e12d0 100644 --- a/src/ConvertToBMS.cpp +++ b/src/ConvertToBMS.cpp @@ -148,17 +148,17 @@ class BMSConverter : public Game::VSRG::RowifiedDifficulty { OutFile << "#MAKER " << Parent->Author << endl; OutFile << endl << "-- WAVs" << endl; - for (auto i : Parent->Data->SoundList){ + for (auto i : Parent->Data->SoundList) { OutFile << "#WAV" << ToBMSBase36(i.first) << " " << i.second << endl; } OutFile << endl << "-- BPMs" << endl; - for (size_t i = 0; i < BPMs.size(); i++){ + for (size_t i = 0; i < BPMs.size(); i++) { OutFile << "#BPM" << ToBMSBase36(i + 1) << " " << BPMs[i] << endl; } OutFile << endl << "-- STOPs" << endl; - for (size_t i = 0; i < Stops.size(); i++){ + for (size_t i = 0; i < Stops.size(); i++) { OutFile << "#STOP" << ToBMSBase36(i + 1) << " " << Stops[i] << endl; } @@ -169,48 +169,87 @@ class BMSConverter : public Game::VSRG::RowifiedDifficulty { } } - void WriteVectorToMeasureChannel(std::vector &Out, int Measure, int Channel, bool AllowMultiple = false) + void WriteVectorToMeasureChannel(std::vector &EventList, int Measure, int Channel, bool AllowMultiple = false) { - if (Out.size() == 0) return; // Nothing to write. + if (EventList.size() == 0) return; // Nothing to write. - auto VecLCM = GetRowCount(Out); - sort(Out.begin(), Out.end(), [](const Event& A, const Event&B) + auto VecLCM = GetRowCount(EventList); + sort(EventList.begin(), EventList.end(), [](const Event& A, const Event&B) -> bool { - auto dA = double(A.Sect.Num) / A.Sect.Den; - auto dB = double(B.Sect.Num) / B.Sect.Den; - return dA < dB; + return ((double)A.Sect.Num / A.Sect.Den) < ((double)B.Sect.Num / B.Sect.Den); }); - std::vector> rowified; + // first of the pair is numerator aka row given veclcm as a denominator + // second is event at numerator aka row + std::map> rowified; + + // simultaneous event count + size_t linecount = 0; // Now that we have the LCM we can easily just place the objects exactly as we want to output them. - for (auto Obj : Out) { // We convert to a fraction that fits with the LCM. - auto rNum = Obj.Sect.Num * VecLCM / Obj.Sect.Den; - bool slotFree = false; - for (size_t i = 0; i < rowified.size(); i++) { - if (!rowified[i][rNum] || !AllowMultiple) { - rowified[i][rNum] = Obj.Evt; - slotFree = true; - break; + for (auto &Obj : EventList) { + + // We convert to a numerator that fits with the LCM. + auto newNumerator = Obj.Sect.Num * VecLCM / Obj.Sect.Den; + auto &it = rowified[newNumerator]; + + if (!it.size() || AllowMultiple) { + it.push_back(Obj.Evt); + + // max amount of simultaneous events are given by the largest vector + linecount = std::max(linecount, it.size()); + } + } + + std::vector lines(linecount); + + // add the tag to all lines. + for (size_t i = 0; i < linecount; i++) { + auto &line = lines[i]; + line << Utility::Format("#%03d%s:", Measure, ToBMSBase36(Channel).c_str()); + } + + if (rowified.find(0) == rowified.end()) + rowified[0].resize(0); + + for (auto row = rowified.begin(); row != rowified.end(); row++) { + + // ith line gets the ith column at fraction row->first / LCM + for (size_t i = 0; i < linecount; i++) { + auto &line = lines[i]; + + if (i < row->second.size()) { + line << ToBMSBase36(row->second[i]); } + else + line << "00"; } - if (!slotFree) // didn't find an unused slot - { - std::vector row(VecLCM, 0); - row[rNum] = Obj.Evt; - rowified.push_back(row); + size_t next_row; + auto next = row; + next++; + + if (next != rowified.end()) { + next_row = next->first; + } + else { + next_row = VecLCM; } - } - for (auto val : rowified) { - OutFile << Utility::Format("#%03d%s:", Measure, ToBMSBase36(Channel).c_str()); - for (auto r : val) { - OutFile << ToBMSBase36(r); + // don't count the destination row itself (take 1) + size_t zero_fill = next_row - row->first - 1; + for (size_t i = 0; i < zero_fill; i++) { + for (auto &line : lines) { + line << "00"; + } } + + } + + for (auto &line : lines) { + OutFile << line.str(); OutFile << std::endl; } - OutFile << std::endl; } int GetChannel(int channel) const; @@ -322,7 +361,7 @@ std::string BMSConverter::ToBMSBase36(int num) return "00"; if (num > 1295) // ZZ in b36 - throw std::runtime_error("Out of range number for BMS conversion"); + throw std::runtime_error("EventList of range number for BMS conversion"); buf[65] = '\0'; i = 65; diff --git a/src/Directory.cpp b/src/Directory.cpp deleted file mode 100644 index 73270f3f..00000000 --- a/src/Directory.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include "pch.h" - -#include "Directory.h" - -Directory::Directory() -{ -} - -Directory::~Directory() -{ -} - -Directory::Directory(std::string subpath) -{ - curpath = subpath; -} - -Directory::Directory(const char* path) -{ - curpath = path; -} - -void Directory::operator=(std::string subpath) -{ - curpath = subpath; -} - -bool Directory::operator==(std::string subpath) const -{ - return curpath == subpath; -} - -Directory Directory::ParentDirectory() -{ - int a = curpath.length(); - while (--a) - { - if (curpath[a] == '/' || curpath[a] == '\\') - { - return Directory(curpath.substr(0, a)); - } - } - return Directory(std::string(".")); // if there is no slash, then the root directory has been reached. -} - -void Directory::Normalize(bool RemoveIllegal) -{ - if (RemoveIllegal) - { - Utility::RemoveFilenameIllegalCharacters(curpath, false, false); - Utility::ReplaceAll(curpath, "\\\\", "/"); - } - - std::string newCurPath; - - // remove all redundant slashes - char last = 0; - for (auto i = curpath.begin(); i != curpath.end(); ++i) - { - if (last == '/' && *i == '/') - { - continue; - } - - last = *i; - newCurPath += *i; - } - - curpath = newCurPath; -} - -Directory operator/(Directory parent, std::string subpath) -{ - if (subpath == "..") return parent.ParentDirectory(); - - std::string newpath; - - if (!parent.path().length()) - return subpath; - - char last = parent.path()[parent.path().length() - 1]; - - if (last == '/' || last == '\\') - newpath = parent.path() + subpath; - else - newpath = parent.path() + "/" + subpath; - - return Directory(newpath); -} - -Directory operator/(std::string subpath, Directory parent) -{ - return operator/(Directory(subpath), std::string(parent)); // o_O -} - -Directory Directory::Filename() -{ - size_t place; - if ((place = curpath.find_last_of('/')) != std::string::npos) - { - return curpath.substr(place + 1); - } - else if ((place = curpath.find_last_of('\\')) != std::string::npos) - { - return curpath.substr(place + 1); - } - - return curpath; -} - -std::string Directory::GetExtension() const -{ - auto out = curpath.substr(curpath.find_last_of(".") + 1); - Utility::ToLower(out); - return out; -} - -std::string Directory::path() const { return curpath; } -const char* Directory::c_path() const { return curpath.c_str(); } - -std::vector& Directory::ListDirectory(std::vector& Vec, DirType T, const char* ext, bool Recursive) -{ -#ifdef _WIN32 - HANDLE hFind = INVALID_HANDLE_VALUE; - WIN32_FIND_DATA ffd; - std::string DirFind = curpath + "/*"; - wchar_t tmp[MAX_PATH]; - char tmpmbs[MAX_PATH]; - - memset(tmp, 0, sizeof(wchar_t) * MAX_PATH); - - std::wstring Wide; - - if (ext == nullptr) - Wide = Utility::Widen(curpath) + L"/*"; - else - Wide = Utility::Widen(curpath) + L"/*." + Utility::Widen(ext); - - hFind = FindFirstFile(LPCWSTR(Wide.c_str()), &ffd); - - do - { - if (hFind == INVALID_HANDLE_VALUE) - continue; - - if ((ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY && T == FS_DIR) || (!(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && T == FS_REG)) - { - std::wstring fname = ffd.cFileName; - - if (fname == L"." || fname == L"..") - continue; - - Vec.push_back(Utility::Narrow(fname)); - } - else - { - wcstombs(tmpmbs, ffd.cFileName, MAX_PATH); - - std::string fname = curpath + tmpmbs + "./*"; - - if (Recursive) - { - Directory NewPath(fname); - NewPath.ListDirectory(Vec, T, ext, Recursive); - } - } - } while (FindNextFile(hFind, &ffd) != 0); - - FindClose(hFind); - -#else // Unixlike? - DIR *d; - struct dirent *dir; - d = opendir(curpath.c_str()); - if (d) - { - while ((dir = readdir(d)) != NULL) - { - if (dir->d_type == DT_REG || (dir->d_type == DT_DIR && T == FS_DIR)) - { - std::string fname = dir->d_name; - - // Extension is what we need? - if (ext && fname.substr(fname.find_last_of(".") + 1) == std::string(ext)) - Vec.push_back(fname); - else if (!ext) - Vec.push_back(fname); - } - else if (dir->d_type == DT_DIR) - { - std::string fname = std::string(dir->d_name) + "/"; - - if (Recursive) - { - Directory NewPath(fname); - NewPath.ListDirectory(Vec, T, ext, Recursive); - } - } - } - - closedir(d); - } -#endif - return Vec; -} - -Directory::operator std::string() const -{ - return path(); -} \ No newline at end of file diff --git a/src/Directory.h b/src/Directory.h deleted file mode 100644 index a19cdb77..00000000 --- a/src/Directory.h +++ /dev/null @@ -1,40 +0,0 @@ -//#if 0 - -#pragma once - -class Directory -{ -public: - - enum DirType - { - FS_DIR, - FS_REG - }; - - Directory(); - Directory(std::string path); - Directory(const char* path); - ~Directory(); - - void operator=(std::string); - bool operator==(std::string) const; - operator std::string() const; - - void Normalize(bool RemoveIllegal = false); - Directory ParentDirectory(); - Directory Filename(); - - std::string GetExtension() const; - std::string path() const; - const char* c_path() const; - std::vector& ListDirectory(std::vector& Vec, DirType T = FS_REG, const char* ext = NULL, bool Recursive = false); - -private: - std::string curpath; -}; - -extern Directory operator/(Directory, std::string); -extern Directory operator/(std::string, Directory); - -//#endif \ No newline at end of file diff --git a/src/Font.cpp b/src/Font.cpp index 24cc6dd5..a5a4287b 100644 --- a/src/Font.cpp +++ b/src/Font.cpp @@ -25,7 +25,7 @@ void Font::Invalidate() /* stub */ } -void Font::Render(const std::string &Text, const Vec2 &Position, const Mat4& Transform) +void Font::Render(const std::string &Text, const Vec2 &Position, const Mat4& Transform, const Vec2 &Scale) { /* stub */ } diff --git a/src/Font.h b/src/Font.h index 1db8b63f..3ae4e60f 100644 --- a/src/Font.h +++ b/src/Font.h @@ -12,5 +12,7 @@ class Font virtual float GetHorizontalLength(const char *Text); virtual void Invalidate(); - virtual void Render(const std::string &Text, const Vec2 &Position, const Mat4& Transform = Mat4()); -}; \ No newline at end of file + virtual void Render(const std::string &Text, const Vec2 &Position, const Mat4& Transform = Mat4(), const Vec2 &Scale = Vec2(1,1)); +}; + +#define SDF_SIZE 512 \ No newline at end of file diff --git a/src/GameGlobal.h b/src/GameGlobal.h index e087b946..eca7ea5c 100644 --- a/src/GameGlobal.h +++ b/src/GameGlobal.h @@ -6,64 +6,6 @@ const uint16_t ScreenWidthDefault = 1024; const uint16_t ScreenHeightDefault = 768; -// 16:9 -const uint16_t ScreenWidthWidescreen = 1360; -const uint16_t ScreenHeightWidescreen = 768; - -/* raindrop .cur mode Consts */ -namespace Game { - namespace dotcur { - const uint16_t PlayfieldWidth = 800; - const uint16_t PlayfieldHeight = 600; - const int16_t ScreenOffset = 80; - const float CircleSize = 80.0f; - - const float LeniencyHitTime = 0.135f; - const float HoldLeniencyHitTime = 0.1f; - - const float LE_EXCELLENT = 0.03f; - const float LE_PERFECT = 0.05f; - const float LE_GREAT = 0.1f; - - enum Judgment - { - None, - NG, - Miss, - OK, - Bad, - J_GREAT, - J_PERFECT, - J_EXCELLENT - }; - - struct EvaluationData - { - uint32_t MaxCombo; - uint32_t NumNG; - uint32_t NumOK; - uint32_t NumMisses; - uint32_t NumBads; - uint32_t NumGreats; - uint32_t NumPerfects; - uint32_t NumExcellents; - - // Scoring - uint32_t totalNotes; - double dpScore; - double dpScoreSquare; - }; - } -} - -float _ScreenDifference(); - -#define ScreenDifference _ScreenDifference() - - - - - enum KeyType { // General stuff @@ -81,18 +23,6 @@ enum KeyType KT_Debug, KT_ReloadCFG, - // raindrop specific - KT_GameplayClick, - - // Editor specific - KT_FractionDec, - KT_FractionInc, - KT_ChangeMode, - KT_GridDec, - KT_GridInc, - KT_SwitchOffsetPrompt, - KT_SwitchBPMPrompt, - // 7K specific KT_P1_SCRATCH_UP, KT_P1_SCRATCH_DOWN, @@ -116,7 +46,7 @@ enum KeyType KT_Key16 }; -extern char* KeytypeNames[]; +extern const char* KeytypeNames[]; @@ -147,7 +77,7 @@ namespace Game { enum ChartType { - // Autodecide - Not a value for anything other than Parameters! + // Autodecide - Not a value for anything other than PlayscreenParameters! TI_NONE = 0, TI_BMS = 1, TI_OSUMANIA = 2, @@ -225,7 +155,7 @@ namespace Game { enum LifeType { - LT_AUTO = 0, // Only for Parameters + LT_AUTO = 0, // Only for PlayscreenParameters // actual groove type should be set by playing field from chart LT_GROOVE = 1, // Beatmania default lifebar @@ -274,7 +204,40 @@ namespace Game { PMT_F = 35, }; - struct Parameters { + enum TimingType + { + TT_TIME, + TT_BEATS, + TT_PIXELS + }; + + struct Difficulty; + struct PlayerChartState; + class ChartInfo; + class Mechanics; + class ScoreKeeper; + + struct PlayscreenParameters { + private: + struct SHiddenData { + EHiddenMode Mode; + float Center; // in NDC + float TransitionSize; // in NDC + float CenterSize; // in NDC + + + }; + + void UpdateHidden(double JudgeY); + SHiddenData Hidden; + + TimingType SetupGameSystem( + std::shared_ptr TimingInfo, + ScoreKeeper* PlayerScoreKeeper); + + void SetupGauge(std::shared_ptr TimingInfo, ScoreKeeper* PlayerScoreKeeper); + + public: // If true, use upscroll (VSRG only) int Upscroll; @@ -297,7 +260,7 @@ namespace Game { float Rate; // Scroll speed - double SpeedMultiplier; + double UserSpeedMultiplier; // Randomizing mode -> 0 = Disabled, 1 = Per-Lane, 2 = Panic (unimplemented) int Random; @@ -311,26 +274,54 @@ namespace Game { // Game System Type (VSRG only) int32_t SystemType; - Parameters() { + // Whether to interpret desired speed + // as green number + bool GreenNumber; + + // Whether to enable the use of strictest timing + bool UseW0; + + PlayerChartState* Setup( + double DesiredDefaultSpeed, + int SpeedType, + double Drift, + std::shared_ptr CurrentDiff); + + std::unique_ptr PrepareMechanicsSet( + std::shared_ptr CurrentDiff, + std::shared_ptr PlayerScorekeeper, + double JudgeY); + + ScoreType GetScoringType() const; + + int GetHiddenMode(); + float GetHiddenCenter(); + float GetHiddenTransitionSize(); + float GetHiddenCenterSize(); + + PlayscreenParameters() { Upscroll = false; Wave = false; Preloaded = false; Auto = false; NoFail = false; + GreenNumber = false; + UseW0 = false; HiddenMode = HM_NONE; StartMeasure = -1; Random = 0; Rate = 1; GaugeType = LT_AUTO; SystemType = VSRG::TI_NONE; - SpeedMultiplier = 4; + UserSpeedMultiplier = 4; } }; /* Vertical Space for a Measure. A single 4/4 measure takes all of the playing field. Increasing this will decrease multiplier resolution. */ - const float MeasureBaseSpacing = 0.8f * ScreenHeightDefault; + extern SkinMetric PLAYFIELD_SIZE; + extern SkinMetric UNITS_PER_MEASURE; /* vsrg constants */ @@ -348,7 +339,7 @@ namespace Game { /* Program itself consts */ #define RAINDROP_WINDOWTITLE "raindrop ver: " -#define RAINDROP_VERSION "0.500" +#define RAINDROP_VERSION "0.600" #ifdef NDEBUG #define RAINDROP_BUILDTYPE " " #else @@ -358,4 +349,4 @@ namespace Game { #define RAINDROP_VERSIONTEXT RAINDROP_VERSION RAINDROP_BUILDTYPE __DATE__ #include "BindingsManager.h" -#include "Configuration.h" + diff --git a/src/GameObject.cpp b/src/GameObject.cpp deleted file mode 100644 index fe1e7265..00000000 --- a/src/GameObject.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "GameState.h" -#include "Sprite.h" -#include "VBO.h" -#include "GameObject.h" -#include "ImageLoader.h" -#include "Audio.h" - -#define FADEIN_DURATION 0.3f -#define FADEOUT_DURATION 0.7f - -SoundSample *HitSnd = nullptr; -SoundSample *HoldReleaseSnd = nullptr; -bool GameObjectTexInitialized = false; - -namespace Game { - namespace dotcur { - GameObject::GameObject() : Sprite(false) - { - SetImage(GameState::GetInstance().GetSkinImage("hitcircle.png")); - Centered = true; // use the object's center instead of top-left - - SetSize(CircleSize); - - fadeout_time = 0; - fadein_time = FADEIN_DURATION; - AnimationStatus = 0; - hold_duration = 0; - endTime = 0; - heldKey = -1; - Fraction = -1; - BeingHeld = false; - DoTextureCleanup = false; - waiting_time = 0; - AffectedByLightning = true; - - if (!HitSnd) - { - HitSnd = new SoundSample(); - HitSnd->Open((GameState::GetInstance().GetSkinFile("hit.ogg")).c_str()); - } - - if (!HoldReleaseSnd) - { - HoldReleaseSnd = new SoundSample(); - HoldReleaseSnd->Open((GameState::GetInstance().GetSkinFile("holdfinish.ogg")).c_str()); - } - - UvBuffer = Renderer::GetDefaultTextureBuffer(); - - ColorInvert = false; - } - - void GameObject::GlobalInit() - { - } - - void GameObject::Animate(float delta, float songTime) - { - if (GetPosition().x == 0) - { - Alpha = 0; - return; - } - - if (AnimationStatus == 0) - { - if (fadein_time >= 0) - { - Alpha = 0; - if (waiting_time) - { - waiting_time -= delta; - } - - if (waiting_time <= 0) - { - Alpha = LerpRatio(0.0, 1.0, FADEIN_DURATION - fadein_time, FADEIN_DURATION); - SetScale(LerpRatio(1.5, 1.0, FADEIN_DURATION - fadein_time, FADEIN_DURATION)); - fadein_time -= delta; - } - - return; - } - - Alpha = 1; - SetScale(1); - AnimationStatus = 1; - } - else if (AnimationStatus == 1) - { - if (fadeout_time) - AnimationStatus = 2; - } - else - { - fadeout_time -= delta * 2; - - // alpha out - Alpha = LerpRatio(1.0, 0.0, FADEOUT_DURATION - fadeout_time, FADEOUT_DURATION); - - if (fadeout_time <= 0) - { - Alpha = 0; - AnimationStatus = 3; // Remove only - } - - // scale in - if (endTime == 0) - { - SetScale(LerpRatio(1.0, 2.0, FADEOUT_DURATION - fadeout_time, FADEOUT_DURATION)); - } - else // Holds have a bigger scale-in - SetScale(LerpRatio(1.3, 3.0, FADEOUT_DURATION - fadeout_time, FADEOUT_DURATION)); - - return; - } - - if (BeingHeld == true) - { - float holdDuration = endTime - startTime; - float Progress = songTime - startTime; - SetScale(LerpRatio(1.0, 1.3, Progress, holdDuration)); - Green = LerpRatio(0.5, 0.0, Progress, holdDuration); - } - } - - bool GameObject::ShouldRemove() - { - return AnimationStatus == 3; - } - - Judgment GameObject::Run(double delta, double Time, bool Autoplay) - { - if (fadeout_time || GetPosition().x == 0) // It was hit, so stop. - return None; - - if (Autoplay) - { - // you can be slightly early autoplay, it's fine. - if (Time >= startTime - 0.008 && fadeout_time == 0 && !BeingHeld) // A pretend kind of thing. ;) - { - return Hit(startTime, GetPosition(), true, false, -1); - // pretend that all autoplay notes were hit exactly on time. - } - } - - // it's not being held? Out of leniency? - if (Time - startTime > LeniencyHitTime) - { - if (endTime == 0) - { - fadeout_time = FADEOUT_DURATION; // Fade out! You missed! - return Miss; - } - } - - if (endTime == 0) // stuff forward is holds only - return None; - - if (BeingHeld) - { - /* it's done */ - if (Time >= endTime && fadeout_time == 0) - { - fadeout_time = FADEOUT_DURATION; - BeingHeld = false; - HoldReleaseSnd->Play(); - return OK; - } - } - else if (!BeingHeld && Time > startTime + LeniencyHitTime) - { - fadeout_time = FADEOUT_DURATION; - return NG; - } - - return None; - } - - Judgment GameObject::Hit(double Time, Vec2 mpos, bool KeyDown, bool Autoplay, int32_t Key) - { - Vec2 dist = mpos - GetPosition(); - - if (fadeout_time || GetPosition().x == 0 || Autoplay) - return None; - - if (BeingHeld) // Held note? Handle things here. - { - if (!KeyDown && Key == heldKey) // Same key we pressed initially? It was released! Panic! - BeingHeld = false; - - return None; - } - - if (!KeyDown) - { - if (!Autoplay) - return None; - } - - // not already fading out and mouse within the circle? - float dabs = std::abs(glm::length(dist)) * 2; // dabs*2 < diameter; dabs < radius - if (dabs < CircleSize && fadeout_time == 0) - { - float SpareTime = std::abs(Time - startTime); - if (SpareTime < LeniencyHitTime) // within leniency? - { - // Hit! - Judgment RVal; - - HitSnd->Play(); - - if (endTime == 0) // Not a hold? - fadeout_time = FADEOUT_DURATION; - else - { - heldKey = Key; - BeingHeld = true; - } - - // Judgment (values in seconds) - RVal = Bad; - - if (SpareTime < LE_GREAT) - RVal = J_GREAT; - - if (SpareTime < LE_PERFECT) - RVal = J_PERFECT; - - if (SpareTime < LE_EXCELLENT) - RVal = J_EXCELLENT; - - return RVal; - } - } - return None; - } - - bool GameObject::IsHold() - { - return endTime > 0; - } - - void GameObject::Assign(double Duration, uint32_t _Measure, double _MeasureFraction) - { - hold_duration = Duration; - Measure = _Measure; - Fraction = _MeasureFraction; - } - - /* Assume Sprite was already invalidated */ - void GameObject::Invalidate() - { - UvBuffer->Invalidate(); - UpdateTexture(); - } - - double GameObject::GetFraction() const - { - return Fraction; - } - - void GameObject::SetFraction(double frac) - { - Fraction = frac; - } - } -} \ No newline at end of file diff --git a/src/GameObject.h b/src/GameObject.h deleted file mode 100644 index 8b72dd0d..00000000 --- a/src/GameObject.h +++ /dev/null @@ -1,45 +0,0 @@ -#pragma once - -#include "Sprite.h" - -namespace Game { - namespace dotcur { - - class GameObject : public Sprite - { - private: - friend class ScreenEdit; - - bool BeingHeld; - - int32_t heldKey; - - unsigned char AnimationStatus; - - public: - - double startTime, endTime, beat, hold_duration; - uint32_t Measure; - double Fraction; - float fadeout_time, fadein_time; // time to fadeout, and time to get a hit - float waiting_time; - - GameObject(); - static void GlobalInit(); - void Initialize(); - - Judgment Hit(double time, Vec2 mpos, bool KeyDown, bool Autoplay, int32_t Key); - Judgment Run(double delta, double Time, bool Autoplay); - void Animate(float delta, float songTime); - void LoadFile(double Duration, uint32_t Measure, double MeasureFraction); - - double GetFraction() const; - void SetFraction(double frac); - bool IsHold(); - void Invalidate() override; - bool ShouldRemove(); - }; - - using GameObjectVector = std::vector; - } -} \ No newline at end of file diff --git a/src/GameState.cpp b/src/GameState.cpp index d8fa1fdb..af743249 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -1,7 +1,8 @@ #include "pch.h" -#include "GameGlobal.h" +#include "PlayerContext.h" #include "GameState.h" + #include "GameWindow.h" #include "Song.h" #include "SongDatabase.h" @@ -16,6 +17,12 @@ #include "NoteLoader7K.h" +namespace Game { + namespace VSRG { + SkinMetric PLAYFIELD_SIZE("PlayfieldSize"); + SkinMetric UNITS_PER_MEASURE("UnitsPerMeasure"); + } +} #define DirectoryPrefix std::string("GameData/") #define SkinsPrefix std::string("Skins/") @@ -92,9 +99,15 @@ GameState& GameState::GetInstance() return *StateInstance; } -Song *GameState::GetSelectedSong() const +void GameState::SetSelectedSong(std::shared_ptr sng) { - return SelectedSong.get(); + SelectedSong = sng; +} + +VSRG::Song *GameState::GetSelectedSong() const +{ + auto p = SongWheel::GetInstance().GetSelectedSong().get(); + return p ? p : SelectedSong.get(); } void Game::GameState::StartScreenTransition(std::string target) @@ -138,11 +151,6 @@ std::filesystem::path GameState::GetSkinFile(const std::string &Name, const std: return Test; } -void GameState::SetSelectedSong(std::shared_ptr song) -{ - SelectedSong = song; -} - std::filesystem::path GameState::GetSkinFile(const std::string& Name) { return GetSkinFile(Name, GetSkin()); @@ -214,16 +222,17 @@ bool GameState::PlayerNumberInBounds(int pn) const return pn >= 0 && pn < PlayerInfo.size(); } -void GameState::SetCurrentGaugeType(int GaugeType, int pn) +void Game::GameState::SetPlayerContext(VSRG::PlayerContext * pc, int pn) { - if (PlayerNumberInBounds(pn)) - PlayerInfo[pn].CurrentGaugeType = GaugeType; + if (PlayerNumberInBounds(pn)) { + PlayerInfo[pn].ctx = pc; + } } int GameState::GetCurrentGaugeType(int pn) const { if (PlayerNumberInBounds(pn)) - return PlayerInfo[pn].CurrentGaugeType; + return PlayerInfo[pn].ctx ? PlayerInfo[pn].ctx->GetCurrentGaugeType() : PlayerInfo[pn].Params.GaugeType; return 0; } @@ -251,47 +260,42 @@ Texture* GameState::GetSongStage() { if (SelectedSong) { - if (SelectedSong->Mode == MODE_VSRG) + auto Song = SelectedSong; + + if (PlayerInfo[0].Diff) { - VSRG::Song *Song = static_cast(SelectedSong.get()); + auto diff = PlayerInfo[0].Diff; + std::filesystem::path File = Database->GetStageFile(diff->ID); + + // Oh so it's loaded and it's not in the database, fine. + if (File.string().length() == 0 && diff->Data) + File = diff->Data->StageFile; - if (PlayerInfo[0].Diff) + auto toLoad = SelectedSong->SongDirectory / File; + + // ojn files use their cover inside the very ojn + if (File.extension() == ".ojn") { - auto diff = PlayerInfo[0].Diff; - std::filesystem::path File = Database->GetStageFile(diff->ID); - - // Oh so it's loaded and it's not in the database, fine. - if (File.string().length() == 0 && diff->Data) - File = diff->Data->StageFile; - - auto toLoad = SelectedSong->SongDirectory / File; - - // ojn files use their cover inside the very ojn - if (File.extension() == ".ojn") - { - size_t read; - const unsigned char* buf = reinterpret_cast(LoadOJNCover(toLoad, read)); - ImageData data = ImageLoader::GetDataForImageFromMemory(buf, read); - StageImage->SetTextureData2D(data, true); - delete[] buf; - - return StageImage; - } - - if (File.string().length() && std::filesystem::exists(toLoad)) - { - StageImage->LoadFile(toLoad, true); - return StageImage; - } - - return nullptr; + size_t read; + const unsigned char* buf = reinterpret_cast(LoadOJNCover(toLoad, read)); + ImageData data = ImageLoader::GetDataForImageFromMemory(buf, read); + StageImage->SetTextureData2D(data, true); + delete[] buf; + + return StageImage; + } + + if (File.string().length() && std::filesystem::exists(toLoad)) + { + StageImage->LoadFile(toLoad, true); + return StageImage; } return nullptr; - // Oh okay, no difficulty assigned. } - // Stage file not supported for DC songs yet + return nullptr; + // Oh okay, no difficulty assigned. } // no song selected @@ -340,30 +344,19 @@ void GameState::SetScorekeeper7K(std::shared_ptr Other, PlayerInfo[pn].SKeeper7K = Other; } -void GameState::SetCurrentScoreType(int ScoreType, int pn) -{ - if (PlayerNumberInBounds(pn)) - PlayerInfo[pn].CurrentScoreType = ScoreType; -} int GameState::GetCurrentScoreType(int pn) const { if (PlayerNumberInBounds(pn)) - return PlayerInfo[pn].CurrentScoreType; + return PlayerInfo[pn].Params.GetScoringType(); else return 0; } -void GameState::SetCurrentSystemType(int SystemType, int pn) -{ - if (PlayerNumberInBounds(pn)) - PlayerInfo[pn].CurrentSubsystemType = SystemType; -} - int GameState::GetCurrentSystemType(int pn) const { if (PlayerNumberInBounds(pn)) - return PlayerInfo[pn].CurrentSubsystemType; + return PlayerInfo[pn].ctx ? PlayerInfo[pn].ctx->GetCurrentSystemType() : PlayerInfo[pn].Params.SystemType; else return 0; } diff --git a/src/GameState.h b/src/GameState.h index fe55a1d7..d1d5e151 100644 --- a/src/GameState.h +++ b/src/GameState.h @@ -11,17 +11,14 @@ namespace Game { class Song; - namespace dotcur - { - class Song; - } namespace VSRG { - struct Parameters; + struct PlayscreenParameters; struct Difficulty; class Song; class ScoreKeeper; + class PlayerContext; } class GameState @@ -31,21 +28,13 @@ namespace Game Texture* StageImage; Texture* SongBG; - std::shared_ptr SelectedSong; + std::shared_ptr SelectedSong; struct SPlayerCurrent7K { std::shared_ptr SKeeper7K; - VSRG::Parameters Params; + VSRG::PlayscreenParameters Params; std::shared_ptr Diff; - - int CurrentGaugeType; - int CurrentScoreType; - int CurrentSubsystemType; - SPlayerCurrent7K::SPlayerCurrent7K (){ - CurrentGaugeType = 0; - CurrentScoreType = 0; - CurrentSubsystemType = 0; - } + VSRG::PlayerContext *ctx; }; std::vector PlayerInfo; @@ -75,8 +64,8 @@ namespace Game bool SkinSupportsChannelCount(int Count); std::string GetSkin(); - void SetSelectedSong(std::shared_ptr song); - Game::Song *GetSelectedSong() const; + void SetSelectedSong(std::shared_ptr song); + Game::VSRG::Song *GetSelectedSong() const; Texture* GetSongBG(); Texture* GetSongStage(); @@ -96,23 +85,22 @@ namespace Game /* Player-number dependant functions */ bool PlayerNumberInBounds(int pn) const; + void SetPlayerContext(VSRG::PlayerContext* pc, int pn); + // VSRG Gauge Type - void SetCurrentGaugeType(int GaugeType, int pn); int GetCurrentGaugeType(int pn) const; // VSRG score system - void SetCurrentScoreType(int ScoreType, int pn); int GetCurrentScoreType(int pn) const; // VSRG subsystem - void SetCurrentSystemType(int SystemType, int pn); int GetCurrentSystemType(int pn) const; // Note: Returning a shared_ptr causes lua to fail an assertion, since shared_ptr is not registered. VSRG::ScoreKeeper* GetScorekeeper7K(int pn); void SetScorekeeper7K(std::shared_ptr Other, int pn); - VSRG::Parameters* GetParameters(int pn); + VSRG::PlayscreenParameters* GetParameters(int pn); VSRG::Difficulty* GetDifficulty(int pn); std::shared_ptr GetDifficultyShared(int pn); void SetDifficulty(std::shared_ptr df, int pn); diff --git a/src/GameStateLua.cpp b/src/GameStateLua.cpp index dd3420ae..84879da2 100644 --- a/src/GameStateLua.cpp +++ b/src/GameStateLua.cpp @@ -1,7 +1,5 @@ #include "pch.h" -#include "GameGlobal.h" - #include "LuaManager.h" #include "LuaBridge.h" @@ -9,7 +7,6 @@ #include "Song.h" #include "Song7K.h" -#include "SongDC.h" #include "SongDatabase.h" #include "SongWheel.h" @@ -52,6 +49,19 @@ struct songHelper return; } + template + static std::string getDifficultyGenre(T const *Diff) + { + auto candidate = GameState::GetInstance().GetSongDatabase()->GetGenreForDifficulty(Diff->ID); + return candidate; + } + + template + static void setDifficultyGenre(T *Diff, std::string s) + { + return; + } + template static Q* getDifficulty(T *Sng, uint32_t idx) { @@ -61,25 +71,40 @@ struct songHelper Q* diff = (Q*)Sng->Difficulties[idx]; return diff; } -}; -Game::VSRG::Song* toSong7K(Game::Song* Sng) -{ - if (Sng && Sng->Mode == MODE_VSRG) - return (Game::VSRG::Song*) Sng; - else - return nullptr; -} + template + static int GetObjCount(T const* diff) + { + return 0; + /*if (diff->Mode == MODE_VSRG) { + return; + }*/ + } + + template + static int GetScoreObjCount(T const* diff) + { + return 0; + /*if (diff->Mode == MODE_VSRG) { + return; + }*/ + } + + template + static void SetObjCount(T* Diff, int s) + { + return; + } -Game::dotcur::Song* toSongDC(Game::Song* Sng) -{ - if (Sng && Sng->Mode == MODE_DOTCUR) - return (Game::dotcur::Song*) Sng; - else - return nullptr; -} -Game::VSRG::Parameters* GameState::GetParameters(int pn) + template + static void SetScoreObjCount(T* diff, int s) + { + return; + } +}; + +Game::VSRG::PlayscreenParameters* GameState::GetParameters(int pn) { if (PlayerNumberInBounds(pn)) return &PlayerInfo[pn].Params; @@ -88,13 +113,8 @@ Game::VSRG::Parameters* GameState::GetParameters(int pn) Game::VSRG::Difficulty * Game::GameState::GetDifficulty(int pn) { - if (PlayerNumberInBounds(pn) && PlayerInfo[pn].Diff) - return PlayerInfo[pn].Diff.get(); - else { - if(GetSelectedSong() && GetSelectedSong()->Mode == MODE_VSRG) - return ((Game::VSRG::Song*)GetSelectedSong())->GetDifficulty(SongWheel::GetInstance().GetDifficulty()); - } - + if(GetSelectedSong()) + return ((Game::VSRG::Song*)GetSelectedSong())->GetDifficulty(SongWheel::GetInstance().GetDifficulty()); return nullptr; } @@ -103,8 +123,7 @@ std::shared_ptr Game::GameState::GetDifficultyShared(int if (PlayerNumberInBounds(pn) && PlayerInfo[pn].Diff) return PlayerInfo[pn].Diff; else { - if(GetSelectedSong()->Mode == MODE_VSRG) - return ((Game::VSRG::Song*)GetSelectedSong())->Difficulties[SongWheel::GetInstance().GetDifficulty()]; + return ((Game::VSRG::Song*)GetSelectedSong())->Difficulties[SongWheel::GetInstance().GetDifficulty()]; } return nullptr; @@ -128,12 +147,18 @@ void GameState::InitializeLua(lua_State *L) .addData("Duration", &Game::Song::Difficulty::Duration, false) .addData("Name", &Game::Song::Difficulty::Name, false) .addData("Offset", &Game::Song::Difficulty::Offset, false) - .addData("Holds", &Game::Song::Difficulty::TotalHolds, false) - .addData("Notes", &Game::Song::Difficulty::TotalNotes, false) - .addData("Objects", &Game::Song::Difficulty::TotalObjects, false) - .addData("ScoreObjects", &Game::Song::Difficulty::TotalScoringObjects, false) + + .addProperty("Objects", &songHelper::GetObjCount, + &songHelper::SetObjCount) + + .addProperty("ScoreObjects", &songHelper::GetScoreObjCount, + &songHelper::SetScoreObjCount) + .addProperty("Author", &songHelper::getDifficultyAuthor, &songHelper::setDifficultyAuthor ) + + .addProperty("Genre", &songHelper::getDifficultyGenre, + &songHelper::setDifficultyGenre) .endClass(); luabridge::getGlobalNamespace(L) @@ -142,9 +167,6 @@ void GameState::InitializeLua(lua_State *L) .addData("Channels", &VSRG::Difficulty::Channels, false) .endClass(); - luabridge::getGlobalNamespace(L) - .deriveClass ("DifficultyDC") - .endClass(); luabridge::getGlobalNamespace(L) .deriveClass ("Song7K") @@ -153,29 +175,24 @@ void GameState::InitializeLua(lua_State *L) .addFunction("GetDifficulty", &VSRG::Song::GetDifficulty) .endClass(); - luabridge::getGlobalNamespace(L) - .deriveClass ("SongDC") - .addProperty("DifficultyCount", &songHelper::getDifficultyCountForSong, - &songHelper::setDifficultyCountForSong) - // .addFunction("GetDifficulty", &songHelper::getDifficulty) - .endClass(); - using namespace Game::VSRG; luabridge::getGlobalNamespace(L) - .beginClass ("Parameters") - .addData("Upscroll", &Parameters::Upscroll) - .addData("Wave", &Parameters::Wave) - .addData("NoFail", &Parameters::NoFail) - .addData("Autoplay", &Parameters::Auto) - .addData("HiddenMode", &Parameters::HiddenMode) - .addData("Rate", &Parameters::Rate) - .addData("Random", &Parameters::Random) - .addData("GaugeType", &Parameters::GaugeType) - .addData("SystemType", &Parameters::SystemType) + .beginClass ("PlayscreenParameters") + .addData("Upscroll", &PlayscreenParameters::Upscroll) + .addData("Wave", &PlayscreenParameters::Wave) + .addData("NoFail", &PlayscreenParameters::NoFail) + .addData("Autoplay", &PlayscreenParameters::Auto) + .addData("HiddenMode", &PlayscreenParameters::HiddenMode) + .addData("Rate", &PlayscreenParameters::Rate) + .addData("Random", &PlayscreenParameters::Random) + .addData("GaugeType", &PlayscreenParameters::GaugeType) + .addData("SystemType", &PlayscreenParameters::SystemType) + .addData("GreenNumber", &PlayscreenParameters::GreenNumber) + .addData("UseW0", &PlayscreenParameters::UseW0) .endClass(); luabridge::getGlobalNamespace(L) - .beginClass ("GameState") + .beginClass ("GameState") .addFunction("GetSelectedSong", &GameState::GetSelectedSong) .addFunction("GetDifficulty", &GameState::GetDifficulty) .addFunction("GetScorekeeper7K", &GameState::GetScorekeeper7K) @@ -186,9 +203,7 @@ void GameState::InitializeLua(lua_State *L) .addFunction("SortWheelBy", &GameState::SortWheelBy) .addFunction("StartScreen", &GameState::StartScreenTransition) .addFunction("ExitScreen", &GameState::ExitCurrentScreen) - .endClass() - .addFunction("toSong7K", toSong7K) - .addFunction("toSongDC", toSongDC); + .endClass(); luabridge::push(L, this); lua_setglobal(L, "Global"); diff --git a/src/GameWindow.cpp b/src/GameWindow.cpp index d7d7c925..8dbbe050 100644 --- a/src/GameWindow.cpp +++ b/src/GameWindow.cpp @@ -3,7 +3,7 @@ #include "GameGlobal.h" #include "Logging.h" #include "BindingsManager.h" -#include "Configuration.h" + #include "Screen.h" #include "Application.h" #include "GameWindow.h" @@ -122,8 +122,6 @@ struct defaultKeys_s { GLFW_KEY_BACKSPACE, KT_BSPC }, { GLFW_MOUSE_BUTTON_LEFT, KT_Select }, { GLFW_MOUSE_BUTTON_RIGHT, KT_SelectRight }, - { 'Z', KT_GameplayClick }, - { 'X', KT_GameplayClick }, { GLFW_KEY_F5, KT_ReloadScreenScripts }, { GLFW_KEY_F10, KT_ReloadCFG } }; @@ -131,7 +129,7 @@ struct defaultKeys_s const int DEFAULT_KEYS_COUNT = sizeof(defaultKeys) / sizeof(defaultKeys_s); // Must match KeyType structure. -char* KeytypeNames[] = { +const char* KeytypeNames[] = { "unknown", "escape", "select", @@ -440,7 +438,7 @@ bool GameWindow::SetupWindow() glEnable(GL_FRAMEBUFFER_SRGB); glEnable(GL_ALPHA_TEST); - // glEnable(GL_POLYGON_SMOOTH); + glEnable(GL_POLYGON_SMOOTH); glAlphaFunc(GL_GREATER, 0); if (VSync) @@ -677,7 +675,7 @@ void GameWindow::RunInput() const float *axisArray = glfwGetJoystickAxes(GLFW_JOYSTICK_1, &axisArraySize); if (axisArraySize) { for (auto i = 0; i < axisArraySize; i++) { - for (auto j = 0; j < SpecialKeys.size(); j++) { + for (size_t j = 0; j < SpecialKeys.size(); j++) { // as before, specialkeys vector value int axis = SpecialKeys[j].boundkey - 1000; diff --git a/src/GameplayParameters7K.cpp b/src/GameplayParameters7K.cpp new file mode 100644 index 00000000..fa2c2e74 --- /dev/null +++ b/src/GameplayParameters7K.cpp @@ -0,0 +1,388 @@ +#include "pch.h" +#include "Song7K.h" +#include "PlayerChartData.h" +#include "NoteTransformations.h" +#include "VSRGMechanics.h" +#include "ScoreKeeper7K.h" + +namespace Game { + namespace VSRG { + + PlayerChartState* PlayscreenParameters::Setup( + double DesiredDefaultSpeed, + int Type, + double Drift, + std::shared_ptr CurrentDiff) + { + /* + * There are four kinds of speed modifiers: + * -CMod (Keep speed the same through the song, equal to a constant) + * -MMod (Find highest speed and set multiplier to such that the highest speed is equal to a constant) + * -First (Find the first speed in the chart, and set multiplier to such that the first speed is equal to a constant) + * -Mode (Find the speed that lasts the most in the chart, set multiplier based on that) + * + * The calculations are done ahead, and while SpeedConstant = 0 either MMod or first are assumed + * but only if there's a constant specified by the user. + */ + + PlayerChartState ChartState; + + if (DesiredDefaultSpeed) + { + DesiredDefaultSpeed /= Rate; + + if (Type == SPEEDTYPE_CMOD) // cmod + { + UserSpeedMultiplier = 1; + + double spd = GreenNumber ? 1000 : DesiredDefaultSpeed; + + ChartState = VSRG::PlayerChartState::FromDifficulty(CurrentDiff.get(), Drift, spd); + } + else + ChartState = VSRG::PlayerChartState::FromDifficulty(CurrentDiff.get(), Drift); + + // if GN is true, Default Speed = GN! + // Convert GN to speed. + if (GreenNumber) { + + // v0 is normal speed + // (green number speed * green number time) / (normal speed * normal time) + // equals + // playfield distance / note distance + + // simplifying that gets you dn / tn lol + double new_speed = PLAYFIELD_SIZE / (DesiredDefaultSpeed / 1000); + DesiredDefaultSpeed = new_speed; + + if (Type == SPEEDTYPE_CMOD) { + UserSpeedMultiplier = DesiredDefaultSpeed / 1000; + } + } + + if (Type == SPEEDTYPE_MMOD) // mmod + { + double speed_max = 0; // Find the highest speed + for (auto i : ChartState.ScrollSpeeds) + { + speed_max = std::max(speed_max, abs(i.Value)); + } + + double Ratio = DesiredDefaultSpeed / speed_max; // How much above or below are we from the maximum speed? + UserSpeedMultiplier = Ratio; + } + else if (Type == SPEEDTYPE_FIRST) // First speed. + { + double DesiredMultiplier = DesiredDefaultSpeed / ChartState.ScrollSpeeds[0].Value; + UserSpeedMultiplier = DesiredMultiplier; + } + else if (Type == SPEEDTYPE_MODE) // Most lasting speed. + { + std::map freq; + for (auto i = ChartState.ScrollSpeeds.begin(); i != ChartState.ScrollSpeeds.end(); i++) + { + if (i + 1 != ChartState.ScrollSpeeds.end()) + { + freq[i->Value] += (i + 1)->Time - i->Time; + } + else freq[i->Value] += abs(CurrentDiff->Duration - i->Time); + } + auto max = -std::numeric_limits::infinity(); + auto val = 1000.f; + for (auto i : freq) + { + if (i.second > max) + { + max = i.second; + val = i.first; + } + } + + UserSpeedMultiplier = DesiredDefaultSpeed / val; + } + else if (Type != SPEEDTYPE_CMOD) // other cases + { + double bpsd = 4.0 / (ChartState.BPS[0].Value); + double Speed = (UNITS_PER_MEASURE / bpsd); + double DesiredMultiplier = DesiredDefaultSpeed / Speed; + + UserSpeedMultiplier = DesiredMultiplier; + } + } + else + ChartState = VSRG::PlayerChartState::FromDifficulty(CurrentDiff.get(), Drift); + + if (Random) + NoteTransform::Randomize(ChartState.NotesByChannel, CurrentDiff->Channels, CurrentDiff->Data->Turntable); + + return new PlayerChartState(ChartState); + } + + std::unique_ptr PlayscreenParameters::PrepareMechanicsSet( + std::shared_ptr CurrentDiff, + std::shared_ptr PlayerScoreKeeper, + double JudgeY) + { + std::unique_ptr MechanicsSet = nullptr; + + // This must be done before setLifeTotal in order for it to work. + PlayerScoreKeeper->setMaxNotes(CurrentDiff->Data->GetScoreItemsCount()); + + PlayerScoreKeeper->setUseW0(UseW0); + + // JudgeScale, Stepmania and OD can't be run together - only one can be set. + auto TimingInfo = CurrentDiff->Data->TimingInfo; + + // Pick a timing system + if (SystemType == VSRG::TI_NONE) { + if (TimingInfo) { + // Automatic setup + SystemType = TimingInfo->GetType(); + } + else { + // Log::Printf("Null timing info - assigning raindrop defaults.\n"); + SystemType = VSRG::TI_RAINDROP; + // pick raindrop system for null Timing Info + } + } + + + // If we got just assigned one or was already requested + // unlikely: timing info type is none? what + + if (SystemType == VSRG::TI_NONE) { + // Player didn't request a specific subsystem + // Log::Printf("System picked was none - on purpose. Defaulting to raindrop.\n"); + SystemType = VSRG::TI_RAINDROP; + } + + TimingType UsedTimingType = SetupGameSystem(TimingInfo, PlayerScoreKeeper.get()); + + /* + If we're on TT_BEATS we've got to recalculate all note positions to beats, + and use mechanics that use TT_BEATS as its timing type. + */ + + bool disable_forced_release = SystemType == VSRG::TI_BMS || + SystemType == VSRG::TI_RDAC || + SystemType == VSRG::TI_STEPMANIA; + if (UsedTimingType == VSRG::TT_TIME) + { + if (SystemType == VSRG::TI_RDAC) + { + // Log::Printf("RAINDROP ARCADE STAAAAAAAAART!\n"); + MechanicsSet = std::make_unique(); + } + else { + // Log::Printf("Using raindrop mechanics set!\n"); + // Only forced release if not a bms or a stepmania chart. + MechanicsSet = std::make_unique(!disable_forced_release); + } + } + else if (UsedTimingType == TT_BEATS) + { + //Log::Printf("Using o2jam mechanics set!\n"); + MechanicsSet = std::make_unique(); + } + + MechanicsSet->Setup(CurrentDiff.get(), PlayerScoreKeeper); + SetupGauge(TimingInfo, PlayerScoreKeeper.get()); + UpdateHidden(JudgeY); + + return MechanicsSet; + } + + void PlayscreenParameters::UpdateHidden(double JudgeY) + { + /* + Given the top of the screen being 1, the bottom being -1 + calculate the range for which the current hidden mode is defined. + */ + CfgVar HiddenSize("HiddenSize", "Hidden"); + CfgVar FLSize("FlashlightSize", "Hidden"); + CfgVar Threshold("Threshold", "Hidden"); + + auto toYRange = [](float x) { + x /= ScreenHeight; // [0,768] -> [0,1] + x *= -2; // [0,1] -> [0, -2] + x += 1; // [1, -1] + return x; + }; + + float Center = toYRange(Threshold * PLAYFIELD_SIZE); + + // Hidden calc + if (HiddenMode) + { + float pfCenter; + + Hidden.TransitionSize = HiddenSize * PLAYFIELD_SIZE / ScreenHeight; + + if (Upscroll) + { + pfCenter = toYRange(ScreenHeight - JudgeY + Threshold * PLAYFIELD_SIZE); + Center = pfCenter; + + + // Invert Hidden Mode. + if (HiddenMode == HM_SUDDEN) Hidden.Mode = HM_HIDDEN; + else if (HiddenMode == HM_HIDDEN) Hidden.Mode = HM_SUDDEN; + else Hidden.Mode = (Game::VSRG::EHiddenMode)HiddenMode; + } + else + { + pfCenter = Center; + Center = pfCenter; + Hidden.Mode = (Game::VSRG::EHiddenMode)HiddenMode; + } + + Hidden.CenterSize = FLSize * PLAYFIELD_SIZE / ScreenHeight; + } + } + + ScoreType PlayscreenParameters::GetScoringType() const + { + if (SystemType == TI_BMS || SystemType == TI_RDAC) { + return ST_EX; + } + + if (SystemType == VSRG::TI_O2JAM) { + return ST_O2JAM; + } + + if (SystemType == VSRG::TI_OSUMANIA) { + return ST_OSUMANIA; + } + + if (SystemType == VSRG::TI_STEPMANIA) { + return ST_DP; + } + + if (SystemType == VSRG::TI_RAINDROP) { + return ST_EXP3; + } + + return ST_EX; + } + + int PlayscreenParameters::GetHiddenMode() + { + return Hidden.Mode; + } + + float PlayscreenParameters::GetHiddenCenter() + { + return Hidden.Center; + } + + float PlayscreenParameters::GetHiddenTransitionSize() + { + return Hidden.TransitionSize; + } + + float PlayscreenParameters::GetHiddenCenterSize() + { + return Hidden.CenterSize; + } + + TimingType PlayscreenParameters::SetupGameSystem( + std::shared_ptr TimingInfo, + ScoreKeeper* PlayerScoreKeeper) { + TimingType UsedTimingType = TT_TIME; + + if (SystemType == VSRG::TI_BMS || SystemType == VSRG::TI_RDAC) { + UsedTimingType = TT_TIME; + if (TimingInfo->GetType() == VSRG::TI_BMS) { + auto Info = static_cast (TimingInfo.get()); + if (!Info->PercentualJudgerank) + PlayerScoreKeeper->setJudgeRank(Info->JudgeRank); + else + PlayerScoreKeeper->setJudgeScale(Info->JudgeRank / 100.0); + } + else { + PlayerScoreKeeper->setJudgeRank(2); + } + } + else if (SystemType == VSRG::TI_O2JAM) { + UsedTimingType = TT_BEATS; + PlayerScoreKeeper->setJudgeRank(-100); + } + else if (SystemType == VSRG::TI_OSUMANIA) { + UsedTimingType = TT_TIME; + if (TimingInfo->GetType() == VSRG::TI_OSUMANIA) { + auto InfoOM = static_cast (TimingInfo.get()); + PlayerScoreKeeper->setODWindows(InfoOM->OD); + } + else PlayerScoreKeeper->setODWindows(7); + } + else if (SystemType == VSRG::TI_STEPMANIA) { + UsedTimingType = TT_TIME; + PlayerScoreKeeper->setSMJ4Windows(); + } + else if (SystemType == VSRG::TI_RAINDROP) { + // LifebarType = LT_STEPMANIA; + } + + return UsedTimingType; + } + void PlayscreenParameters::SetupGauge(std::shared_ptr TimingInfo, ScoreKeeper* PlayerScoreKeeper) + { + if (GaugeType == LT_AUTO) { + using namespace VSRG; + switch (SystemType) { + case TI_BMS: + case TI_RAINDROP: + case TI_RDAC: + GaugeType = LT_GROOVE; + break; + case TI_O2JAM: + GaugeType = LT_O2JAM; + break; + case TI_OSUMANIA: + case TI_STEPMANIA: + GaugeType = LT_STEPMANIA; + break; + default: + throw std::runtime_error("Invalid requested system."); + } + } + + switch (GaugeType) { + case LT_STEPMANIA: + // LifebarType = LT_STEPMANIA; // Needs no setup. + break; + + case LT_O2JAM: + if (TimingInfo->GetType() == VSRG::TI_O2JAM) { + auto InfoO2 = static_cast (TimingInfo.get()); + PlayerScoreKeeper->setO2LifebarRating(InfoO2->Difficulty); + } // else by default + // LifebarType = LT_O2JAM; // By default, HX + break; + + case LT_GROOVE: + case LT_DEATH: + case LT_EASY: + case LT_EXHARD: + case LT_SURVIVAL: + if (TimingInfo->GetType() == VSRG::TI_BMS) { // Only needs setup if it's a BMS file + auto Info = static_cast (TimingInfo.get()); + if (Info->IsBMSON) + PlayerScoreKeeper->setLifeTotal(NAN, Info->GaugeTotal / 100.0); + else + PlayerScoreKeeper->setLifeTotal(Info->GaugeTotal); + } + else // by raindrop defaults + PlayerScoreKeeper->setLifeTotal(-1); + // LifebarType = (LifeType)Parameters.GaugeType; + break; + case LT_NORECOV: + // ... + break; + default: + throw std::runtime_error("Invalid gauge type recieved"); + } + + } + } +} \ No newline at end of file diff --git a/src/GraphicalString.cpp b/src/GraphicalString.cpp index b13647bb..e403f4e6 100644 --- a/src/GraphicalString.cpp +++ b/src/GraphicalString.cpp @@ -10,6 +10,8 @@ GraphicalString::GraphicalString() { SetSize(1); // Default size, so it doesn't scale to 0 SetZ(0); + mFontHeight = 16; + mKernScale = 1; } void GraphicalString::SetFont(Font* _Font) @@ -32,10 +34,38 @@ std::string GraphicalString::GetText() const return mText; } +float GraphicalString::GetKerningScale() const +{ + return mKernScale; +} + +void GraphicalString::SetKerningScale(float ks) +{ + mKernScale = ks; +} + +float GraphicalString::GetTextSize() const +{ + if (!mFont) return 0.0f; + return mFont->GetHorizontalLength(mText.c_str()) / SDF_SIZE * mFontHeight * mKernScale; +} + +void GraphicalString::SetFontSize(float fsize) +{ + mFontHeight = fsize; +} + +float GraphicalString::GetFontSize() const +{ + return mFontHeight; +} + void GraphicalString::Render() { if (!mFont) return; mFont->SetColor(Red, Green, Blue); mFont->SetAlpha(Alpha); - mFont->Render(mText, Vec2(0, 0), GetMatrix()); + + float sc = mFontHeight; + mFont->Render(mText, Vec2(0, 0), GetMatrix(), Vec2(mKernScale, sc)); } \ No newline at end of file diff --git a/src/GraphicalString.h b/src/GraphicalString.h index 36a584db..4c0367f7 100644 --- a/src/GraphicalString.h +++ b/src/GraphicalString.h @@ -6,6 +6,8 @@ class GraphicalString : public Sprite { std::string mText; Font* mFont; + float mFontHeight; + float mKernScale; public: GraphicalString(); void SetText(std::string _Text); @@ -13,5 +15,11 @@ class GraphicalString : public Sprite void SetFont(Font* _Font); Font* GetFont() const; + float GetKerningScale() const; + void SetKerningScale(float ks); + float GetTextSize() const; + void SetFontSize(float fsize); + float GetFontSize() const; + void Render() override; }; diff --git a/src/GuiButton.cpp b/src/GuiButton.cpp index 9cbd1114..2141090e 100644 --- a/src/GuiButton.cpp +++ b/src/GuiButton.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "GameWindow.h" #include "GuiButton.h" diff --git a/src/GuiTextPrompt.cpp b/src/GuiTextPrompt.cpp index 1aef6ac1..92aa7f13 100644 --- a/src/GuiTextPrompt.cpp +++ b/src/GuiTextPrompt.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "BitmapFont.h" #include "GuiTextPrompt.h" @@ -64,7 +64,7 @@ void TextPrompt::Render() { if (mOpen) { - std::string str = Utility::Format("%s\n\n%s_\n\nPress Enter to Confirm or Escape to Abort", mPromptText, mBufferText); + std::string str = Utility::Format("%s\n\n%s_\n\nPress Enter to Confirm or Escape to Abort", mPromptText.c_str(), mBufferText.c_str()); if (mPromptFont) mPromptFont->Render(str, Vec2(100, 200)); // todo: change position } diff --git a/src/ImageLoader.cpp b/src/ImageLoader.cpp index 38b8f089..9807bd85 100644 --- a/src/ImageLoader.cpp +++ b/src/ImageLoader.cpp @@ -1,7 +1,7 @@ #include "pch.h" #include "Logging.h" -#include "Configuration.h" + #include "Texture.h" #include "ImageLoader.h" @@ -12,9 +12,11 @@ std::map ImageLoader::Textures; std::map ImageLoader::PendingUploads; CfgVar ImageLoaderMessages("ImageLoader", "Debug"); +CfgVar XorTexture("XorTexture", "Debug"); ImageLoader::ImageLoader() { + } ImageLoader::~ImageLoader() @@ -50,6 +52,8 @@ void ImageLoader::UnloadAll() void ImageLoader::DeleteImage(Texture* &ToDelete) { + if (ToDelete == Renderer::GetXorTexture()) return; + if (ToDelete) { auto tex = Textures.find(ToDelete->fname); if (tex != Textures.end()) { @@ -70,6 +74,7 @@ void ImageLoader::DeleteImage(Texture* &ToDelete) Texture* ImageLoader::InsertImage(std::filesystem::path Name, ImageData &imgData) { Texture* I; + if (XorTexture) return Renderer::GetXorTexture(); if (imgData.Data.size() == 0) return nullptr; @@ -177,7 +182,8 @@ ImageData ImageLoader::GetDataForImage(std::filesystem::path filename) return {}; } - auto file = std::ifstream{ filename.string(), std::ios::binary }; + // this macro warps around windows/linux stuff wrt wide strings + CreateBinIfstream(file, filename); if (!file.is_open()) { if (ImageLoaderMessages) { @@ -209,6 +215,8 @@ ImageData ImageLoader::GetDataForImageFromMemory(const unsigned char* const buff Texture* ImageLoader::Load(std::filesystem::path filename) { + if (XorTexture) return Renderer::GetXorTexture(); + if (std::filesystem::is_directory(filename)) return NULL; if (Textures.find(filename) != Textures.end() && Textures[filename]->IsValid) { @@ -229,6 +237,9 @@ Texture* ImageLoader::Load(std::filesystem::path filename) void ImageLoader::AddToPending(std::filesystem::path Filename) { UploadData New; + + if (XorTexture) return; + if (Textures.find(Filename) == Textures.end()) { auto d = GetDataForImage(Filename); @@ -242,7 +253,7 @@ void ImageLoader::AddToPending(std::filesystem::path Filename) } /* For multi-threaded loading. */ -void ImageLoader::LoadFromManifest(char** Manifest, int Count, std::string Prefix) +void ImageLoader::LoadFromManifest(const char** Manifest, int Count, std::string Prefix) { for (int i = 0; i < Count; i++) { diff --git a/src/ImageLoader.h b/src/ImageLoader.h index d0337682..841845da 100644 --- a/src/ImageLoader.h +++ b/src/ImageLoader.h @@ -28,7 +28,7 @@ class ImageLoader /* For multi-threaded loading. */ static void AddToPending(std::filesystem::path Filename); - static void LoadFromManifest(char** Manifest, int Count, std::string Prefix = ""); + static void LoadFromManifest(const char** Manifest, int Count, std::string Prefix = ""); static void UpdateTextures(); static ImageData GetDataForImage(std::filesystem::path filename); static ImageData GetDataForImageFromMemory(const unsigned char *const buffer, size_t len); diff --git a/src/LuaAnimationInterface.cpp b/src/LuaAnimationInterface.cpp index d4a7036f..1ca4a771 100644 --- a/src/LuaAnimationInterface.cpp +++ b/src/LuaAnimationInterface.cpp @@ -7,7 +7,7 @@ #include "Sprite.h" #include "LuaManager.h" #include "SceneEnvironment.h" -#include "Configuration.h" + #include "LuaBridge.h" #include "Font.h" @@ -166,7 +166,8 @@ struct O2DProxy return obj->GetRotation(); } - static Transformation getChainTransformation(Sprite const* obj) + template + static Transformation getChainTransformation(T const* obj) { return Transformation(); } @@ -211,7 +212,8 @@ struct O2DProxy obj->SetPositionY(param); } - static void setChainTransformation(Sprite *obj, Transformation* param) + template + static void setChainTransformation(T *obj, Transformation* param) { obj->ChainTransformation(param); } @@ -265,25 +267,7 @@ class LShader : public Renderer::Shader { void DefineSpriteInterface(LuaManager* anim_lua) { - anim_lua->AppendPath("./?;./?.lua"); - anim_lua->AppendPath(GameState::GetInstance().GetScriptsDirectory() + "?"); - anim_lua->AppendPath(GameState::GetInstance().GetScriptsDirectory() + "?.lua"); - - anim_lua->AppendPath(GameState::GetInstance().GetSkinPrefix() + "?"); - anim_lua->AppendPath(GameState::GetInstance().GetSkinPrefix() + "?.lua"); - - // anim_lua->AppendPath(GameState::GetFallbackSkinPrefix()); - anim_lua->Register(LuaAnimFuncs::Require, "skin_require"); - anim_lua->Register(LuaAnimFuncs::FallbackRequire, "fallback_require"); - - // anim_lua->NewMetatable(LuaAnimFuncs::SpriteMetatable); - anim_lua->Register(LuaAnimFuncs::GetSkinConfigF, "GetConfigF"); - anim_lua->Register(LuaAnimFuncs::GetSkinConfigS, "GetConfigS"); - anim_lua->Register(LuaAnimFuncs::GetSkinDirectory, "GetSkinDirectory"); - anim_lua->Register(LuaAnimFuncs::GetSkinFile, "GetSkinFile"); - - anim_lua->SetGlobal("ScreenHeight", ScreenHeight); - anim_lua->SetGlobal("ScreenWidth", ScreenWidth); + AddRDLuaGlobal(anim_lua); // Animation constants anim_lua->SetGlobal("EaseNone", Animation::EaseLinear); @@ -348,11 +332,34 @@ void DefineSpriteInterface(LuaManager* anim_lua) .q(Height) .q(X) .q(Y) - .q(ChainTransformation) + .addProperty("ChainTransformation", &O2DProxy::getChainTransformation, &O2DProxy::setChainTransformation) .addProperty("Texture", GetImage, SetImage) // Special for setting image. .endClass(); } +void AddRDLuaGlobal(LuaManager * anim_lua) +{ + anim_lua->AppendPath("./?;./?.lua"); + anim_lua->AppendPath(GameState::GetInstance().GetScriptsDirectory() + "?"); + anim_lua->AppendPath(GameState::GetInstance().GetScriptsDirectory() + "?.lua"); + + anim_lua->AppendPath(GameState::GetInstance().GetSkinPrefix() + "?"); + anim_lua->AppendPath(GameState::GetInstance().GetSkinPrefix() + "?.lua"); + + // anim_lua->AppendPath(GameState::GetFallbackSkinPrefix()); + anim_lua->Register(LuaAnimFuncs::Require, "skin_require"); + anim_lua->Register(LuaAnimFuncs::FallbackRequire, "fallback_require"); + + // anim_lua->NewMetatable(LuaAnimFuncs::SpriteMetatable); + anim_lua->Register(LuaAnimFuncs::GetSkinConfigF, "GetConfigF"); + anim_lua->Register(LuaAnimFuncs::GetSkinConfigS, "GetConfigS"); + anim_lua->Register(LuaAnimFuncs::GetSkinDirectory, "GetSkinDirectory"); + anim_lua->Register(LuaAnimFuncs::GetSkinFile, "GetSkinFile"); + + anim_lua->SetGlobal("ScreenHeight", ScreenHeight); + anim_lua->SetGlobal("ScreenWidth", ScreenWidth); +} + // New lua interface. void CreateNewLuaAnimInterface(LuaManager *AnimLua) { @@ -393,7 +400,7 @@ void CreateNewLuaAnimInterface(LuaManager *AnimLua) .addFunction("GetLength", &Font::GetHorizontalLength) .endClass() .deriveClass ("TruetypeFont") - .addConstructor () + .addConstructor () .endClass() .deriveClass ("BitmapFont") .addConstructor() @@ -401,11 +408,15 @@ void CreateNewLuaAnimInterface(LuaManager *AnimLua) .addFunction("LoadBitmapFont", LoadBmFont) .endNamespace(); - luabridge::getGlobalNamespace(AnimLua->GetState()) - .deriveClass("StringObject2D") - .addConstructor () - .addProperty("Font", &GraphicalString::GetFont, &GraphicalString::SetFont) - .addProperty("Text", &GraphicalString::GetText, &GraphicalString::SetText) + luabridge::getGlobalNamespace(AnimLua->GetState()) + .deriveClass("StringObject2D") + .addConstructor () + .addProperty("Font", &GraphicalString::GetFont, &GraphicalString::SetFont) + .addProperty("Text", &GraphicalString::GetText, &GraphicalString::SetText) + .addProperty("FontSize", &GraphicalString::GetFontSize, &GraphicalString::SetFontSize) + .addProperty("TextSize", &GraphicalString::GetTextSize) + .addProperty("KernScale", &GraphicalString::GetKerningScale, &GraphicalString::SetKerningScale) + .addProperty("ChainTransformation", &O2DProxy::getChainTransformation, &O2DProxy::setChainTransformation) .endClass(); } diff --git a/src/LuaManager.cpp b/src/LuaManager.cpp index 054279db..83e3e24e 100644 --- a/src/LuaManager.cpp +++ b/src/LuaManager.cpp @@ -80,7 +80,7 @@ bool LuaManager::RunScript(std::filesystem::path file) Log::LogPrintf("LuaManager: Running script %s.\n", Utility::ToU8(file.wstring()).c_str()); - if ((errload = luaL_loadfile(State, Utility::ToLocaleStr(file).c_str()))) { + if ((errload = luaL_loadfile(State, Utility::ToLocaleStr(file.wstring()).c_str()))) { reportError(State); return false; } @@ -88,7 +88,7 @@ bool LuaManager::RunScript(std::filesystem::path file) lua_pushcfunction(State, LuaPanic); lua_insert(State, -2); - if (errcall = lua_pcall(State, 0, 0, -2)) + if ((errcall = lua_pcall(State, 0, 0, -2))) { reportError(State); Pop(); // remove pushed panic function diff --git a/src/LuaManager.h b/src/LuaManager.h index 51b84f5b..4278c7fc 100644 --- a/src/LuaManager.h +++ b/src/LuaManager.h @@ -111,3 +111,5 @@ T* GetUserObject(lua_State *L, int Parameter, const char* MetatableName) luaL_argcheck(L, ud != NULL, 1, "Expected object of different type!"); return ud; } + +void AddRDLuaGlobal(LuaManager * anim_lua); \ No newline at end of file diff --git a/src/NPSGraph.cpp b/src/NPSGraph.cpp index ee4a0043..1cf8c276 100644 --- a/src/NPSGraph.cpp +++ b/src/NPSGraph.cpp @@ -79,7 +79,7 @@ class NPSGraph out << "" << endl; - auto ptIdx = 0; + size_t ptIdx = 0; float ImageHeight = CfgValNPS("GraphHeight", 300); float GraphYOffset = CfgValNPS("GraphYOffs", 50); float GraphXOffset = CfgValNPS("GraphXOffs", 100); @@ -102,7 +102,7 @@ class NPSGraph if (!DiffAuth.length()) DiffAuth = "an anonymous charter"; - float avgNPS = Song->GetDifficulty(diffIndex)->TotalScoringObjects / Song->GetDifficulty(diffIndex)->Duration; + float avgNPS = Song->GetDifficulty(diffIndex)->Data->GetScoreItemsCount() / Song->GetDifficulty(diffIndex)->Duration; out << Utility::Format("%s - %s (%s) by %s (Max NPS: %.2f/Avg NPS: %.2f)", TextXOffset, TextYOffset, diff --git a/src/NoteLoaderBMS.cpp b/src/NoteLoaderBMS.cpp index 14e76063..44a36df2 100644 --- a/src/NoteLoaderBMS.cpp +++ b/src/NoteLoaderBMS.cpp @@ -25,6 +25,7 @@ */ namespace NoteLoaderBMS{ + using namespace Game::VSRG; @@ -334,10 +335,7 @@ namespace NoteLoaderBMS{ Note.Sound = ev.Event; UsedSounds[ev.Event] = true; - Chart->TotalScoringObjects++; - Chart->TotalNotes++; - Chart->TotalObjects++; - + Msr.Notes[Track].push_back(Note); /* For future reference: @@ -355,8 +353,6 @@ namespace NoteLoaderBMS{ { if (LastNotes[Track]) { - Chart->TotalHolds++; - Chart->TotalScoringObjects++; LastNotes[Track]->EndTime = Time; LastNotes[Track] = nullptr; } @@ -385,10 +381,6 @@ namespace NoteLoaderBMS{ Note.Sound = ev.Event; UsedSounds[ev.Event] = true; - Chart->TotalScoringObjects += 2; - Chart->TotalHolds++; - Chart->TotalObjects++; - Msr.Notes[Track].push_back(Note); startTime[Track] = -1; @@ -687,7 +679,7 @@ namespace NoteLoaderBMS{ int Limit = std::stoi(Contents.c_str()); assert(CurrentNestedLevel < 16); - assert(Limit > 1); + //assert(Limit > 1); RandomStack[CurrentNestedLevel] = std::randint(1, Limit); @@ -797,9 +789,16 @@ namespace NoteLoaderBMS{ TimingInfo->GaugeTotal = total; } + void SetDefexRank(double defex) + { + TimingInfo->JudgeRank = defex; + TimingInfo->PercentualJudgerank = true; + } + void SetJudgeRank(double judgerank) { TimingInfo->JudgeRank = judgerank; + TimingInfo->PercentualJudgerank = false; } void SetSound(int index, std::string command_contents) @@ -964,9 +963,12 @@ namespace NoteLoaderBMS{ Utility::ReplaceAll(Line, "[\r\n]", ""); - if (Line.length() == 0 || Line[0] != '#') + if (Line.length() == 0 || Line.find_first_of("#") == std::string::npos) continue; + // allow indentation + Line = Line.substr(Line.find_first_of("#")); + #define OnCommand(x) if(command == #x) #define OnCommandSub(x) if(command.substr(0, strlen(#x)) == #x) @@ -1006,7 +1008,7 @@ namespace NoteLoaderBMS{ OnCommand(#genre) { - // stub + Diff->Data->Genre = CommandContents; } OnCommand(#subtitle) @@ -1069,6 +1071,11 @@ namespace NoteLoaderBMS{ Out->PreviewTime = latof(CommandContents.c_str()); } + OnCommand(#defexrank) + { + Info->SetDefexRank(latof(CommandContents.c_str())); + } + OnCommand(#stagefile) { Diff->Data->StageFile = CommandContents; @@ -1132,7 +1139,8 @@ namespace NoteLoaderBMS{ OnCommand(#playlevel) { - Diff->Level = std::stoi(CommandContents.c_str()); + if (CommandContents != "") + Diff->Level = std::stoi(CommandContents); } OnCommand(#rank) diff --git a/src/NoteLoaderBMSON.cpp b/src/NoteLoaderBMSON.cpp index 26b407a8..d13de4cc 100644 --- a/src/NoteLoaderBMSON.cpp +++ b/src/NoteLoaderBMSON.cpp @@ -25,18 +25,6 @@ namespace NoteLoaderBMSON double Length; // in beats }; - const struct - { - int bmson_level; - int rank_level; - } level_bindings[] = { - { 52, 1 }, - { 60, 2 }, - { 70, 3 }, - { 100, 4 }, - { 120, 5 } - }; - struct { const char* hint; @@ -89,7 +77,9 @@ namespace NoteLoaderBMSON std::string GetSubartist(const char *string) { - std::regex sreg(Utility::Format("\\s*%s\\s*:\\s*(.*?)\\s*$", string)); + std::regex sreg(Utility::Format("\\s*%s\\s*:\\s*(.*?)\\s*$", string), + std::regex_constants::icase | std::regex_constants::ECMAScript); + for (const auto& s : root["info"]["subartists"]) { std::smatch sm; @@ -166,7 +156,7 @@ namespace NoteLoaderBMSON auto meta = root["info"]; song->SongName = NoteLoaderBMS::GetSubtitles(meta["title"].asString(), subtitles); song->SongAuthor = meta["artist"].asString(); - song->Subtitle = Utility::Join(subtitles, " "); + song->Subtitle = meta["subtitle"].asString() + Utility::Join(subtitles, " "); song->BackgroundFilename = meta["back_image"].asString(); @@ -207,8 +197,8 @@ namespace NoteLoaderBMSON song->SongPreviewSource = meta["preview_music"].asString(); - // DEFEXRANK? - int jRank; + // DEFEXRANK! + double jRank; Json::Value jr; if (version == UNSPECIFIED_VERSION) @@ -217,13 +207,13 @@ namespace NoteLoaderBMSON jr = meta["judge_rank"]; if (!jr.isNull()) - jRank = jr.asInt(); + jRank = jr.asDouble(); else jRank = 100; - for (auto v : level_bindings) - if (v.bmson_level == jRank) - TimingInfo->JudgeRank = v.rank_level; + TimingInfo->JudgeRank = jRank; + TimingInfo->PercentualJudgerank = true; + TimingInfo->GaugeTotal = meta["total"].asInt(); } @@ -293,7 +283,7 @@ namespace NoteLoaderBMSON double prev = 0; - for (int i = 0; i < Measure; i++) // set length of last measure to difference between last note and last measure's beats. + for (size_t i = 0; i < Measure; i++) // set length of last measure to difference between last note and last measure's beats. prev += Chart->Data->Measures[i].Length; Chart->Data->Measures[Chart->Data->Measures.size() - 1].Length = FindLastNoteBeat() - prev; @@ -525,9 +515,18 @@ namespace NoteLoaderBMSON auto notes = (*audio)["notes"]; for (auto ¬e : notes) - objs.push_back(BmsonObject{ note["x"].asInt(), note["y"].asDouble(), note["l"].asDouble(), note["c"].asBool() }); - - std::stable_sort(objs.begin(), objs.end(), [](const BmsonObject& l, const BmsonObject& r) -> bool { return l.y < r.y; }); + objs.push_back(BmsonObject + { + note["x"].asInt(), + note["y"].asDouble(), + note["l"].asDouble(), + note["c"].asBool() + }); + + std::stable_sort(objs.begin(), objs.end(), + [](const BmsonObject& l, const BmsonObject& r) -> bool { + return l.y < r.y; + }); JoinBGMSlices(objs); for (auto note = objs.begin(); note != objs.end(); ++note) @@ -544,27 +543,41 @@ namespace NoteLoaderBMSON auto& bga = root["bga"]; if (!bga.isNull()) { + bool hasData = false; for (auto &bgi : bga["bga_header"]) out->BMPList[bgi["id"].asInt()] = CleanFilename(bgi["name"].asString()); if (version != VERSION_1) { - for (auto &bg0 : bga["bga_notes"]) + for (auto &bg0 : bga["bga_notes"]) { out->BMPEventsLayerBase.push_back(AutoplayBMP(TimeForObj(bg0["y"].asDouble() / resolution), bg0["id"].asInt())); - for (auto &bg0 : bga["layer_notes"]) + hasData = true; + } + for (auto &bg0 : bga["layer_notes"]) { out->BMPEventsLayer.push_back(AutoplayBMP(TimeForObj(bg0["y"].asDouble() / resolution), bg0["id"].asInt())); - for (auto &bg0 : bga["poor_notes"]) + hasData = true; + } + for (auto &bg0 : bga["poor_notes"]) { out->BMPEventsLayerMiss.push_back(AutoplayBMP(TimeForObj(bg0["y"].asDouble() / resolution), bg0["id"].asInt())); + hasData = true; + } } else { - for (auto &bg0 : bga["bga_events"]) + for (auto &bg0 : bga["bga_events"]) { out->BMPEventsLayerBase.push_back(AutoplayBMP(TimeForObj(bg0["y"].asDouble() / resolution), bg0["id"].asInt())); - for (auto &bg0 : bga["layer_events"]) + hasData = true; + } + for (auto &bg0 : bga["layer_events"]) { out->BMPEventsLayer.push_back(AutoplayBMP(TimeForObj(bg0["y"].asDouble() / resolution), bg0["id"].asInt())); - for (auto &bg0 : bga["poor_events"]) + hasData = true; + } + for (auto &bg0 : bga["poor_events"]) { out->BMPEventsLayerMiss.push_back(AutoplayBMP(TimeForObj(bg0["y"].asDouble() / resolution), bg0["id"].asInt())); + hasData = true; + } } - Chart->Data->BMPEvents = out; + if (hasData) + Chart->Data->BMPEvents = out; } } public: @@ -610,21 +623,15 @@ namespace NoteLoaderBMSON if (note.second.Length) { new_note.EndTime = TimeForObj(note.first + note.second.Length); - Chart->TotalHolds++; - Chart->TotalScoringObjects++; } new_note.Sound = note.second.Sound; - int Measure = MeasureForBeat(note.first); + auto Measure = MeasureForBeat(note.first); if (Measure >= Chart->Data->Measures.size()) Chart->Data->Measures.resize(Measure + 1); Chart->Data->Measures[MeasureForBeat(note.first)].Notes[lane.first].push_back(new_note); - Chart->TotalObjects++; - Chart->TotalScoringObjects++; - Chart->TotalNotes++; - Chart->Duration = std::max(std::max(Chart->Duration, new_note.StartTime), new_note.EndTime); } } diff --git a/src/NoteLoaderDC.cpp b/src/NoteLoaderDC.cpp deleted file mode 100644 index 9ab6df10..00000000 --- a/src/NoteLoaderDC.cpp +++ /dev/null @@ -1,217 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "SongDC.h" -#include "NoteLoaderDC.h" - -/* Note Loader for the .dcf format. Heavily inspired by Stepmania. */ -/* Even for dotcur, I wouldn't use this anymore, I just keep it for historical purposes. I'd use bmson. */ - -using namespace Game::dotcur; - -float _ScreenDifference() -{ - return std::abs(float((ScreenWidth / 2.f) - (PlayfieldWidth / 2.f))); -} - -void LoadNotes(Song* Out, Difficulty * Diff, std::string line) -{ - // get the object std::string (all between a colon and a semicolon. - std::string objectString = line.substr(line.find_first_of(":") + 1); - std::vector< std::string > splitvec; - bool invert = false; - - Diff->Name = Out->SongName; // todo: change this. - Diff->TotalNotes = Diff->TotalHolds = Diff->TotalObjects = 0; - - // Remove whitespace. - Utility::ReplaceAll(objectString, "[\n\r]", ""); - - splitvec = Utility::TokenSplit(objectString); // Separate measures! - for (std::string objectlist : splitvec) // for each measure - { - std::vector< std::string > splitobjects; - Measure Msr; - invert = false; - - if (objectlist.length() == 0) - { - Diff->Measures.push_back(Msr); - continue; - } - - /* Mirror command. */ - if (objectlist[0] == 'M') - { - invert = true; - Utility::ReplaceAll(objectlist, "M", ""); - } - - splitobjects = Utility::TokenSplit(objectlist, "{}", true); - size_t SoSize = 0; - size_t CurObj = 0; - - for (std::string object_description : splitobjects) // Count total valid objects - { - if (object_description.length() != 0) - SoSize += 1; - } - - for (std::string object_description : splitobjects) // For all objects in measure - { - std::vector< std::string > object_parameters; - - if (object_description.length() == 0) // we must have at least a plain "0" - continue; - - object_parameters = Utility::TokenSplit(object_description, " :"); - if (object_parameters.size() > 0) // We got a position - { - int32_t xpos = 0; - float hold_duration = 0; - int32_t sound = 0; - - if (object_parameters[0].length() > 0) // does it have length? - xpos = latof(object_parameters[0].c_str()); // assign it - - if (object_parameters.size() > 1) // We got a hold note parameter - { - if (object_parameters[1].length() > 0) // length? - hold_duration = latof(object_parameters[1].c_str()); // load it in - - if (object_parameters.size() > 2) // We got a sound parameter - { - if (object_parameters[2].length() > 0) // got a valid sound? - sound = latof(object_parameters[2].c_str()); // cast it in - } - } - - if (invert) - { - if (xpos != 0) - xpos = PlayfieldWidth - xpos; - } - - GameObject Temp; - - if (xpos != 0) - { - Temp.SetPositionX(xpos); - Diff->TotalObjects++; - - if (hold_duration) - Diff->TotalHolds++; - else - Diff->TotalNotes++; - } - else - { - /* Position 0 is a special X constant that will make the note invisible - as well as making it not emit any kind of judgment in-game. It's filler. */ - Temp.SetPositionX(0); - } - - Temp.Assign(hold_duration, Diff->Measures.size(), (double)CurObj / SoSize); - Msr.push_back(Temp); - } // got a position - CurObj++; - } // foreach object in measure - - Diff->Measures.push_back(Msr); - } // foreach measure - - Out->Difficulties.push_back(Diff); -} - -Song* NoteLoaderDC::LoadObjectsFromFile(std::filesystem::path filename, std::filesystem::path prefix) -{ - std::ifstream filein(filename.string()); - Song *Out = new Song(); - Difficulty *Diff = new Difficulty(); - - if (!filein.is_open()) - { - throw std::runtime_error(Utility::Format("Unable to open %s for reading!", filename.c_str()).c_str()); - } - - Out->SongDirectory = prefix; - - // get lines separating with ; token - std::string line; - while (filein) - { - std::getline(filein, line, ';'); - std::string command = line.substr(0, line.find_first_of(":")); - -#define OnCommand(x) if(command.find(#x)!=std::string::npos) - - std::string CommandContents = line.substr(line.find_first_of(":") + 1); - - // First, metadata. - OnCommand(#NAME) - { - Out->SongName = CommandContents; - } - - OnCommand(#AUTHOR) - { - Out->SongAuthor = CommandContents; - } - - OnCommand(#MLEN) - { - std::stringstream str(CommandContents); - str >> Out->MeasureLength; - } - - // Then, Timing data. - OnCommand(#BPM) - { - LoadTimingList(Diff->Timing, line); - } - - OnCommand(#OFFSET) - { - std::stringstream str(CommandContents); - str >> Diff->Offset; - Diff->Offset += Configuration::GetConfigf("OffsetDC"); - } - - // Then, file info. - OnCommand(#SONG) - { - Out->SongFilename = CommandContents; - } - - OnCommand(#BACKGROUNDIMAGE) - { - Out->BackgroundFilename = CommandContents; - } - - OnCommand(#LEADIN) - { - std::stringstream str(CommandContents); - str >> Out->LeadInTime; - } - - OnCommand(#SOUNDS) - { - std::vector SoundList; - std::string CmdLine = CommandContents; - // Diff->SoundList = Utility::TokenSplit(CmdLine, ","); - } - - // Then, the charts. - OnCommand(#NOTES) // current command is notes? - { - LoadNotes(Out, Diff, line); - Diff = new Difficulty(); - }// command == #notes -#undef OnCommand - } - delete Diff; // There will always be an extra copy. - - // at this point the objects are sorted! by measure and within the measure, by fraction. - Out->Process(); - return Out; -} diff --git a/src/NoteLoaderDC.h b/src/NoteLoaderDC.h deleted file mode 100644 index 28676a36..00000000 --- a/src/NoteLoaderDC.h +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -namespace NoteLoaderDC -{ - // user responsability to clean this one up. - Game::dotcur::Song *LoadObjectsFromFile(std::filesystem::path filename, std::filesystem::path prefix = ""); -}; diff --git a/src/NoteLoaderFTB.cpp b/src/NoteLoaderFTB.cpp index 4643b1cf..4390c4be 100644 --- a/src/NoteLoaderFTB.cpp +++ b/src/NoteLoaderFTB.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Song7K.h" #include "NoteLoader7K.h" @@ -90,19 +90,14 @@ void NoteLoaderFTB::LoadObjectsFromFile(std::filesystem::path filename, Song *Ou { Note.StartTime = latof(NoteInfo.at(0).c_str()) / 1000.0; Note.EndTime = latof(NoteInfo.at(1).c_str()) / 1000.0; - Diff->TotalHolds++; - Diff->TotalScoringObjects += 2; } else { Note.StartTime = latof(NoteInfo.at(0).c_str()) / 1000.0; - Diff->TotalNotes++; - Diff->TotalScoringObjects++; } /* index 1 is unused */ int Track = atoi(LineContents[2].c_str()); // Always > 1 - Diff->TotalObjects++; Diff->Duration = std::max(std::max(Note.StartTime, Note.EndTime), Diff->Duration); Msr.Notes[Track - 1].push_back(Note); @@ -122,6 +117,6 @@ void NoteLoaderFTB::LoadObjectsFromFile(std::filesystem::path filename, Song *Ou else goto failed; - Diff->Level = Diff->TotalScoringObjects / Diff->Duration; + Diff->Level = Diff->Data->GetScoreItemsCount() / Diff->Duration; Out->Difficulties.push_back(Diff); } diff --git a/src/NoteLoaderOJN.cpp b/src/NoteLoaderOJN.cpp index 81d9302b..41cd06d6 100644 --- a/src/NoteLoaderOJN.cpp +++ b/src/NoteLoaderOJN.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Logging.h" #include "Song7K.h" #include "NoteLoader7K.h" @@ -43,14 +43,14 @@ struct OjnHeader int32_t cover_offset; }; -struct OjnPackage +struct OjnPackageHeader { int measure; short channel; short events; }; -union OjnEvent +union OjnPackage { struct { @@ -61,7 +61,7 @@ union OjnEvent float floatValue; }; -struct OjnInternalEvent +struct OjnExpandedPackage { int Channel; int noteKind; // undefined if channel is not note or autoplay channel @@ -71,13 +71,17 @@ struct OjnInternalEvent float fValue; int iValue; }; + + bool operator <(const OjnExpandedPackage& other) { + return Fraction < other.Fraction; + } }; struct OjnMeasure { float Len; - std::vector Events; + std::vector Events; OjnMeasure() { @@ -85,194 +89,255 @@ struct OjnMeasure } }; -class OjnLoadInfo -{ -public: - std::vector Measures; - Game::VSRG::Song* S; - float BPM; -}; - -static double BeatForMeasure(OjnLoadInfo *Info, int Measure) -{ - double Out = 0; - - for (int i = 0; i < Measure; i++) - { - Out += Info->Measures[i].Len; - } - - return Out; -} - -// Based off the O2JAM method at -// https://github.com/open2jamorg/open2jam/blob/master/parsers/src/org/open2jam/parsers/EventList.java -void FixOJNEvents(OjnLoadInfo *Info) +class OjnLoadDifficultyContext { - auto CurrentMeasure = 0; - OjnInternalEvent prevIter[7] = { -1, -1, -1, -1 }; - - for (auto Measure : Info->Measures) - { - // Sort events. This is very important, since we assume events are sorted! - std::sort(Measure.Events.begin(), Measure.Events.end(), - [&](const OjnInternalEvent A, const OjnInternalEvent B) -> bool - { return A.Fraction < B.Fraction; }); - - for (auto Evt = Measure.Events.begin(); Evt != Measure.Events.end(); ++Evt) - { - if (Evt->Channel < AUTOPLAY_CHANNEL) - { - if (prevIter[Evt->Channel].Channel != -1) // There is a previous event - { - if (prevIter[Evt->Channel].noteKind == 2 && - (Evt->noteKind == 0 || Evt->noteKind == 2)) // This note or hold head is in between holds - { - Evt->Channel = AUTOPLAY_CHANNEL; - Evt->noteKind = 0; - continue; - } - - if (prevIter[Evt->Channel].noteKind != 2 && // Hold tail without ongoing hold - Evt->noteKind == 3) - { - Evt->Channel = AUTOPLAY_CHANNEL; - Evt->noteKind = 0; - continue; - } - } - - prevIter[Evt->Channel] = *Evt; - } - } - - CurrentMeasure++; - } -} - -void ProcessOJNEvents(OjnLoadInfo *Info, Game::VSRG::Difficulty* Out) -{ - ptrdiff_t CurrentMeasure = 0; - - // First, we sort and clear up invalid events. - FixOJNEvents(Info); +private: + double BeatForMeasure(int Measure) + { + double Out = 0; - // Then we need to have just as many measures going out as we've got in here. - Out->Data->Measures.reserve(Info->Measures.size()); + for (int i = 0; i < Measure; i++) + { + Out += Measures[i].Len; + } - // First of all, we need to process BPM changes and fractional measures. - for (auto Measure : Info->Measures) - { - float MeasureBaseBeat = BeatForMeasure(Info, CurrentMeasure); + return Out; + } - Out->Data->Measures.push_back(Game::VSRG::Measure()); + std::vector Measures; +public: + Game::VSRG::Song* S; + float BPM; - // All fractional measure events were already handled at read time. - Out->Data->Measures[CurrentMeasure].Length = Measure.Len; + void ReadPackages(OjnHeader &Head, int difficulty_index, std::fstream &ojnfile) + { + // Reserve measures. + Measures.resize(Head.measure_count[difficulty_index] + 1); + + for (size_t package = 0U; package < Head.package_count[difficulty_index]; ++package) + { + OjnPackageHeader PackageHeader; + ojnfile.read(reinterpret_cast(&PackageHeader), sizeof(OjnPackageHeader)); + + for (size_t event_index = 0U; event_index < PackageHeader.events; ++event_index) + { + auto Fraction = float(event_index) / float(PackageHeader.events); + OjnPackage package; + OjnExpandedPackage o2evt; + + ojnfile.read(reinterpret_cast(&package), sizeof(OjnPackage)); + + switch (PackageHeader.channel) + { + case 0: // Fractional measure + Measures[PackageHeader.measure].Len = 4 * package.floatValue; + break; + case 1: // BPM change + o2evt.fValue = package.floatValue; + o2evt.Channel = BPM_CHANNEL; + o2evt.Fraction = Fraction; + Measures[PackageHeader.measure].Events.push_back(o2evt); + break; + case 2: // note events (enginechannel = PackageHeader.channel - 2) + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + if (package.noteValue == 0) continue; + o2evt.Fraction = Fraction; + o2evt.Channel = PackageHeader.channel - 2; + o2evt.iValue = package.noteValue; + o2evt.noteKind = package.type; + Measures[PackageHeader.measure].Events.push_back(o2evt); + break; + default: // autoplay notes + if (package.noteValue == 0) continue; + o2evt.Channel = AUTOPLAY_CHANNEL; + o2evt.Fraction = Fraction; + o2evt.iValue = package.noteValue; + o2evt.noteKind = package.type; + Measures[PackageHeader.measure].Events.push_back(o2evt); + break; + } + } + } + } - for (auto Evt : Measure.Events) - { - if (Evt.Channel != BPM_CHANNEL) continue; // These are the only ones we directly handle. - /* We calculate beat multiplying fraction by 4 instead of the measure's length, - because o2jam's measure fractions don't compress the notes inside. */ - auto Beat = MeasureBaseBeat + Evt.Fraction * 4; - TimingSegment Segment(Beat, Evt.fValue); + void FixOJNEvents() + { + auto CurrentMeasure = 0; + OjnExpandedPackage prevIter[7] = { -1, -1, -1, -1 }; + + for (auto &Measure : Measures) + { + // Sort events. This is very important, since we assume events are sorted! + std::sort(Measure.Events.begin(), Measure.Events.end()); + + for (auto Evt = Measure.Events.begin(); Evt != Measure.Events.end(); ++Evt) + { + if (Evt->Channel < AUTOPLAY_CHANNEL) + { + if (prevIter[Evt->Channel].Channel != -1) // There is a previous event + { + if (prevIter[Evt->Channel].noteKind == 2 && + (Evt->noteKind == 0 || Evt->noteKind == 2)) // This note or hold head is in between holds + { + Evt->Channel = AUTOPLAY_CHANNEL; + Evt->noteKind = 0; + continue; + } + + if (prevIter[Evt->Channel].noteKind != 2 && // Hold tail without ongoing hold + Evt->noteKind == 3) + { + Evt->Channel = AUTOPLAY_CHANNEL; + Evt->noteKind = 0; + continue; + } + } + + prevIter[Evt->Channel] = *Evt; + } + } + + CurrentMeasure++; + } + } - // 0 values must be ignored. - if (Evt.fValue == 0) continue; + void CopyOJNTimingData(Game::VSRG::Difficulty *Out) + { + auto CurrentMeasure = 0; + for (auto Measure : Measures) + { + float MeasureBaseBeat = BeatForMeasure(CurrentMeasure); + + Out->Data->Measures.push_back(Game::VSRG::Measure()); + + // All fractional measure events were already handled at read time. + Out->Data->Measures[CurrentMeasure].Length = Measure.Len; + + for (auto Evt : Measure.Events) + { + if (Evt.Channel != BPM_CHANNEL) continue; // These are the only ones we directly handle. + + /* We calculate beat multiplying fraction by 4 instead of the measure's length, + because o2jam's measure fractions don't compress the notes inside. */ + auto Beat = MeasureBaseBeat + Evt.Fraction * 4; + TimingSegment Segment(Beat, Evt.fValue); + + // 0 values must be ignored. + if (Evt.fValue == 0) continue; + + if (Out->Timing.size()) + { + // For some reason, a few BPMs are redundant. Since our events are sorted, there's no need for worry.. + if (Out->Timing.back().Value == Evt.fValue) // ... We already have this BPM. + continue; + } + + Out->Timing.push_back(Segment); + } + + CurrentMeasure++; + } + + // The BPM info on the header is not for decoration. It's the very first BPM we should be using. + // A few of the charts already have set BPMs at beat 0, so we only need to add information if it's missing. + if (Out->Timing.size() == 0 || Out->Timing[0].Time > 0) + { + TimingSegment Seg(0, BPM); + Out->Timing.push_back(Seg); + + // Since events and measures are ordered already, there's no need to sort + // timing data unless we insert new information. + sort(Out->Timing.begin(), Out->Timing.end(), TimeSegmentCompare); + } + } - if (Out->Timing.size()) - { - // For some reason, a few BPMs are redundant. Since our events are sorted, there's no need for worry.. - if (Out->Timing.back().Value == Evt.fValue) // ... We already have this BPM. - continue; - } + // Based off the O2JAM method at + // https://github.com/open2jamorg/open2jam/blob/master/parsers/src/org/open2jam/parsers/EventList.java + void OutputAllOJNEventsToDifficulty(Game::VSRG::Difficulty *Out) + { + // First, we sort and clear up invalid events. + FixOJNEvents(); - Out->Timing.push_back(Segment); - } + // Then we need to have just as many measures going out as we've got in here. + Out->Data->Measures.reserve(Measures.size()); - CurrentMeasure++; - } + // Now to output, we need to process BPM changes and fractional measures. + CopyOJNTimingData(Out); - // The BPM info on the header is not for decoration. It's the very first BPM we should be using. - // A few of the charts already have set BPMs at beat 0, so we only need to add information if it's missing. - if (Out->Timing.size() == 0 || Out->Timing[0].Time > 0) - { - TimingSegment Seg(0, Info->BPM); - Out->Timing.push_back(Seg); + // Now, we can process notes and long notes. + CopyOJNNoteData(Out); + } - // Since events and measures are ordered already, there's no need to sort - // timing data unless we insert new information. - sort(Out->Timing.begin(), Out->Timing.end(), TimeSegmentCompare); - } + void CopyOJNNoteData(Game::VSRG::Difficulty * Out) + { + auto CurrentMeasure = 0; + float PendingLNs[7] = { 0 }; + float PendingLNSound[7] = { 0 }; + + for (auto Measure : Measures) + { + auto MeasureBaseBeat = BeatForMeasure(CurrentMeasure); + + for (auto Evt : Measure.Events) + { + if (Evt.Channel == BPM_CHANNEL) continue; + else + { + auto Beat = MeasureBaseBeat + Evt.Fraction * 4; + auto Time = TimeAtBeat(Out->Timing, 0, Beat); + + if (Evt.noteKind % 8 > 3) // Okay... This is obscure. Big thanks to open2jam. + Evt.iValue += 1000; + + if (Evt.Channel == AUTOPLAY_CHANNEL) // Ah, autoplay audio. + { + AutoplaySound Snd; + + Snd.Sound = Evt.iValue; + Snd.Time = Time; + Out->Data->BGMEvents.push_back(Snd); + } + else // A note! In this case, we already 'normalized' O2Jam channels into raindrop channels. + { + Game::VSRG::NoteData Note; + + if (Evt.Channel >= 7) continue; // Who knows... A buffer overflow may be possible. + + Note.StartTime = Time; + Note.Sound = Evt.iValue; + + switch (Evt.noteKind) + { + case 0: + Out->Data->Measures[CurrentMeasure].Notes[Evt.Channel].push_back(Note); + break; + case 2: + PendingLNs[Evt.Channel] = Time; + PendingLNSound[Evt.Channel] = Evt.iValue; + break; + case 3: + Note.StartTime = PendingLNs[Evt.Channel]; + Note.EndTime = Time; + Note.Sound = PendingLNSound[Evt.Channel]; + Out->Data->Measures[CurrentMeasure].Notes[Evt.Channel].push_back(Note); + break; + } + } + } + } + CurrentMeasure++; + } + } +}; - // Now, we can process notes and long notes. - CurrentMeasure = 0; - float PendingLNs[7] = { 0 }; - float PendingLNSound[7] = { 0 }; - for (auto Measure : Info->Measures) - { - float MeasureBaseBeat = BeatForMeasure(Info, CurrentMeasure); - for (auto Evt : Measure.Events) - { - if (Evt.Channel == BPM_CHANNEL) continue; - else - { - auto Beat = MeasureBaseBeat + Evt.Fraction * 4; - float Time = TimeAtBeat(Out->Timing, 0, Beat); - - if (Evt.noteKind % 8 > 3) // Okay... This is obscure. Big thanks to open2jam. - Evt.iValue += 1000; - - if (Evt.Channel == AUTOPLAY_CHANNEL) // Ah, autoplay audio. - { - AutoplaySound Snd; - - Snd.Sound = Evt.iValue; - Snd.Time = Time; - Out->Data->BGMEvents.push_back(Snd); - } - else // A note! In this case, we already 'normalized' O2Jam channels into raindrop channels. - { - Game::VSRG::NoteData Note; - - if (Evt.Channel >= 7) continue; // Who knows... A buffer overflow may be possible. - - Note.StartTime = Time; - Note.Sound = Evt.iValue; - - switch (Evt.noteKind) - { - case 0: - Out->TotalNotes++; - Out->TotalObjects++; - Out->TotalScoringObjects++; - Out->Data->Measures[CurrentMeasure].Notes[Evt.Channel].push_back(Note); - break; - case 2: - Out->TotalScoringObjects++; - PendingLNs[Evt.Channel] = Time; - PendingLNSound[Evt.Channel] = Evt.iValue; - break; - case 3: - Out->TotalObjects++; - Out->TotalHolds++; - Out->TotalScoringObjects++; - Note.StartTime = PendingLNs[Evt.Channel]; - Note.EndTime = Time; - Note.Sound = PendingLNSound[Evt.Channel]; - Out->Data->Measures[CurrentMeasure].Notes[Evt.Channel].push_back(Note); - break; - } - } - } - } - CurrentMeasure++; - } -} bool IsValidOJN(std::fstream &filein, OjnHeader *Head) { @@ -318,6 +383,7 @@ const char *LoadOJNCover(std::filesystem::path filename, size_t &read) return out; } + void NoteLoaderOJN::LoadObjectsFromFile(std::filesystem::path filename, Game::VSRG::Song *Out) { CreateBinIfstream(filein, filename); @@ -355,7 +421,7 @@ void NoteLoaderOJN::LoadObjectsFromFile(std::filesystem::path filename, Game::VS for (auto i = 0; i < 3; i++) { - OjnLoadInfo Info; + OjnLoadDifficultyContext Info; std::shared_ptr Diff(new Game::VSRG::Difficulty()); std::shared_ptr TInfo(new Game::VSRG::O2JamChartInfo); std::shared_ptr LInfo(new Game::VSRG::DifficultyLoadInfo); @@ -391,67 +457,15 @@ void NoteLoaderOJN::LoadObjectsFromFile(std::filesystem::path filename, Game::VS Diff->IsVirtual = true; Info.BPM = Head.bpm; - Info.Measures.reserve(Head.measure_count[i]); - for (auto k = 0U; k <= Head.measure_count[i] + 1; ++k) - Info.Measures.emplace_back(OjnMeasure{}); - /* The implications of this structure are interesting. Measures may be unordered; but events may not, if only there's one package per channel per measure. */ - for (auto package = 0U; package < Head.package_count[i]; ++package) - { - OjnPackage PackageHeader; - filein.read(reinterpret_cast(&PackageHeader), sizeof(OjnPackage)); - - for (auto cevt = 0U; cevt < PackageHeader.events; ++cevt) - { - auto Fraction = float(cevt) / float(PackageHeader.events); - OjnEvent Event; - OjnInternalEvent IEvt; - - filein.read(reinterpret_cast(&Event), sizeof(OjnEvent)); - - switch (PackageHeader.channel) - { - case 0: // Fractional measure - Info.Measures[PackageHeader.measure].Len = 4 * Event.floatValue; - break; - case 1: // BPM change - IEvt.fValue = Event.floatValue; - IEvt.Channel = BPM_CHANNEL; - IEvt.Fraction = Fraction; - Info.Measures[PackageHeader.measure].Events.push_back(IEvt); - break; - case 2: // note events (enginechannel = PackageHeader.channel - 2) - case 3: - case 4: - case 5: - case 6: - case 7: - case 8: - if (Event.noteValue == 0) goto next_event; - IEvt.Fraction = Fraction; - IEvt.Channel = PackageHeader.channel - 2; - IEvt.iValue = Event.noteValue; - IEvt.noteKind = Event.type; - Info.Measures[PackageHeader.measure].Events.push_back(IEvt); - break; - default: // autoplay notes - if (Event.noteValue == 0) goto next_event; - IEvt.Channel = AUTOPLAY_CHANNEL; - IEvt.Fraction = Fraction; - IEvt.iValue = Event.noteValue; - IEvt.noteKind = Event.type; - Info.Measures[PackageHeader.measure].Events.push_back(IEvt); - break; - } -next_event:; - } - } + Info.ReadPackages(Head, i, filein); // Process Info... then push back difficulty. - ProcessOJNEvents(&Info, Diff.get()); + Info.OutputAllOJNEventsToDifficulty(Diff.get()); Out->Difficulties.push_back(Diff); } } + diff --git a/src/NoteLoaderOM.cpp b/src/NoteLoaderOM.cpp index b6987827..b867d594 100644 --- a/src/NoteLoaderOM.cpp +++ b/src/NoteLoaderOM.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Logging.h" #include "Song7K.h" #include "NoteLoader7K.h" @@ -184,7 +184,11 @@ class OsumaniaLoader { Offsetize(); - auto seclst = filter([](const HitsoundSectionData& H) {return !H.IsInherited && !H.Omit;}, HitsoundSections); + auto seclst = filter([](const HitsoundSectionData& H) + { + return !H.IsInherited && !H.Omit; + }, HitsoundSections); + for (auto i = seclst.begin(); i != seclst.end();) { double SectionDurationInBeats = 0; @@ -738,21 +742,11 @@ class OsumaniaLoader Log::Printf("NoteLoaderOM: object at track %d has startTime > endTime (%f and %f)\n", Track, startTime, endTime); Note.EndTime = 0; - - Diff->TotalScoringObjects += 1; - Diff->TotalNotes++; - } - else - { - Diff->TotalScoringObjects += 2; - Diff->TotalHolds++; } } else if (NoteType & NOTE_NORMAL) { Note.StartTime = startTime; - Diff->TotalNotes++; - Diff->TotalScoringObjects++; } else if (NoteType & NOTE_SLIDER) { @@ -772,9 +766,6 @@ class OsumaniaLoader Note.StartTime = startTime; Note.EndTime = len_seconds + startTime; - - Diff->TotalScoringObjects += 2; - Diff->TotalHolds++; } Hitsound = atoi(ObjectData[4].c_str()); @@ -792,7 +783,6 @@ class OsumaniaLoader Note.Sound = Sounds[Sample]; } - Diff->TotalObjects++; Notes[Track].push_back(Note); Diff->Duration = std::max(std::max(Note.StartTime, Note.EndTime) + 1, Diff->Duration); @@ -895,8 +885,13 @@ class OsumaniaLoader default: break; } } + + auto notecount = 0; + for (int i = 0; i < Diff->Channels; i++) { + notecount += Notes[i].size(); + } - if (Diff->TotalObjects) + if (notecount) { // Okay then, convert timing data into a measure-based format raindrop can use // and calculate offset. @@ -913,7 +908,7 @@ class OsumaniaLoader Diff->Data->SoundList[i.second] = i.first; // Calculate level as NPS - Diff->Level = Diff->TotalScoringObjects / Diff->Duration; + Diff->Level = Diff->Data->GetScoreItemsCount() / Diff->Duration; osu_sng->Difficulties.push_back(Diff); } } @@ -931,5 +926,4 @@ void NoteLoaderOM::LoadObjectsFromFile(std::filesystem::path filename, Song *Out OsumaniaLoader Info(Out); Info.LoadFromFile(filename); - } diff --git a/src/NoteLoaderSM.cpp b/src/NoteLoaderSM.cpp index 0848e6dd..a5101e39 100644 --- a/src/NoteLoaderSM.cpp +++ b/src/NoteLoaderSM.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Song7K.h" #include "NoteLoader7K.h" #include "Logging.h" @@ -112,15 +112,15 @@ void LoadNotesSM(Song *Out, Difficulty *Diff, std::vector &MeasureT /* For each measure of the song */ for (size_t i = 0; i < MeasureText.size(); i++) /* i = current measure */ { - ptrdiff_t MeasureFractions = MeasureText[i].length() / Keys; + ptrdiff_t MeasureSubdivisions = MeasureText[i].length() / Keys; Measure Msr; if (MeasureText[i].length()) { /* For each fraction of the measure*/ - for (ptrdiff_t m = 0; m < MeasureFractions; m++) /* m = current fraction */ + for (ptrdiff_t m = 0; m < MeasureSubdivisions; m++) /* m = current fraction */ { - double Beat = i * 4.0 + m * 4.0 / (double)MeasureFractions; /* Current beat */ + double Beat = i * 4.0 + m * 4.0 / (double)MeasureSubdivisions; /* Current beat */ double StopsTime = StopTimeAtBeat(Diff->Data->Stops, Beat); double Time = TimeAtBeat(Diff->Timing, Diff->Offset, Beat, true) + StopsTime; bool InWarpSection = IsTimeWithinWarp(Diff, Time); @@ -137,14 +137,6 @@ void LoadNotesSM(Song *Out, Difficulty *Diff, std::vector &MeasureT { case '1': /* Taps */ Note.StartTime = Time; - - if (!InWarpSection) - { - Diff->TotalNotes++; - Diff->TotalObjects++; - Diff->TotalScoringObjects++; - } - Msr.Notes[k].push_back(Note); break; case '2': /* Holds */ @@ -152,20 +144,13 @@ void LoadNotesSM(Song *Out, Difficulty *Diff, std::vector &MeasureT KeyStartTime[k] = Time; KeyBeat[k] /*heh*/ = Beat; - if (!InWarpSection) - Diff->TotalScoringObjects++; break; case '3': /* Hold releases */ Note.StartTime = KeyStartTime[k]; Note.EndTime = Time; if (!IsTimeWithinWarp(Diff, KeyStartTime[k])) - { Note.NoteKind = NK_NORMAL; // Un-fake it. - Diff->TotalHolds++; - Diff->TotalObjects++; - Diff->TotalScoringObjects++; - } Msr.Notes[k].push_back(Note); break; case 'F': @@ -351,7 +336,7 @@ TimingData CalculateRaindropWarpData(Difficulty* Diff, const TimingData& Warps) } // DRY etc I'll see it later. -TimingData CalculateRaindropSCData(Difficulty* Diff, const TimingData& SCd) +TimingData CalculateRaindropScrollData(Difficulty* Diff, const TimingData& SCd) { TimingData Ret; for (auto SC : SCd) @@ -368,9 +353,9 @@ TimingData CalculateRaindropSCData(Difficulty* Diff, const TimingData& SCd) } // Transform the time data from beats to seconds -VectorSpeeds CalculateRaindropScrolls(Difficulty *Diff, const SpeedData& In) +VectorInterpolatedSpeedMultipliers CalculateRaindropSpeedData(Difficulty *Diff, const SpeedData& In) { - VectorSpeeds Ret; + VectorInterpolatedSpeedMultipliers Ret; for (auto scroll : In) { @@ -566,9 +551,9 @@ void NoteLoaderSSC::LoadObjectsFromFile(std::filesystem::path filename, Song *Ou Diff->Data->Scrolls = ScrollData; if (!diffSpeedData.size()) - Diff->Data->Speeds = CalculateRaindropScrolls(Diff.get(), speedData); + Diff->Data->InterpoloatedSpeedMultipliers = CalculateRaindropSpeedData(Diff.get(), speedData); else - Diff->Data->Speeds = CalculateRaindropScrolls(Diff.get(), diffSpeedData); + Diff->Data->InterpoloatedSpeedMultipliers = CalculateRaindropSpeedData(Diff.get(), diffSpeedData); Diff->Offset = -Offset; Diff->Duration = 0; @@ -578,7 +563,7 @@ void NoteLoaderSSC::LoadObjectsFromFile(std::filesystem::path filename, Song *Ou Diff->Data->StageFile = Banner; Diff->Data->Warps = CalculateRaindropWarpData(Diff.get(), Diff->Data->Warps); - Diff->Data->Scrolls = CalculateRaindropSCData(Diff.get(), Diff->Data->Scrolls); + Diff->Data->Scrolls = CalculateRaindropScrollData(Diff.get(), Diff->Data->Scrolls); CommandContents = RemoveComments(CommandContents); auto Measures = Utility::TokenSplit(CommandContents); @@ -628,29 +613,52 @@ void WarpifyTiming(Difficulty* Diff) // for all negative sections between i and the next positive section // add up their duration in seconds - double warpDuration = 0; - double warpDurationBeats = 0; + double totalWarpDuration = 0; while (currentSection->Value < 0) { if (currentSection != Diff->Timing.end()) { - // add the duration of section k, if there's one to determine it. + // add the duration of section, if there's one to determine it. auto nextSection = currentSection + 1; if (nextSection != Diff->Timing.end()) { - warpDuration += spb(abs(currentSection->Value)) * (nextSection->Time - currentSection->Time); - warpDurationBeats += nextSection->Time - currentSection->Time; + auto sectionDurationBeats = (nextSection->Time - currentSection->Time); + auto sectionSecondsPerBeat = spb(abs(currentSection->Value)); + totalWarpDuration += sectionSecondsPerBeat * sectionDurationBeats; } + + /* + todo: + dtinth's interpretation for sm5 warps + infinite bpm, implies we have to split the warp when + there's a stop. + */ + + // negative bpm means backward scrolling to raindrop + // therefore, to scroll forwards and warp over this section properly + // we make it positive currentSection->Value = -currentSection->Value; } else break; ++currentSection; } - // Now since W = DurationInBeats(Dn) + TimeToBeatsAtBPM(DurationInTime(Dn), NextPositiveBPM) - // DurationInBeats is warpDurationBeats, DurationInTime is warpDuration. k->Value is NextPositiveBPM - double warpTime = TimeAtBeat(Diff->Timing, Diff->Offset, i->Time, true) + StopTimeAtBeat(Diff->Data->Stops, i->Time); - - Diff->Data->Warps.push_back(TimingSegment(warpTime, warpDuration * 2)); + + // diff->timing is in "no-warp" time + // which means it's in "the time if warps were not considered" + // or "chart time" used for display + // the audio time, or time used for judgement + // is usually behind the chart time. + // notedata is in chart time. + // therefore, tracknote time data is in audio time + // and vertical position is in chart time. + // chart time is calculated by adding up all the + // time scrolled by bpm ignoring everything else + // plus time scrolled by stops + // plus time scrolled by warps. + double warpTime = TimeAtBeat(Diff->Timing, Diff->Offset, i->Time, true) + + StopTimeAtBeat(Diff->Data->Stops, i->Time); + + Diff->Data->Warps.push_back(TimingSegment(warpTime, totalWarpDuration * 2)); // And now that we're done, there's no need to check the negative BPMs inbetween this one and the next positive BPM, so... i = currentSection; if (i == Diff->Timing.end()) break; diff --git a/src/NoteTransformations.cpp b/src/NoteTransformations.cpp index d6b70eec..609c59bf 100644 --- a/src/NoteTransformations.cpp +++ b/src/NoteTransformations.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Song7K.h" namespace Game { diff --git a/src/PlayerChartData.cpp b/src/PlayerChartData.cpp index 3add9f2d..5d2d86bf 100644 --- a/src/PlayerChartData.cpp +++ b/src/PlayerChartData.cpp @@ -7,7 +7,7 @@ CfgVar DebugMeasurePosGen("MeasurePosGen", "Debug"); namespace Game { namespace VSRG { - GameChartData::GameChartData() + PlayerChartState::PlayerChartState() { Drift = 0; WaitTime = DEFAULT_WAIT_TIME; @@ -164,14 +164,14 @@ namespace Game { return BPS; } - TimingData GetVSpeeds(TimingData& BPS, double Speed) + TimingData GetVSpeeds(TimingData& BPS, double ConstantUserSpeed) { TimingData VerticalSpeeds; // We're using a CMod, so further processing is pointless - if (Speed) + if (ConstantUserSpeed) { - VerticalSpeeds.push_back(TimingSegment(0, Speed)); + VerticalSpeeds.push_back(TimingSegment(0, ConstantUserSpeed)); return VerticalSpeeds; } // End CMod @@ -183,7 +183,7 @@ namespace Game { if (Section.Value) { auto spb = 1 / Section.Value; - VerticalSpeed = MeasureBaseSpacing / (spb * 4); + VerticalSpeed = UNITS_PER_MEASURE / (spb * 4); } else VerticalSpeed = 0; @@ -207,7 +207,7 @@ namespace Game { return VerticalSpeeds; } - TimingData GetSpeedChanges(TimingData VerticalSpeeds, TimingData Scrolls, double Drift, double Offset, bool Reset) + TimingData ApplySpeedChanges(TimingData VerticalSpeeds, TimingData Scrolls, double Drift, double Offset, bool Reset) { std::sort(Scrolls.begin(), Scrolls.end()); @@ -297,7 +297,7 @@ namespace Game { return VerticalSpeeds; } - double GameChartData::GetWarpAmount(double Time) const + double PlayerChartState::GetWarpAmount(double Time) const { double wAmt = 0; for (auto warp : Warps) @@ -309,7 +309,7 @@ namespace Game { return wAmt; } - bool GameChartData::IsWarpingAt(double start_time) const + bool PlayerChartState::IsWarpingAt(double start_time) const { auto it = std::lower_bound(Warps.begin(), Warps.end(), start_time, TimeSegmentCompare); if (it != Warps.end()) @@ -319,31 +319,34 @@ namespace Game { } - GameChartData GameChartData::FromDifficulty(Difficulty *diff, double Drift, double Speed) + PlayerChartState PlayerChartState::FromDifficulty(Difficulty *diff, double UserOffset, double ConstantUserSpeed) { - GameChartData out; + PlayerChartState out; auto data = diff->Data; if (data == nullptr) throw std::runtime_error("Tried to pass a metadata-only difficulty to Player Chart data generator.\n"); out.ConnectedDifficulty = diff; - out.Drift = Drift; - out.BPS = GetBPSData(diff, Drift); - out.VSpeeds = GetVSpeeds(out.BPS, Speed); - out.VSpeeds = GetSpeedChanges(out.VSpeeds, data->Scrolls, - Drift, diff->Offset, - diff->BPMType == VSRG::Difficulty::BT_BEATSPACE); + out.Drift = UserOffset; + out.BPS = GetBPSData(diff, UserOffset); + out.ScrollSpeeds = GetVSpeeds(out.BPS, ConstantUserSpeed); out.HasTurntable = diff->Data->Turntable; - out.MeasureBarlines = out.GetMeasureLines(); - if (!Speed) { + if (!ConstantUserSpeed) { + out.ScrollSpeeds = ApplySpeedChanges(out.ScrollSpeeds, data->Scrolls, + UserOffset, diff->Offset, + diff->BPMType == VSRG::Difficulty::BT_BEATSPACE); + out.Warps = data->Warps; - out.Speeds = data->Speeds; + out.InterpoloatedSpeedMultipliers = data->InterpoloatedSpeedMultipliers; } + // this has to go _after_ speed changes were applied + out.MeasureBarlines = out.GetMeasureLines(); + /* For all channels of this difficulty */ for (int KeyIndex = 0; KeyIndex < diff->Channels; KeyIndex++) { @@ -365,10 +368,10 @@ namespace Game { TrackNote NewNote; NewNote.AssignNotedata(CurrentNote); - NewNote.AddTime(Drift); + NewNote.AddTime(UserOffset); - auto VerticalPosition = IntegrateToTime(out.VSpeeds, NewNote.GetStartTime()); - auto HoldEndPosition = IntegrateToTime(out.VSpeeds, NewNote.GetEndTime()); + auto VerticalPosition = IntegrateToTime(out.ScrollSpeeds, NewNote.GetStartTime()); + auto HoldEndPosition = IntegrateToTime(out.ScrollSpeeds, NewNote.GetEndTime()); // if upscroll change minus for plus as well as matrix at screengameplay7k if (!CurrentNote.EndTime) @@ -391,7 +394,7 @@ namespace Game { // !Speed: non-constant // Judgable & ! warping: Constant speed, so only add non-warped notes. - if (!Speed || (NewNote.IsJudgable() && !out.IsWarpingAt(CurrentNote.StartTime))) + if (!ConstantUserSpeed || (NewNote.IsJudgable() && !out.IsWarpingAt(CurrentNote.StartTime))) out.NotesByChannel[KeyIndex].push_back(NewNote); } @@ -407,23 +410,21 @@ namespace Game { }); } - if (Speed) // Only apply speeds on non-constant velocities - out.Speeds = diff->Data->Speeds; - for (auto&& w : out.Warps) - w.Time += Drift; - for (auto&& s : out.Speeds) - s.Time += Drift; + w.Time += UserOffset; + for (auto&& s : out.InterpoloatedSpeedMultipliers) + s.Time += UserOffset; // Toggle whether we can use our guarantees for optimizations or not at rendering/judge time. out.HasNegativeScroll = false; - for (auto S : out.Speeds) if (S.Value < 0) out.HasNegativeScroll = true; - for (auto S : out.VSpeeds) if (S.Value < 0) out.HasNegativeScroll = true; + for (auto S : out.InterpoloatedSpeedMultipliers) if (S.Value < 0) out.HasNegativeScroll = true; + for (auto S : out.ScrollSpeeds) if (S.Value < 0) out.HasNegativeScroll = true; return out; } - double GameChartData::GetWarpedSongTime(double SongTime) const + // audio time -> chart time + double PlayerChartState::GetWarpedSongTime(double SongTime) const { auto T = SongTime; for (auto k = Warps.cbegin(); k != Warps.cend(); ++k) @@ -448,7 +449,7 @@ namespace Game { } - std::vector GameChartData::GetMeasureLines() const + std::vector PlayerChartState::GetMeasureLines() const { auto &diff = ConnectedDifficulty; auto Data = diff->Data; @@ -483,7 +484,7 @@ namespace Game { for (auto i = 0; i < TotMeasures; i++) { auto T = Drift + diff->Offset - MeasureTime * i; - auto PositionOut = IntegrateToTime(VSpeeds, T); + auto PositionOut = IntegrateToTime(ScrollSpeeds, T); Out.push_back(PositionOut); if (DebugMeasurePosGen) Log::LogPrintf("Add measure line at time %f (Vertical %f)\n", T, PositionOut); @@ -496,14 +497,14 @@ namespace Game { if (BPMType == Difficulty::BT_BEAT) // VerticalSpeeds already has drift applied, so we don't need to apply it again here. { - PositionOut = IntegrateToTime(VSpeeds, Drift + GetTimeAtBeat(Last)); + PositionOut = IntegrateToTime(ScrollSpeeds, Drift + GetTimeAtBeat(Last)); } else if (BPMType == Difficulty::BT_BEATSPACE) { auto TargetTime = IntegrateToTime(SPB, Last) + diff->Offset; //TargetTime = round(TargetTime * 1000.0) / 1000.0; // Round to MS - PositionOut = IntegrateToTime(VSpeeds, TargetTime); + PositionOut = IntegrateToTime(ScrollSpeeds, TargetTime); if (DebugMeasurePosGen) { Log::LogPrintf("Add measure line at time %f (Vertical: %f)\n", TargetTime, PositionOut); } @@ -516,36 +517,36 @@ namespace Game { return Out; } - double GameChartData::GetBpmAt(double Time) const + double PlayerChartState::GetBpmAt(double Time) const { return SectionValue(BPS, Time) * 60; } - double GameChartData::GetBpsAt(double Time) const + double PlayerChartState::GetBpsAt(double Time) const { return SectionValue(BPS, Time); } - double GameChartData::GetBeatAt(double Time) const + double PlayerChartState::GetBeatAt(double Time) const { return IntegrateToTime(BPS, Time); } - double GameChartData::GetSpeedMultiplierAt(double Time) const + double PlayerChartState::GetSpeedMultiplierAt(double Time) const { // Calculate current speed value to apply. auto CurrentTime = GetWarpedSongTime(Time); // speedIter: first which time is greater than current - auto speedIter = lower_bound(Speeds.begin(), Speeds.end(), CurrentTime); + auto speedIter = lower_bound(InterpoloatedSpeedMultipliers.begin(), InterpoloatedSpeedMultipliers.end(), CurrentTime); double previousValue = 1; double currentValue = 1; double speedTime = CurrentTime; double duration = 1; bool integrateByBeats = false; - if (speedIter != Speeds.begin()) + if (speedIter != InterpoloatedSpeedMultipliers.begin()) speedIter--; // Do we have a speed that has a value to interpolate from? @@ -554,7 +555,7 @@ namespace Game { From the speed at the end of the previous speed, interpolate to speedIter->value in speedIter->duration time across the current time - speedIter->time. */ - if (speedIter != Speeds.begin()) + if (speedIter != InterpoloatedSpeedMultipliers.begin()) { auto prevSpeed = speedIter - 1; previousValue = prevSpeed->Value; @@ -565,7 +566,7 @@ namespace Game { } else // Oh, we don't? { - if (speedIter != Speeds.end()) + if (speedIter != InterpoloatedSpeedMultipliers.end()) currentValue = previousValue = speedIter->Value; } @@ -582,62 +583,92 @@ namespace Game { return lerpedMultiplier; } - double GameChartData::GetTimeAtBeat(double beat, double drift) const + // returns chart time + double PlayerChartState::GetTimeAtBeat(double beat, double drift) const { - return TimeAtBeat(ConnectedDifficulty->Timing, ConnectedDifficulty->Offset + drift, beat) + StopTimeAtBeat(ConnectedDifficulty->Data->Stops, beat); + return TimeAtBeat(ConnectedDifficulty->Timing, ConnectedDifficulty->Offset + drift, beat) + + StopTimeAtBeat(ConnectedDifficulty->Data->Stops, beat); } - double GameChartData::GetOffset() const + double PlayerChartState::GetOffset() const { return ConnectedDifficulty->Offset; } - std::map GameChartData::GetSoundList() const + double PlayerChartState::GetMeasureTime(double msr) const + { + double beat = 0; + + if (msr <= 0) + { + return 0; + } + + auto whole = int(floor(msr)); + auto fraction = msr - double(whole); + for (auto i = 0; i < whole; i++) + beat += ConnectedDifficulty->Data->Measures[i].Length; + beat += ConnectedDifficulty->Data->Measures[whole].Length * fraction; + + // Log::Logf("Warping to measure measure %d at beat %f.\n", whole, beat); + + return GetTimeAtBeat(beat); + } + + bool PlayerChartState::IsNoteTimeSorted() + { + return !Warps.size() // The actual time is scrambled + && !HasNegativeScroll; // Draw order may be incorrect + } + + std::map PlayerChartState::GetSoundList() const { assert(ConnectedDifficulty && ConnectedDifficulty->Data); return ConnectedDifficulty->Data->SoundList; } - ChartType GameChartData::GetChartType() const + ChartType PlayerChartState::GetChartType() const { assert(ConnectedDifficulty && ConnectedDifficulty->Data); assert(ConnectedDifficulty->Data->TimingInfo); return ConnectedDifficulty->Data->TimingInfo->GetType(); } - bool GameChartData::IsBmson() const + bool PlayerChartState::IsBmson() const { return GetChartType() == TI_BMS && dynamic_cast(ConnectedDifficulty->Data->TimingInfo.get())->IsBMSON; } - bool GameChartData::IsVirtual() const + bool PlayerChartState::IsVirtual() const { return ConnectedDifficulty->IsVirtual; } - bool GameChartData::HasTimingData() const + bool PlayerChartState::HasTimingData() const { assert(ConnectedDifficulty != nullptr); return ConnectedDifficulty->Timing.size() > 0; } - SliceContainer GameChartData::GetSliceData() const + SliceContainer PlayerChartState::GetSliceData() const { return ConnectedDifficulty->Data->SliceData; } - double GameChartData::GetDisplacementAt(double Time) const + // chart time -> note displacement + double PlayerChartState::GetChartDisplacementAt(double Time) const { - return IntegrateToTime(VSpeeds, Time); + return IntegrateToTime(ScrollSpeeds, Time); } - double GameChartData::GetDisplacementSpeedAt(double Time) const + // song time -> scroll speed + double PlayerChartState::GetDisplacementSpeedAt(double Time) const { - return SectionValue(VSpeeds, GetWarpedSongTime(Time)); + return SectionValue(ScrollSpeeds, GetWarpedSongTime(Time)); } - void GameChartData::DisableNotesUntil(double Time) + void PlayerChartState::DisableNotesUntil(double Time) { ResetNotes(); for (auto k = 0U; k < MAX_CHANNELS; k++) @@ -646,7 +677,7 @@ namespace Game { m->Disable(); } - void GameChartData::ResetNotes() + void PlayerChartState::ResetNotes() { for (auto k = 0U; k < MAX_CHANNELS; k++) for (auto m = NotesByChannel[k].begin(); m != NotesByChannel[k].end(); ++m) diff --git a/src/PlayerChartData.h b/src/PlayerChartData.h index 281a8dc0..aad3f437 100644 --- a/src/PlayerChartData.h +++ b/src/PlayerChartData.h @@ -1,14 +1,14 @@ #pragma once -#include "GameGlobal.h" + namespace Game { namespace VSRG { - struct GameChartData { - TimingData VSpeeds; + struct PlayerChartState { + TimingData ScrollSpeeds; TimingData BPS; TimingData Warps; - VectorSpeeds Speeds; + VectorInterpolatedSpeedMultipliers InterpoloatedSpeedMultipliers; VectorTN NotesByChannel; std::vector MeasureBarlines; bool HasNegativeScroll; @@ -18,21 +18,24 @@ namespace Game { double WaitTime; Difficulty* ConnectedDifficulty; - GameChartData(); + PlayerChartState(); // Chart data functions double GetWarpedSongTime(double SongTime) const; double GetWarpAmount(double Time) const; bool IsWarpingAt(double start_time) const; - std::vector GameChartData::GetMeasureLines() const; - double GameChartData::GetSpeedMultiplierAt(double Time) const; // Time in unwarped song time + std::vector GetMeasureLines() const; + double GetSpeedMultiplierAt(double Time) const; // Time in unwarped song time double GetBpmAt(double Time) const; double GetBpsAt(double Time) const; double GetBeatAt(double Time) const; - double GetDisplacementAt(double Time) const; + double GetChartDisplacementAt(double Time) const; double GetDisplacementSpeedAt(double Time) const; // in unwarped song time double GetTimeAtBeat(double beat, double drift = 0) const; double GetOffset() const; + double GetMeasureTime(double msr) const; + + bool IsNoteTimeSorted(); std::map GetSoundList() const; ChartType GetChartType() const; bool IsBmson() const; @@ -45,7 +48,7 @@ namespace Game { // Drift is an offset to apply to _everything_. // Speed is a constant to set the speed to. - static GameChartData FromDifficulty(Difficulty *diff, double Drift = 0, double Speed = 0); + static PlayerChartState FromDifficulty(Difficulty *diff, double Drift = 0, double Speed = 0); }; } } \ No newline at end of file diff --git a/src/PlayerContext.cpp b/src/PlayerContext.cpp index 16e9d57e..200169fa 100644 --- a/src/PlayerContext.cpp +++ b/src/PlayerContext.cpp @@ -15,20 +15,16 @@ CfgVar DebugNoteRendering("NoteRender", "Debug"); namespace Game { namespace VSRG { - PlayerContext::PlayerContext(int pn, Game::VSRG::Parameters p) : PlayerNoteskin(this) + PlayerContext::PlayerContext(int pn, Game::VSRG::PlayscreenParameters p) : PlayerNoteskin(this) { PlayerNumber = pn; Drift = 0; JudgeNotes = true; - LifebarType = LT_AUTO; - ScoringType = ST_EXP3; PlayerScoreKeeper = std::make_shared(); Parameters = p; - Hidden = {}; - Gear = {}; if (!fnt && DebugNoteRendering) { @@ -38,7 +34,7 @@ namespace Game { } void PlayerContext::Init() { - PlayerNoteskin.SetupNoteskin(ChartData.HasTurntable, CurrentDiff->Channels); + PlayerNoteskin.SetupNoteskin(ChartState.HasTurntable, CurrentDiff->Channels); } void PlayerContext::Validate() { @@ -46,182 +42,55 @@ namespace Game { MsDisplayMargin = (Configuration::GetSkinConfigf("HitErrorDisplayLimiter")); - if (!BindKeysToLanes(ChartData.HasTurntable)) - if (!BindKeysToLanes(!ChartData.HasTurntable)) + if (!BindKeysToLanes(ChartState.HasTurntable)) + if (!BindKeysToLanes(!ChartState.HasTurntable)) Log::LogPrintf("Couldn't get valid bindings for current key count %d.\n", GetChannelCount()); - if (PlayerNoteskin.IsBarlineEnabled()) Barline = std::make_unique(); } - const GameChartData& PlayerContext::GetPlayerState() + const PlayerChartState& PlayerContext::GetPlayerState() { - return ChartData; + return ChartState; } void PlayerContext::SetupMechanics() { using namespace Game::VSRG; - bool disable_forced_release = false; - - // This must be done before setLifeTotal in order for it to work. - PlayerScoreKeeper->setMaxNotes(CurrentDiff->TotalScoringObjects); - - // JudgeScale, Stepmania and OD can't be run together - only one can be set. - auto TimingInfo = CurrentDiff->Data->TimingInfo.get(); - - // Pick a timing system - if (Parameters.SystemType == VSRG::TI_NONE) { - if (TimingInfo) { - // Automatic setup - Parameters.SystemType = TimingInfo->GetType(); - } - else { - Log::Printf("Null timing info - assigning raindrop defaults.\n"); - Parameters.SystemType = VSRG::TI_RAINDROP; - // pick raindrop system for null Timing Info - } - } - - // If we got just assigned one or was already requested - // unlikely: timing info type is none? what - retry: - if (Parameters.SystemType != VSRG::TI_NONE) { - // Player requested a specific subsystem - if (Parameters.SystemType == VSRG::TI_BMS || Parameters.SystemType == VSRG::TI_RDAC) { - disable_forced_release = true; - ScoringType = ST_EX; - UsedTimingType = TT_TIME; - if (TimingInfo->GetType() == VSRG::TI_BMS) { - auto Info = static_cast (TimingInfo); - PlayerScoreKeeper->setLifeTotal(Info->GaugeTotal); - } - else { - PlayerScoreKeeper->setJudgeRank(3); - } - } - else if (Parameters.SystemType == VSRG::TI_O2JAM) { - ScoringType = ST_O2JAM; - UsedTimingType = TT_BEATS; - PlayerScoreKeeper->setJudgeRank(-100); - } - else if (Parameters.SystemType == VSRG::TI_OSUMANIA) { - ScoringType = ST_OSUMANIA; - UsedTimingType = TT_TIME; - if (TimingInfo->GetType() == VSRG::TI_OSUMANIA) { - auto InfoOM = static_cast (TimingInfo); - PlayerScoreKeeper->setODWindows(InfoOM->OD); - } - else PlayerScoreKeeper->setODWindows(7); - } - else if (Parameters.SystemType == VSRG::TI_STEPMANIA) { - disable_forced_release = true; - UsedTimingType = TT_TIME; - ScoringType = ST_DP; - PlayerScoreKeeper->setSMJ4Windows(); - } - else if (Parameters.SystemType == VSRG::TI_RAINDROP) { - ScoringType = ST_EXP3; - LifebarType = LT_STEPMANIA; - } - } - else - { - Log::Printf("System picked was none - on purpose. Defaulting to raindrop.\n"); - Parameters.SystemType = VSRG::TI_RAINDROP; - goto retry; - } - // Timing System is set up. Set up life bar - if (Parameters.GaugeType == LT_AUTO) { - using namespace VSRG; - switch (Parameters.SystemType) { - case TI_BMS: - case TI_RAINDROP: - case TI_RDAC: - Parameters.GaugeType = LT_GROOVE; - break; - case TI_O2JAM: - Parameters.GaugeType = LT_O2JAM; - break; - case TI_OSUMANIA: - case TI_STEPMANIA: - Parameters.GaugeType = LT_STEPMANIA; - break; - default: - throw std::runtime_error("Invalid requested system."); - } - } + MechanicsSet = Parameters.PrepareMechanicsSet(CurrentDiff, PlayerScoreKeeper, GetJudgmentY()); + MechanicsSet->TransformNotes(ChartState); - switch (Parameters.GaugeType) { - case LT_STEPMANIA: - LifebarType = LT_STEPMANIA; // Needs no setup. - break; - - case LT_O2JAM: - if (TimingInfo->GetType() == VSRG::TI_O2JAM) { - auto InfoO2 = static_cast (TimingInfo); - PlayerScoreKeeper->setO2LifebarRating(InfoO2->Difficulty); - } // else by default - LifebarType = LT_O2JAM; // By default, HX - break; - - case LT_GROOVE: - case LT_DEATH: - case LT_EASY: - case LT_EXHARD: - case LT_SURVIVAL: - if (TimingInfo->GetType() == VSRG::TI_BMS) { // Only needs setup if it's a BMS file - auto Info = static_cast (TimingInfo); - PlayerScoreKeeper->setLifeTotal(Info->GaugeTotal); - } - else // by raindrop defaults - PlayerScoreKeeper->setLifeTotal(-1); - LifebarType = (LifeType)Parameters.GaugeType; - break; - case LT_NORECOV: - LifebarType = LT_NORECOV; - break; - default: - throw std::runtime_error("Invalid gauge type recieved"); - } - - /* - If we're on TT_BEATS we've got to recalculate all note positions to beats, - and use mechanics that use TT_BEATS as its timing type. - */ - - if (UsedTimingType == TT_TIME) - { - if (Parameters.SystemType == VSRG::TI_RDAC) - { - Log::Printf("RAINDROP ARCADE STAAAAAAAAART!\n"); - MechanicsSet = std::make_unique(); - } - else { - Log::Printf("Using raindrop mechanics set!\n"); - // Only forced release if not a bms or a stepmania chart. - MechanicsSet = std::make_unique(!disable_forced_release); - } - } - else if (UsedTimingType == TT_BEATS) - { - Log::Printf("Using o2jam mechanics set!\n"); - MechanicsSet = std::make_unique(); - NoteTransform::TransformToBeats(GetChannelCount(), ChartData.NotesByChannel, ChartData.BPS); - } MechanicsSet->Setup(CurrentDiff.get(), PlayerScoreKeeper); - MechanicsSet->HitNotify = std::bind(&PlayerContext::HitNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4); - MechanicsSet->MissNotify = std::bind(&PlayerContext::MissNote, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, std::placeholders::_5); - MechanicsSet->IsLaneKeyDown = std::bind(&PlayerContext::GetGearLaneState, this, std::placeholders::_1); - MechanicsSet->SetLaneHoldingState = std::bind(&PlayerContext::SetLaneHoldState, this, std::placeholders::_1, std::placeholders::_2); + // Setup mechanics set callbacks + MechanicsSet->HitNotify = std::bind(&PlayerContext::HitNote, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4); + + MechanicsSet->MissNotify = std::bind(&PlayerContext::MissNote, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5); + + MechanicsSet->IsLaneKeyDown = std::bind(&PlayerContext::GetGearLaneState, this, + std::placeholders::_1); + + MechanicsSet->SetLaneHoldingState = std::bind(&PlayerContext::SetLaneHoldState, this, + std::placeholders::_1, + std::placeholders::_2); + MechanicsSet->PlayNoteSoundEvent = PlayKeysound; // We're set - setup all of the variables that depend on mechanics, scoring etc.. to their initial values. } + void PlayerContext::TranslateKey(int32_t Index, bool KeyDown, double Time) { if (Parameters.Auto) @@ -264,7 +133,7 @@ namespace Game { double PlayerContext::GetCurrentBPM() const { - return ChartData.GetBpmAt(GetWarpedSongTime()); + return ChartState.GetBpmAt(GetWarpedSongTime()); } double PlayerContext::GetJudgmentY() const @@ -283,17 +152,17 @@ namespace Game { double PlayerContext::GetDuration() const { - return ChartData.ConnectedDifficulty->Duration; + return ChartState.ConnectedDifficulty->Duration; } double PlayerContext::GetBeatDuration() const { - return ChartData.GetBeatAt(GetDuration()); + return ChartState.GetBeatAt(GetDuration()); } int PlayerContext::GetChannelCount() const { - return ChartData.ConnectedDifficulty->Channels; + return ChartState.ConnectedDifficulty->Channels; } int PlayerContext::GetPlayerNumber() const @@ -311,12 +180,12 @@ namespace Game { bool PlayerContext::GetUsesTurntable() const { - return ChartData.HasTurntable; + return ChartState.HasTurntable; } double PlayerContext::GetAppliedSpeedMultiplier(double Time) const { - auto sm = ChartData.GetSpeedMultiplierAt(Time); + auto sm = ChartState.GetSpeedMultiplierAt(Time); if (Parameters.Upscroll) return -sm; else @@ -325,22 +194,22 @@ namespace Game { double PlayerContext::GetCurrentBeat() const { - return ChartData.GetBeatAt(LastUpdateTime); + return ChartState.GetBeatAt(LastUpdateTime); } double PlayerContext::GetUserMultiplier() const { - return Parameters.SpeedMultiplier; + return Parameters.UserSpeedMultiplier; } double PlayerContext::GetCurrentVerticalSpeed() const { - return ChartData.GetDisplacementSpeedAt(LastUpdateTime); + return ChartState.GetDisplacementSpeedAt(LastUpdateTime); } double PlayerContext::GetWarpedSongTime() const { - return ChartData.GetWarpedSongTime(LastUpdateTime); + return ChartState.GetWarpedSongTime(LastUpdateTime); } void PlayerContext::SetupLua(LuaManager *Env) @@ -362,7 +231,7 @@ namespace Game { .addProperty("JudgmentY", &PlayerContext::GetJudgmentY) .addProperty("Difficulty", &PlayerContext::GetDifficulty) .addProperty("Turntable", &PlayerContext::GetUsesTurntable) - .addProperty("SpeedMultiplier", &PlayerContext::GetUserMultiplier, &PlayerContext::SetUserMultiplier) + .addProperty("UserSpeedMultiplier", &PlayerContext::GetUserMultiplier, &PlayerContext::SetUserMultiplier) .addProperty("HasTurntable", &PlayerContext::GetUsesTurntable) .addProperty("LifebarPercent", &PlayerContext::GetLifePST) .addProperty("Difficulty", &PlayerContext::GetDifficulty) @@ -379,7 +248,7 @@ namespace Game { double PlayerContext::GetScore() const { - return PlayerScoreKeeper->getScore(ScoringType); + return PlayerScoreKeeper->getScore(Parameters.GetScoringType()); } int PlayerContext::GetCombo() const @@ -393,7 +262,7 @@ namespace Game { std::string value; std::vector res; - auto CurrentDiff = ChartData.ConnectedDifficulty; + auto CurrentDiff = ChartState.ConnectedDifficulty; if (UseTurntable) KeyProfile = (std::string)CfgVar("KeyProfileSpecial" + Utility::IntToStr(CurrentDiff->Channels)); @@ -427,25 +296,10 @@ namespace Game { void PlayerContext::HitNote(double TimeOff, uint32_t Lane, bool IsHold, bool IsHoldRelease) { - int Judgment = PlayerScoreKeeper->hitNote(TimeOff); + auto Judgment = PlayerScoreKeeper->hitNote(TimeOff); - if (Animations) { - if (Animations->GetEnv()->CallFunction("HitEvent", 6)) - { - Animations->GetEnv()->PushArgument(Judgment); - Animations->GetEnv()->PushArgument(TimeOff); - Animations->GetEnv()->PushArgument((int)Lane + 1); - Animations->GetEnv()->PushArgument(IsHold); - Animations->GetEnv()->PushArgument(IsHoldRelease); - Animations->GetEnv()->PushArgument(PlayerNumber); - Animations->GetEnv()->RunFunction(); - } - - if (PlayerScoreKeeper->getMaxNotes() == PlayerScoreKeeper->getScore(ST_NOTES_HIT)) - Animations->DoEvent("OnFullComboEvent"); - } - - //OnHit(TimeOff, Lane, IsHold, IsHoldRelease); + if (OnHit) + OnHit(Judgment, TimeOff, Lane, IsHold, IsHoldRelease, PlayerNumber); } void PlayerContext::MissNote(double TimeOff, uint32_t Lane, bool IsHold, bool dont_break_combo, bool early_miss) @@ -455,18 +309,8 @@ namespace Game { if (IsHold) Gear.HeldKey[Lane] = false; - if (Animations) { - if (Animations->GetEnv()->CallFunction("MissEvent", 4)) - { - Animations->GetEnv()->PushArgument(TimeOff); - Animations->GetEnv()->PushArgument((int)Lane + 1); - Animations->GetEnv()->PushArgument(IsHold); - Animations->GetEnv()->PushArgument(PlayerNumber); - Animations->GetEnv()->RunFunction(); - } - } - - //OnMiss(TimeOff, Lane, IsHold, dont_break_combo, early_miss); + if (OnMiss) + OnMiss(TimeOff, Lane, IsHold, dont_break_combo, early_miss, PlayerNumber); } void PlayerContext::SetLaneHoldState(uint32_t Lane, bool NewState) @@ -484,12 +328,12 @@ namespace Game { double PlayerContext::GetChartTimeAt(double time) const { - if (UsedTimingType == TT_BEATS) { - return ChartData.GetBeatAt(time); + if (MechanicsSet->GetTimingKind() == TT_BEATS) { + return ChartState.GetBeatAt(time); } - else if (UsedTimingType == TT_TIME) { + /*else if (MechanicsSet->GetTimingKind() == TT_TIME) { return time; - } + }*/ else return time; } @@ -553,7 +397,7 @@ namespace Game { timeClosest[i] = std::numeric_limits::infinity(); double usedTime = GetChartTimeAt(time); - auto &NotesByChannel = ChartData.NotesByChannel; + auto &NotesByChannel = ChartState.NotesByChannel; for (auto k = 0U; k < CurrentDiff->Channels; k++) { @@ -594,12 +438,12 @@ namespace Game { if (!CanJudge()) return; // don't judge any more after stage is failed. - auto &NotesByChannel = ChartData.NotesByChannel; + auto &NotesByChannel = ChartState.NotesByChannel; auto Start = NotesByChannel[Lane].begin(); auto End = NotesByChannel[Lane].end(); // Use this optimization when we can make sure vertical properly aligns up with time. - if (UseNoteOptimization()) + if (ChartState.IsNoteTimeSorted()) { // In comparison to the regular compare function, since end times are what matter with holds (or lift events, where start == end) // this does the job as it should instead of comparing start times where hold tails would be completely ignored. @@ -612,8 +456,12 @@ namespace Game { return A < B.GetEndTime(); }; - auto timeLower = (Time - (PlayerScoreKeeper->usesO2() ? PlayerScoreKeeper->getMissCutoffMS() : (PlayerScoreKeeper->getMissCutoffMS() / 1000.0))); - auto timeHigher = (Time + (PlayerScoreKeeper->usesO2() ? PlayerScoreKeeper->getJudgmentCutoff() : (PlayerScoreKeeper->getJudgmentCutoff() / 1000.0))); + auto timeLower = (Time - (PlayerScoreKeeper->usesO2() ? + PlayerScoreKeeper->getMissCutoffMS() : + (PlayerScoreKeeper->getMissCutoffMS() / 1000.0))); + auto timeHigher = (Time + (PlayerScoreKeeper->usesO2() ? + PlayerScoreKeeper->getJudgmentCutoff() : + (PlayerScoreKeeper->getJudgmentCutoff() / 1000.0))); Start = std::lower_bound(NotesByChannel[Lane].begin(), NotesByChannel[Lane].end(), timeLower, LboundFunc); @@ -656,28 +504,23 @@ namespace Game { void PlayerContext::SetUnwarpedTime(double time) { - ChartData.DisableNotesUntil(time); + ChartState.ResetNotes(); + ChartState.DisableNotesUntil(time); } - int PlayerContext::GetCurrentGaugeType() + int PlayerContext::GetCurrentGaugeType() const { - return LifebarType; + return Parameters.GaugeType; } - int PlayerContext::GetCurrentScoreType() + int PlayerContext::GetCurrentScoreType() const { - return ScoringType; + return Parameters.GetScoringType(); } - int PlayerContext::GetCurrentSystemType() + int PlayerContext::GetCurrentSystemType() const { - return UsedTimingType; - } - - bool PlayerContext::UseNoteOptimization() - { - return !ChartData.Warps.size() // The actual time is scrambled - && !ChartData.HasNegativeScroll; // Draw order may be incorrect + return MechanicsSet->GetTimingKind(); } void PlayerContext::JudgeLane(uint32_t Lane, double Time) @@ -687,16 +530,20 @@ namespace Game { if (!CanJudge()) return; - auto &Notes = ChartData.NotesByChannel[Lane]; + auto &Notes = ChartState.NotesByChannel[Lane]; auto Start = Notes.begin(); auto End = Notes.end(); // Use this optimization when we can make sure vertical properly aligns up with time, as with ReleaseLane. - if (UseNoteOptimization()) + if (ChartState.IsNoteTimeSorted()) { - auto timeLower = (Time - (PlayerScoreKeeper->usesO2() ? PlayerScoreKeeper->getMissCutoffMS() : (PlayerScoreKeeper->getMissCutoffMS() / 1000.0))); - auto timeHigher = (Time + (PlayerScoreKeeper->usesO2() ? PlayerScoreKeeper->getJudgmentCutoff() : (PlayerScoreKeeper->getJudgmentCutoff() / 1000.0))); + auto timeLower = (Time - (PlayerScoreKeeper->usesO2() ? + PlayerScoreKeeper->getMissCutoffMS() : + (PlayerScoreKeeper->getMissCutoffMS() / 1000.0))); + auto timeHigher = (Time + (PlayerScoreKeeper->usesO2() ? + PlayerScoreKeeper->getJudgmentCutoff() : + (PlayerScoreKeeper->getJudgmentCutoff() / 1000.0))); Start = std::lower_bound(Notes.begin(), Notes.end(), timeLower); End = std::upper_bound(Notes.begin(), Notes.end(), timeHigher); @@ -733,12 +580,12 @@ namespace Game { bool PlayerContext::HasFailed() const { - return PlayerScoreKeeper->isStageFailed(LifebarType) && !Parameters.NoFail; + return PlayerScoreKeeper->isStageFailed(GetCurrentGaugeType()) && !Parameters.NoFail; } bool PlayerContext::HasDelayedFailure() { - return PlayerScoreKeeper->hasDelayedFailure(LifebarType); + return PlayerScoreKeeper->hasDelayedFailure(GetCurrentGaugeType()); } double PlayerContext::GetClosestNoteTime(int lane) const @@ -751,12 +598,7 @@ namespace Game { void PlayerContext::SetUserMultiplier(float Multip) { - Parameters.SpeedMultiplier = Multip; - } - - void PlayerContext::SetSceneEnvironment(std::shared_ptr env) - { - Animations = env; + Parameters.UserSpeedMultiplier = Multip; } void PlayerContext::SetPlayableData(std::shared_ptr diff, double Drift, @@ -765,82 +607,9 @@ namespace Game { CurrentDiff = diff; this->Drift = Drift; - /* - * There are four kinds of speed modifiers: - * -CMod (Keep speed the same through the song, equal to a constant) - * -MMod (Find highest speed and set multiplier to such that the highest speed is equal to a constant) - * -First (Find the first speed in the chart, and set multiplier to such that the first speed is equal to a constant) - * -Mode (Find the speed that lasts the most in the chart, set multiplier based on that) - * - * The calculations are done ahead, and while SpeedConstant = 0 either MMod or first are assumed - * but only if there's a constant specified by the user. - */ - - DesiredDefaultSpeed /= Parameters.Rate; - - if (DesiredDefaultSpeed) - { - if (Type == SPEEDTYPE_CMOD) // cmod - { - Parameters.SpeedMultiplier = 1; - ChartData = VSRG::GameChartData::FromDifficulty(CurrentDiff.get(), Drift, DesiredDefaultSpeed); - } else - ChartData = VSRG::GameChartData::FromDifficulty(CurrentDiff.get(), Drift); - - if (Type == SPEEDTYPE_MMOD) // mmod - { - double speed_max = 0; // Find the highest speed - for (auto i : ChartData.VSpeeds) - { - speed_max = std::max(speed_max, abs(i.Value)); - } - - double Ratio = DesiredDefaultSpeed / speed_max; // How much above or below are we from the maximum speed? - Parameters.SpeedMultiplier = Ratio; - } - else if (Type == SPEEDTYPE_FIRST) // First speed. - { - double DesiredMultiplier = DesiredDefaultSpeed / ChartData.VSpeeds[0].Value; - Parameters.SpeedMultiplier = DesiredMultiplier; - } - else if (Type == SPEEDTYPE_MODE) // Most lasting speed. - { - std::map freq; - for (auto i = ChartData.VSpeeds.begin(); i != ChartData.VSpeeds.end(); i++) - { - if (i + 1 != ChartData.VSpeeds.end()) - { - freq[i->Value] += (i + 1)->Time - i->Time; - } - else freq[i->Value] += abs(CurrentDiff->Duration - i->Time); - } - auto max = -std::numeric_limits::infinity(); - auto val = 1000.f; - for (auto i : freq) - { - if (i.second > max) - { - max = i.second; - val = i.first; - } - } - - Parameters.SpeedMultiplier = DesiredDefaultSpeed / val; - } - else if (Type != SPEEDTYPE_CMOD) // other cases - { - double bpsd = 4.0 / (ChartData.BPS[0].Value); - double Speed = (MeasureBaseSpacing / bpsd); - double DesiredMultiplier = DesiredDefaultSpeed / Speed; - - Parameters.SpeedMultiplier = DesiredMultiplier; - } - } - else - ChartData = VSRG::GameChartData::FromDifficulty(CurrentDiff.get(), Drift); - - if (Parameters.Random) - NoteTransform::Randomize(ChartData.NotesByChannel, CurrentDiff->Channels, CurrentDiff->Data->Turntable); + auto d = Parameters.Setup(DesiredDefaultSpeed, Type, Drift, diff); + ChartState = *d; + delete d; SetupMechanics(); } @@ -852,39 +621,18 @@ namespace Game { // Load up BGM events auto BGMs = CurrentDiff->Data->BGMEvents; if (DisableKeysounds) - NoteTransform::MoveKeysoundsToBGM(CurrentDiff->Channels, ChartData.NotesByChannel, BGMs, Drift); + NoteTransform::MoveKeysoundsToBGM(CurrentDiff->Channels, ChartState.NotesByChannel, BGMs, Drift); return BGMs; } - double PlayerContext::GetMeasureTime(double msr) const - { - double beat = 0; - - if (msr <= 0) - { - return 0; - } - - auto whole = int(floor(msr)); - auto fraction = msr - double(whole); - for (auto i = 0; i < whole; i++) - beat += CurrentDiff->Data->Measures[i].Length; - beat += CurrentDiff->Data->Measures[whole].Length * fraction; - - Log::Logf("Warping to measure measure %d at beat %f.\n", whole, beat); - - return ChartData.GetTimeAtBeat(beat); - } - double PlayerContext::HasSongFinished(double time) const { - double wt = ChartData.GetWarpedSongTime(time); + double wt = ChartState.GetWarpedSongTime(time); double cutoff; if (PlayerScoreKeeper->usesO2()) { // beat-based judgements - - double curBPS = ChartData.GetBpsAt(time); + double curBPS = ChartState.GetBpsAt(time); double cutoffspb = 1 / curBPS; cutoff = cutoffspb * PlayerScoreKeeper->getMissCutoffMS(); @@ -904,21 +652,14 @@ namespace Game { void PlayerContext::GearKeyEvent(uint32_t Lane, bool KeyDown) { - if (Animations) { - if (Animations->GetEnv()->CallFunction("GearKeyEvent", 3)) - { - Animations->GetEnv()->PushArgument((int)Lane + 1); - Animations->GetEnv()->PushArgument(KeyDown); - Animations->GetEnv()->PushArgument(PlayerNumber); - - Animations->GetEnv()->RunFunction(); - } + if (OnGearKeyEvent) { + OnGearKeyEvent(Lane, KeyDown, PlayerNumber); } } void PlayerContext::Update(double SongTime) { - PlayerNoteskin.Update(SongTime - LastUpdateTime, ChartData.GetBeatAt(ChartData.GetWarpedSongTime(SongTime))); + PlayerNoteskin.Update(SongTime - LastUpdateTime, ChartState.GetBeatAt(ChartState.GetWarpedSongTime(SongTime))); LastUpdateTime = SongTime; RunMeasures(SongTime); } @@ -931,6 +672,7 @@ namespace Game { double PlayerContext::GetLifePST() const { + auto LifebarType = GetCurrentGaugeType(); auto lifebar_amount = PlayerScoreKeeper->getLifebarAmount(LifebarType); if (LifebarType == LT_GROOVE || LifebarType == LT_EASY) return std::max(2, int(floor(lifebar_amount * 50) * 2)); @@ -962,12 +704,12 @@ namespace Game { } } - void PlayerContext::DrawBarlines(double CurrentVertical, double SpeedMultiplier) + void PlayerContext::DrawBarlines(double CurrentVertical, double UserSpeedMultiplier) { - for (auto i : ChartData.MeasureBarlines) + for (auto i : ChartState.MeasureBarlines) { - double realV = (CurrentVertical - i) * SpeedMultiplier + - PlayerNoteskin.GetBarlineOffset() * sign(SpeedMultiplier) + GetJudgmentY(); + double realV = (CurrentVertical - i) * UserSpeedMultiplier + + PlayerNoteskin.GetBarlineOffset() * sign(UserSpeedMultiplier) + GetJudgmentY(); if (realV > 0 && realV < ScreenWidth) { Barline->SetLocation(Vec2(PlayerNoteskin.GetBarlineStartX(), realV), @@ -977,80 +719,6 @@ namespace Game { } } - void PlayerContext::UpdateHidden(float AdjustmentSize, float FlashlightRatio) - { - /* - Given the top of the screen being 1, the bottom being -1 - calculate the range for which the current hidden mode is defined. - */ - float Center; - float JudgmentLinePos = GetJudgmentY(); - auto Upscroll = Parameters.Upscroll; - - // Hidden calc - if (Parameters.HiddenMode) - { - float LimPos = -((JudgmentLinePos / ScreenHeight) * 2 - 1); // Frac. of screen - float AdjustmentSize; - - if (Upscroll) - { - Center = -((((ScreenHeight - JudgmentLinePos) / 2 + JudgmentLinePos) / ScreenHeight) * 2 - 1); - - // AdjustmentSize = -( ((ScreenHeight - JudgmentLinePos) / 2 / ScreenHeight) - 1 ); // A quarter of the playing field. - - if (Parameters.HiddenMode == HM_HIDDEN) - { - Hidden.ClampHigh = Center; - Hidden.ClampLow = -1 + AdjustmentSize; - } - else if (Parameters.HiddenMode == HM_SUDDEN) - { - Hidden.ClampHigh = LimPos - AdjustmentSize; - Hidden.ClampLow = Center; - } - - // Invert Hidden Mode. - if (Parameters.HiddenMode == HM_SUDDEN) Hidden.Mode = HM_HIDDEN; - else if (Parameters.HiddenMode == HM_HIDDEN) Hidden.Mode = HM_SUDDEN; - else Hidden.Mode = (Game::VSRG::EHiddenMode)Parameters.HiddenMode; - } - else - { - Center = -((JudgmentLinePos / 2 / ScreenHeight) * 2 - 1); - - // AdjustmentSize = -( ((JudgmentLinePos) / 2 / ScreenHeight) - 1 ); // A quarter of the playing field. - - // Hidden/Sudden - if (Parameters.HiddenMode == HM_HIDDEN) - { - Hidden.ClampHigh = 1 - AdjustmentSize; - Hidden.ClampLow = Center; - } - else if (Parameters.HiddenMode == HM_SUDDEN) - { - Hidden.ClampHigh = Center; - Hidden.ClampLow = LimPos + AdjustmentSize; - } - - Hidden.Mode = (Game::VSRG::EHiddenMode)Parameters.HiddenMode; - } - - if (Parameters.HiddenMode == HM_FLASHLIGHT) // Flashlight - { - Hidden.ClampLow = Center - FlashlightRatio; - Hidden.ClampHigh = Center + FlashlightRatio; - Hidden.ClampSum = -Center; - Hidden.ClampFactor = 1 / FlashlightRatio; - } - else // Hidden/Sudden - { - Hidden.ClampSum = -Hidden.ClampLow; - Hidden.ClampFactor = 1 / (Hidden.ClampHigh + Hidden.ClampSum); - } - } - } - Mat4 id; int PlayerContext::DrawMeasures(double song_time) @@ -1060,35 +728,40 @@ namespace Game { DrawMeasures should get the unwarped song time. Internally, it uses warped song time. */ - auto wt = ChartData.GetWarpedSongTime(song_time); + auto wt = ChartState.GetWarpedSongTime(song_time); // note Y displacement at song_time - auto vert = ChartData.GetDisplacementAt(wt); + auto chart_displacement = ChartState.GetChartDisplacementAt(wt); // effective speed multiplier - auto chartmul = GetAppliedSpeedMultiplier(song_time); - auto effmul = chartmul * Parameters.SpeedMultiplier; + auto chart_multiplier = GetAppliedSpeedMultiplier(song_time); + auto effective_chart_speed_multiplier = chart_multiplier * Parameters.UserSpeedMultiplier; // since + is downward, - is upward! - bool upscrolling = effmul < 0; + bool upscrolling = effective_chart_speed_multiplier < 0; if (PlayerNoteskin.IsBarlineEnabled()) - DrawBarlines(vert, effmul); + DrawBarlines(chart_displacement, effective_chart_speed_multiplier); // Set some parameters... - Renderer::SetShaderParameters(false, false, true, true, false, false, Hidden.Mode); + Renderer::SetShaderParameters(false, false, true, true, false, false, Parameters.GetHiddenMode()); // Sudden = 1, Hidden = 2, flashlight = 3 (Defined in the shader) - if (Hidden.Mode) + if (Parameters.GetHiddenMode()) { - Renderer::Shader::SetUniform(Renderer::DefaultShader::GetUniform(Renderer::U_HIDLOW), Hidden.ClampLow); - Renderer::Shader::SetUniform(Renderer::DefaultShader::GetUniform(Renderer::U_HIDHIGH), Hidden.ClampHigh); - Renderer::Shader::SetUniform(Renderer::DefaultShader::GetUniform(Renderer::U_HIDFAC), Hidden.ClampFactor); - Renderer::Shader::SetUniform(Renderer::DefaultShader::GetUniform(Renderer::U_HIDSUM), Hidden.ClampSum); + Renderer::Shader::SetUniform( + Renderer::DefaultShader::GetUniform(Renderer::U_HIDCENTER), + Parameters.GetHiddenCenter()); + Renderer::Shader::SetUniform( + Renderer::DefaultShader::GetUniform(Renderer::U_HIDSIZE), + Parameters.GetHiddenTransitionSize()); + Renderer::Shader::SetUniform( + Renderer::DefaultShader::GetUniform(Renderer::U_HIDFLSIZE), + Parameters.GetHiddenCenterSize()); } Renderer::SetPrimitiveQuadVBO(); - auto &NotesByChannel = ChartData.NotesByChannel; + auto &NotesByChannel = ChartState.NotesByChannel; auto jy = GetJudgmentY(); for (auto k = 0U; k < CurrentDiff->Channels; k++) @@ -1096,29 +769,37 @@ namespace Game { // From the note's vertical StaticVert transform to position on screen. auto Locate = [&](double StaticVert) -> double { - return (vert - StaticVert) * effmul + jy; + return (chart_displacement - StaticVert) * effective_chart_speed_multiplier + jy; }; auto Start = NotesByChannel[k].begin(); auto End = NotesByChannel[k].end(); // We've got guarantees about our note locations. - if (UseNoteOptimization()) + if (ChartState.IsNoteTimeSorted()) { /* Find the location of the first/next visible regular note */ - auto LocPredicate = [&](const TrackNote &A, double _) -> bool + auto LocPredicate = [&](const TrackNote &A, double TrackDisplacement) -> bool { if (!upscrolling) - return _ < Locate(A.GetVertical()); + return TrackDisplacement < Locate(A.GetVertical()); else // Signs are switched. We need to preserve the same order. - return _ > Locate(A.GetVertical()); + return TrackDisplacement > Locate(A.GetVertical()); }; // Signs are switched. Doesn't begin by the first note closest to the lower edge, but the one closest to the higher edge. if (!upscrolling) - Start = std::lower_bound(NotesByChannel[k].begin(), NotesByChannel[k].end(), ScreenHeight + PlayerNoteskin.GetNoteOffset(), LocPredicate); + Start = std::lower_bound( + NotesByChannel[k].begin(), + NotesByChannel[k].end(), + ScreenHeight + PlayerNoteskin.GetNoteOffset(), + LocPredicate); else - Start = std::lower_bound(NotesByChannel[k].begin(), NotesByChannel[k].end(), 0 - PlayerNoteskin.GetNoteOffset(), LocPredicate); + Start = std::lower_bound( + NotesByChannel[k].begin(), + NotesByChannel[k].end(), + 0 - PlayerNoteskin.GetNoteOffset(), + LocPredicate); // Locate the first hold that we can draw in this range /* @@ -1126,17 +807,15 @@ namespace Game { since only head locations are used. Find this possible hold by checking if it intersects the screen. */ - auto rStart = std::reverse_iterator::iterator>(Start); - for (auto i = rStart; i != NotesByChannel[k].rend(); ++i) - { + if (Start != NotesByChannel[k].begin()) { + auto i = Start - 1;//std::reverse_iterator::iterator>(Start); if (i->IsHold() && i->IsVisible()) { auto Vert = Locate(i->GetVertical()); auto VertEnd = Locate(i->GetHoldEndVertical()); if (IntervalsIntersect(0, ScreenHeight, std::min(Vert, VertEnd), std::max(Vert, VertEnd))) { - Start = i.base() - 1; - break; + Start = i; } } } @@ -1163,15 +842,17 @@ namespace Game { VerticalHoldEnd = Locate(m->GetHoldEndVertical()); // Old check method that doesn't rely on a correct vertical ordering. - if (!UseNoteOptimization()) + if (!ChartState.IsNoteTimeSorted()) { if (m->IsHold()) { - if (!IntervalsIntersect(0, ScreenHeight, std::min(Vertical, VerticalHoldEnd), std::max(Vertical, VerticalHoldEnd))) continue; + if (!IntervalsIntersect(0, ScreenHeight, + std::min(Vertical, VerticalHoldEnd), std::max(Vertical, VerticalHoldEnd))) continue; } else { - if (Vertical < -PlayerNoteskin.GetNoteOffset() || Vertical > ScreenHeight + PlayerNoteskin.GetNoteOffset()) continue; + if (Vertical < -PlayerNoteskin.GetNoteOffset() || + Vertical > ScreenHeight + PlayerNoteskin.GetNoteOffset()) continue; } } @@ -1187,6 +868,7 @@ namespace Game { // We draw the body first, so that way the heads get drawn on top if (m->IsHold()) { + // todo: move this note state determination to the note itself enum : int { Failed, Active, BeingHit, SuccesfullyHit }; int Level = -1; @@ -1205,17 +887,19 @@ namespace Game { double Size; // If we're being hit and.. bool decrease_hold_size = PlayerNoteskin.ShouldDecreaseHoldSizeWhenBeingHit() && Level == 2; + auto reference_point = 0.0f; if (decrease_hold_size) { - Pos = (VerticalHoldEnd + JudgeY) / 2; - Size = VerticalHoldEnd - JudgeY; + reference_point = JudgeY; } else // We were failed, not being hit or were already hit { - Pos = (VerticalHoldEnd + Vertical) / 2; - Size = VerticalHoldEnd - Vertical; + reference_point = Vertical; } + Pos = (VerticalHoldEnd + reference_point) / 2; + Size = VerticalHoldEnd - reference_point; + PlayerNoteskin.DrawHoldBody(k, Pos, Size, Level); PlayerNoteskin.DrawHoldTail(*m, k, VerticalHoldEnd, Level); @@ -1242,15 +926,17 @@ namespace Game { if (DebugNoteRendering) { - fnt->Render(Utility::Format("NOTES RENDERED: %d\nN/O: %d\nRNG: %f to %f\nMULT/EFFECTIVEMULT/SPEED: %f/%f/%f", + fnt->Render(Utility::Format("NOTES RENDERED: %d\nSORTEDTIME: %d\nRNG: %f to %f\nMULT/EFFECTIVEMULT/SPEED: %f/%f/%f", rnc, - UseNoteOptimization(), - vert, vert + ScreenHeight, - chartmul, effmul, GetCurrentVerticalSpeed() * effmul), Vec2(0, 0)); + ChartState.IsNoteTimeSorted(), + chart_displacement, chart_displacement + ScreenHeight, + chart_multiplier, effective_chart_speed_multiplier, GetCurrentVerticalSpeed() * effective_chart_speed_multiplier), Vec2(0, 0)); } return rnc; } - } + + +} } diff --git a/src/PlayerContext.h b/src/PlayerContext.h index 3cb3464d..36df71aa 100644 --- a/src/PlayerContext.h +++ b/src/PlayerContext.h @@ -34,7 +34,7 @@ namespace Game { public: ; private: - GameChartData ChartData; + PlayerChartState ChartState; double LastUpdateTime; // seconds, song time @@ -49,17 +49,7 @@ namespace Game { double Drift; bool JudgeNotes; - Parameters Parameters; - - struct SHiddenData { - EHiddenMode Mode; - float ClampLow, ClampHigh, ClampFactor; - float ClampSum; - } Hidden; - - LifeType LifebarType; - ScoreType ScoringType; - + PlayscreenParameters Parameters; struct SGearState { std::map Bindings; @@ -74,32 +64,27 @@ namespace Game { bool TurntableEnabled; } Gear; - // Whether to use time-based binary search on notes - bool UseNoteOptimization(); - void DrawBarlines(double cur_vertical, double smult); int DrawMeasures(double song_time); // returns rendered note count - std::shared_ptr Animations; Noteskin PlayerNoteskin; int PlayerNumber; - int UsedTimingType; void SetupMechanics(); void RunMeasures(double time); void PlayLaneKeysound(uint32_t Lane); void RunAuto(TrackNote *m, double usedTime, uint32_t k); - void UpdateHidden(float AdjustmentSize, float FlashlightRatio); public: - PlayerContext(int pn, Game::VSRG::Parameters par = Game::VSRG::Parameters()); + PlayerContext(int pn, Game::VSRG::PlayscreenParameters par = Game::VSRG::PlayscreenParameters()); void Init(); void Validate(); void Update(double songTime); void Render(double songTime); - std::function PlayKeysound; - std::function OnHit; - std::function OnMiss; + std::function PlayKeysound; + std::function OnHit; + std::function OnMiss; + std::function OnGearKeyEvent; /* About this pointer's lifetime: @@ -107,7 +92,7 @@ namespace Game { */ void SetPlayableData(std::shared_ptr difficulty, double Drift = 0, double DesiredDefaultSpeed = 0, int Type = SPEEDTYPE_DEFAULT); - const GameChartData &GetPlayerState(); + const PlayerChartState &GetPlayerState(); // Getters (Lua) bool IsFailEnabled() const; @@ -129,7 +114,6 @@ namespace Game { int GetChannelCount() const; int GetPlayerNumber() const; bool GetIsHeldKey(int Lane) const; - double GetMeasureTime(double Msr) const; double HasSongFinished(double time) const; double GetWaitingTime(); @@ -148,7 +132,6 @@ namespace Game { // Setters void SetUserMultiplier(float Multip); - void SetSceneEnvironment(std::shared_ptr env); // Only if Difficulty->Data is not null. std::vector GetBgmData(); @@ -174,9 +157,9 @@ namespace Game { void SetUnwarpedTime(double time); - int GetCurrentGaugeType(); - int GetCurrentScoreType(); - int GetCurrentSystemType(); + int GetCurrentGaugeType() const; + int GetCurrentScoreType() const; + int GetCurrentSystemType() const; // Whether the player has actually failed or not bool HasFailed() const; diff --git a/src/RaindropRocketInterface.cpp b/src/RaindropRocketInterface.cpp index 788ce81e..7a874c15 100644 --- a/src/RaindropRocketInterface.cpp +++ b/src/RaindropRocketInterface.cpp @@ -209,7 +209,7 @@ namespace Engine { auto npath = GameState::GetInstance().GetSkinFile(path.CString()); #ifndef _WIN32 - FILE* F = fopen(Utility::Narrow(npath.wstring()).c_str(), "r"); + FILE* F = fopen(Utility::ToU8(npath.wstring()).c_str(), "r"); #else FILE* F = _wfopen(npath.c_str(), L"r"); #endif diff --git a/src/Renderer7K.cpp b/src/Renderer7K.cpp deleted file mode 100644 index 63524a72..00000000 --- a/src/Renderer7K.cpp +++ /dev/null @@ -1,14 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "Screen.h" - -#include "GameWindow.h" -#include "Rendering.h" -#include "Line.h" -#include "SceneEnvironment.h" - -#include "ScreenGameplay7K.h" -#include "Noteskin.h" - -using namespace VSRG; diff --git a/src/Rendering.cpp b/src/Rendering.cpp index 489d16b4..759a7df9 100644 --- a/src/Rendering.cpp +++ b/src/Rendering.cpp @@ -1,7 +1,7 @@ #include "pch.h" #include "GameWindow.h" -#include "Configuration.h" + #include "VBO.h" #include "Sprite.h" #include "Transformation.h" @@ -16,6 +16,8 @@ #include "Shader.h" +#include "ImageLoader.h" + const ColorRGB White = { 1, 1, 1, 1 }; const ColorRGB Black = { 0, 0, 0, 1 }; const ColorRGB Red = { 1, 0, 0, 1 }; @@ -28,6 +30,7 @@ namespace Renderer { VBO* TextureBuffer = nullptr; VBO* TempTextureBuffer = nullptr; VBO* ColorBuffer = nullptr; + Texture* xor_tex = nullptr; float QuadPositions[8] = { @@ -73,6 +76,13 @@ namespace Renderer { Texture::ForceRebind(); } + void SetXorTexParameters() { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + static bool Initialized = false; void InitializeRender() { @@ -93,10 +103,37 @@ namespace Renderer { ColorBuffer = new VBO(VBO::Static, sizeof(QuadColours) / sizeof(float)); ColorBuffer->Validate(); ColorBuffer->AssignData(QuadColours); + + // create xor texture + xor_tex = new Texture; + + std::vector buf(256 * 256); + for(int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + uint8_t x = i ^ j; + + buf[i * 256 + j] = (255 << 24) + x + (x << 8) + (x << 16); + } + } + + ImageData d(256, 256, nullptr); + d.Data = buf; + + xor_tex->SetTextureData2D(d); + xor_tex->fname = "xor"; + SetXorTexParameters(); // it's bound, apply parameters + + ImageLoader::RegisterTexture(xor_tex); Initialized = true; } } + Texture* GetXorTexture(){ + return xor_tex; + } + + + void SetTextureParameters(std::string Dir) { if (!Configuration::TextureParameterExists(Dir, "gen-mipmap") || @@ -201,7 +238,7 @@ namespace Renderer { bool BlackToTransparent, bool ReplaceColor, int8_t HiddenMode) { - DefaultShader::Bind(); + DefaultShader::StaticBind(); Shader::SetUniform(DefaultShader::GetUniform(U_INVERT), InvertColor); if (HiddenMode == -1) @@ -347,7 +384,7 @@ bool Sprite::RenderMinimalSetup() if (!Lighten) Renderer::DefaultShader::SetColor(Red, Green, Blue, Alpha); else - Renderer::DefaultShader::SetColor(Red * lf, Green * lf, Blue * lf, Alpha * lf); + Renderer::DefaultShader::SetColor(Red * lf, Green * lf, Blue * lf, Alpha); Renderer::DoQuadDraw(); @@ -371,7 +408,7 @@ void Sprite::Render() if (!Lighten) Renderer::DefaultShader::SetColor(Red, Green, Blue, Alpha); else - Renderer::DefaultShader::SetColor(Red * lf, Green * lf, Blue * lf, Alpha * lf); + Renderer::DefaultShader::SetColor(Red * lf, Green * lf, Blue * lf, Alpha); Renderer::SetCurrentObjectMatrix(mat); } @@ -417,27 +454,25 @@ void Sprite::Cleanup() void TruetypeFont::ReleaseCodepoint(int cp) { - if (Texes.find(cp) != Texes.end()) + if (Texes->find(cp) != Texes->end()) { - free(Texes[cp].tex); - glDeleteTextures(1, &Texes[cp].gltx); - Texes.erase(cp); + free(Texes->at(cp).tex); + glDeleteTextures(1, &Texes->at(cp).gltx); + Texes->erase(cp); } } -void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat4 &Transform) +void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat4 &Transform, const Vec2 &Scale) { const char* Text = In.c_str(); int Line = 0; size_t len = In.length(); - glm::vec3 vOffs(Position.x, Position.y + scale, 0); + glm::vec3 vOffs(Position.x, Position.y + Scale.y, 0); if (!IsValid) return; - UpdateWindowScale(); - - Renderer::DefaultShader::Bind(); + Renderer::DefaultShader::StaticBind(); Renderer::SetBlendingMode(BLEND_ALPHA); Renderer::SetShaderParameters(false, false, false, false, false, true); Renderer::DefaultShader::SetColor(Red, Green, Blue, Alpha); @@ -450,21 +485,23 @@ void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat utf8::iterator itend(nd, Text, nd); for (; it != itend; ++it) { - CheckCodepoint(*it); // Force a regeneration of this if necessary codepdata &cp = GetTexFromCodepoint(*it); unsigned char* tx = cp.tex; - glm::vec3 trans = vOffs + glm::vec3(cp.xofs, cp.yofs, 0); + glm::vec3 trans = vOffs + glm::vec3( + cp.xofs * Scale.x * Scale.y / SDF_SIZE, + cp.yofs * Scale.y / SDF_SIZE, 0); glm::mat4 dx; if (*it == 10) // utf-32 line feed { Line++; vOffs.x = Position.x; - vOffs.y = Position.y + scale * (Line + 1); + vOffs.y = Position.y + (Line + 1) * Scale.y; continue; } - dx = Transform * glm::translate(Mat4(), trans) * glm::scale(Mat4(), glm::vec3(cp.w, cp.h, 1)); + dx = Transform * glm::translate(Mat4(), trans) * + glm::scale(Mat4(), glm::vec3(cp.w * Scale.y / SDF_SIZE, cp.h * Scale.y / SDF_SIZE, 1)); // do the actual draw? if (cp.gltx == 0) @@ -479,10 +516,11 @@ void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + // SDF texture => filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, cp.tw, cp.th, 0, GL_ALPHA, GL_UNSIGNED_BYTE, tx); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, cp.w, cp.h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, tx); } else glBindTexture(GL_TEXTURE_2D, cp.gltx); @@ -498,7 +536,7 @@ void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat float aW = stbtt_GetCodepointKernAdvance(info.get(), *it, *next); int bW; stbtt_GetCodepointHMetrics(info.get(), *it, &bW, NULL); - vOffs.x += aW * virtualscale + bW * virtualscale; + vOffs.x += (aW * realscale + bW * realscale) * Scale.x * Scale.y / SDF_SIZE; } } } @@ -521,14 +559,18 @@ void TruetypeFont::Render(const std::string &In, const Vec2 &Position, const Mat void TruetypeFont::ReleaseTextures() { - for (auto i = Texes.begin(); - i != Texes.end(); + for (auto i = Texes->begin(); + i != Texes->end(); ++i) { - if (i->second.tex) - free(i->second.tex); - if (i->second.gltx) - glDeleteTextures(1, &i->second.gltx); + if (i->second.tex) { + free(i->second.tex); + i->second.tex = 0; + } + if (i->second.gltx) { + glDeleteTextures(1, &i->second.gltx); + i->second.gltx = 0; + } } } @@ -586,7 +628,7 @@ void Line::Render() glEnable(GL_DEPTH_TEST); } -void BitmapFont::Render(const std::string &In, const Vec2 &Position, const Mat4 &Transform) +void BitmapFont::Render(const std::string &In, const Vec2 &Position, const Mat4 &Transform, const Vec2 &Scale) { const char* Text = In.c_str(); int32_t Character = 0, Line = 0; diff --git a/src/Rendering.h b/src/Rendering.h index e0941730..a58cdcdc 100644 --- a/src/Rendering.h +++ b/src/Rendering.h @@ -32,6 +32,8 @@ namespace Renderer { VBO* GetDefaultGeometryBuffer(); VBO* GetDefaultTextureBuffer(); VBO* GetDefaultColorBuffer(); + + Texture* GetXorTexture(); } inline float l2gamma (float c) { diff --git a/src/RowifiedDifficulty7K.cpp b/src/RowifiedDifficulty7K.cpp index 2b61b678..9b39de37 100644 --- a/src/RowifiedDifficulty7K.cpp +++ b/src/RowifiedDifficulty7K.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Song7K.h" #include "Logging.h" #include "PlayerChartData.h" @@ -23,7 +23,7 @@ namespace Game { else QuantizeFunction = std::bind(PassThrough, std::placeholders::_1); - BPS = Game::VSRG::GameChartData::FromDifficulty(Source).BPS; + BPS = Game::VSRG::PlayerChartState::FromDifficulty(Source).BPS; CalculateMeasureAccomulation(); diff --git a/src/SDF.cpp b/src/SDF.cpp new file mode 100644 index 00000000..ec18f790 --- /dev/null +++ b/src/SDF.cpp @@ -0,0 +1,182 @@ +#include "pch.h" +#include "SDF.h" + +#include "Logging.h" + +// SDF algorithm: 8SSEDT +// Translation of implementation found at www.codersnotes.com/notes/signed-distance-fields/ +// for variable w/h, and slightly more "classed" +// fetched 12/06/2017 DD/MM/YYYY +// supposedly O(n) according to source, should be quick for our uses + +// comments added for myself + +#define INF 10000 + +// Internal structures +struct Point { + int dx, dy; + + int DistSqr() { + return dx*dx + dy*dy; + } +}; + +struct Grid { +public: + Point* point; // faster on debug than a vector. sorry lads + int w, h; + int s; + + Grid() { + point = NULL; + } + ~Grid() { + delete[] point; + } + + void Resize(int w, int h) { + this->w = w; + this->h = h; + point = new Point[w * h]; + s = w * h; + } + + Point Get(int x, int y) { + int idx = y * w + x; + if ( y >= h || y < 0 || x >= w || x < 0 ) + return { INF, INF }; + + return point[idx]; + } + + void Put(int x, int y, Point v) { + point[y * w + x] = v; + } + + int DistSqr(int x, int y) { + return Get(x, y).DistSqr(); + } + + void Compare(Point &p, int x, int y, int ox, int oy) { + Point other = Get(x + ox, y + oy); + other.dx += ox; + other.dy += oy; + + // minimize distance + if (other.DistSqr() < p.DistSqr()) + { + p = other; + } + } +}; + +void GenSDF(Grid &g) +{ + // no idea what these passes do + // but hey, a working implementation says it's like this + + for (int y = 0; y < g.h; y++) { + for (int x = 0; x < g.w; x++) { + Point p = g.Get(x, y); + g.Compare(p, x, y, -1, 0 ); + g.Compare(p, x, y, 0, -1); + g.Compare(p, x, y, -1, -1); + g.Compare(p, x, y, 1, -1 ); + g.Put(x, y, p); + } + + // backwards from height + for (int x = g.w - 1; x >= 0; x--) { + Point p = g.Get(x, y); + g.Compare(p, x, y, 1, 0); + g.Put(x, y, p); + } + } + + for (int y = g.h - 1; y >= 0; y--) + { + for (int x = g.w - 1; x >= 0; x--) + { + Point p = g.Get(x, y); + g.Compare(p, x, y, 1, 0); + g.Compare(p, x, y, 0, 1); + g.Compare(p, x, y, -1, 1); + g.Compare(p, x, y, 1, 1); + g.Put(x, y, p); + } + + for (int x = 0; x < g.w; x++) { + Point p = g.Get(x, y); + g.Compare(p, x, y, -1, 0); + g.Put(x, y, p); + } + } +} + +void ConvertToSDF(unsigned char* out, unsigned char* tex, int w, int h) { + // Generate initial SDF + Grid g1, g2; + g1.Resize(w, h); + g2.Resize(w, h); + + + // build grids + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + int idx = y * w + x; + if (tex[idx] < 128) { + g1.Put(x, y, { 0, 0 }); + g2.Put(x, y, { INF, INF }); + } + else { + g2.Put(x, y, { 0, 0 }); + g1.Put(x, y, { INF, INF }); + } + } + } + + // "propagate" + GenSDF(g1); + GenSDF(g2); + + int max = -INF; + int min = INF; + + std::vector preout(w * h); + // copy to float GL_ALPHA texture + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + auto d1 = sqrt(g1.DistSqr(x, y)); + auto d2 = sqrt(g2.DistSqr(x, y)); + int dist = round(d1 - d2); + + // dist = dist * 3 + 128; + + if (dist > max) max = dist; + if (dist < min) min = dist; + + preout[y * w + x] = dist; + } + } + + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) { + auto v = preout[y * w + x]; + //float o = round(float(v - min) / float(max - min) * 255.0); + + if (v < 0) { + float o = round(v * -128.0 / min + 128); + int d = Clamp((int)o, 0, 255); + out[y * w + x] = d; + } + else { + float o = round(v * 127 / max + 128); + int d = Clamp((int)o, 0, 255); + out[y * w + x] = d; + } + } + } + + // Log::Printf("Max/Min SDF value: %d/%d\n", max, min); +} \ No newline at end of file diff --git a/src/SDF.h b/src/SDF.h new file mode 100644 index 00000000..25a47987 --- /dev/null +++ b/src/SDF.h @@ -0,0 +1,6 @@ +#pragma once + +// Algorithm to convert shape into SDF. +// Input: UNSIGNED BYTE texture (W * H) +// Output: float GL_ALPHA texture (w * h size) +void ConvertToSDF(unsigned char* out, unsigned char* tex, int w, int h); \ No newline at end of file diff --git a/src/SceneEnvironment.cpp b/src/SceneEnvironment.cpp index 070ad7ac..ca81e7cc 100644 --- a/src/SceneEnvironment.cpp +++ b/src/SceneEnvironment.cpp @@ -252,9 +252,9 @@ SceneEnvironment::SceneEnvironment(const char* ScreenName, bool initUI) InitializeUI(); } -TruetypeFont* SceneEnvironment::CreateTTF(const char* Dir, float Size) +TruetypeFont* SceneEnvironment::CreateTTF(const char* Dir) { - TruetypeFont *Ret = new TruetypeFont(Dir, Size); + TruetypeFont *Ret = new TruetypeFont(Dir); ManagedFonts.push_back(Ret); return Ret; } @@ -308,7 +308,7 @@ SceneEnvironment::~SceneEnvironment() ManagedFonts.clear(); if (RocketContext) { - lua_gc(Rocket::Core::Lua::Interpreter::GetLuaState(), LUA_GCCOLLECT, 0); + // lua_gc(Rocket::Core::Lua::Interpreter::GetLuaState(), LUA_GCCOLLECT, 0); /*if (Doc && Doc->GetReferenceCount()) { Doc->RemoveReference(); diff --git a/src/SceneEnvironment.h b/src/SceneEnvironment.h index 6c0ca162..e91799e7 100644 --- a/src/SceneEnvironment.h +++ b/src/SceneEnvironment.h @@ -88,7 +88,7 @@ class SceneEnvironment void RemoveTarget(Drawable2D *Targ); void DrawTargets(double TimeDelta); - TruetypeFont* CreateTTF(const char* Dir, float Size); + TruetypeFont* CreateTTF(const char* Dir); void Sort(); @@ -110,4 +110,6 @@ class SceneEnvironment void HandleScrollInput(double x_off, double y_off); }; -void DefineSpriteInterface(LuaManager* anim_lua); \ No newline at end of file +void DefineSpriteInterface(LuaManager* anim_lua); + +void AddRDLuaGlobal(LuaManager * anim_lua); diff --git a/src/ScoreKeeper7K.cpp b/src/ScoreKeeper7K.cpp index 3d6aeb45..1fca0a3c 100644 --- a/src/ScoreKeeper7K.cpp +++ b/src/ScoreKeeper7K.cpp @@ -40,23 +40,34 @@ namespace Game { int ScoreKeeper::getTotalNotes() const { return total_notes; } + + float ScoreKeeper::getHitStDev() const + { + return sqrt(hit_variance / (total_notes - 1)); + } + // ms is misleading- since it may very well be beats, but it's fine. ScoreKeeperJudgment ScoreKeeper::hitNote(double ms) { // hit notes - avg_hit *= total_notes; + // online variance and average hit ++total_notes; - avg_hit += ms; - avg_hit /= total_notes; + float delta = ms - avg_hit; + avg_hit += delta / total_notes; + + hit_variance += delta * (ms - avg_hit); + + // std::cerr << use_bbased << " " << ms << " "; - if (use_bbased) + if (use_o2jam) { - if (abs(ms * 150) < 128) + auto dist = ms / O2_WINDOW * 128; + if (abs(dist) < 128) { - ++histogram[static_cast(round(ms * 150)) + 127]; + ++histogram[static_cast(round(dist)) + 127]; } } else @@ -70,29 +81,33 @@ namespace Game { ms = abs(ms); // combo - - double jt; - if (use_bbased) - jt = judgment_time[SKJ_W2]; - else - jt = judgment_time[SKJ_W3]; - - if (ms <= jt) - { + auto increase_combo = [&]() { ++notes_hit; ++combo; if (combo > max_combo) max_combo = combo; - } - else + }; + + // check combo for o2jam later (after transformed judgment) + double combo_leniency; + if (!use_o2jam) { - combo = 0; + combo_leniency = judgment_time[SKJ_W3]; // GOODs/GREATs or less + + if (ms <= combo_leniency) + { + increase_combo(); + } + else + { + combo = 0; + } } // accuracy score - if (use_bbased) - total_sqdev += ms * ms * 22500; + if (use_o2jam) + total_sqdev += ms * ms / pow(O2_WINDOW, 2); else total_sqdev += ms * ms; @@ -102,13 +117,13 @@ namespace Game { ScoreKeeperJudgment judgment = SKJ_NONE; - for (int i = (use_w0 ? 0 : 1); i < (use_bbased ? 4 : 6); i++) + for (int i = (use_w0 ? 0 : 1); i < (use_o2jam ? 4 : 6); i++) { if (ms <= judgment_time[i]) { judgment = ScoreKeeperJudgment(i); - if (!use_bbased) + if (!use_o2jam) judgment_amt[judgment]++; else { @@ -125,6 +140,16 @@ namespace Game { } } + // since it may be transformed we check for combo here instead + if (use_o2jam) { + if (judgment < SKJ_W3) { + increase_combo(); + } + else { + combo = 0; + } + } + // SC, ACC^2 score sc_score += Clamp(accuracy_percent(ms * ms) / 100, 0.0, 1.0) * 2; @@ -134,6 +159,27 @@ namespace Game { // lifebars + lifebarHit(ms, judgment); + + if (judgment == SKJ_NONE) + std::cerr << "Error, invalid judgment: " << ms << "\n"; + + // std::cerr << std::endl; + + // Other methods + + update_ranks(judgment); // rank calculation + update_bms(judgment); // Beatmania scoring + update_lr2(judgment); // Lunatic Rave 2 scoring + update_exp2(judgment); + update_osu(judgment); + update_o2(judgment); + + return judgment; + } + + void ScoreKeeper::lifebarHit(double ms, Game::VSRG::ScoreKeeperJudgment judgment) + { if (ms <= judgment_time[SKJ_W3]) { lifebar_easy = std::min(1.0, lifebar_easy + lifebar_easy_increment); @@ -165,22 +211,6 @@ namespace Game { // std::cerr << ms << " " << judgment << " " << life_increment[judgment] << std::endl; lifebar_stepmania = std::min(1.0, lifebar_stepmania + life_increment[judgment]); - - if (judgment == SKJ_NONE) - std::cerr << "Error, invalid judgment: " << ms << "\n"; - - // std::cerr << std::endl; - - // Other methods - - update_ranks(judgment); // rank calculation - update_bms(judgment); // Beatmania scoring - update_lr2(judgment); // Lunatic Rave 2 scoring - update_exp2(judgment); - update_osu(judgment); - update_o2(judgment); - - return judgment; } int ScoreKeeper::getJudgmentCount(int judgment) @@ -242,14 +272,11 @@ namespace Game { double ScoreKeeper::getJudgmentCutoff() { auto rt = 0.0; - for (int i = 0; i <= SKJ_MISS; i++) - rt = std::max(judgment_time[i], rt); - rt = std::max(std::max(miss_threshold, rt), earlymiss_threshold); return rt; } - double ScoreKeeper::getEarlyMissCutoff() const + double ScoreKeeper::getEarlyMissCutoffMS() const { return earlymiss_threshold; } @@ -492,6 +519,9 @@ namespace Game { return rank_pts - (total_notes * 260 / 100 + (total_notes * 260 % 100 != 0)); case PMT_RANK_P9: return rank_pts - (total_notes * 280 / 100 + (total_notes * 280 % 100 != 0)); + + default: + break; } return 0; @@ -543,17 +573,17 @@ namespace Game { void ScoreKeeper::update_bms(ScoreKeeperJudgment judgment) { - if (!use_w0_for_ex2 && judgment <= SKJ_W1 || use_w0_for_ex2 && judgment == SKJ_W0) + if ((!use_w0_for_ex2 && judgment <= SKJ_W1) || (use_w0_for_ex2 && judgment == SKJ_W0)) { ex_score += 2; bms_dance_pts += 15; } - else if (!use_w0_for_ex2 && judgment == SKJ_W2 || use_w0_for_ex2 && judgment == SKJ_W1) + else if ((!use_w0_for_ex2 && judgment == SKJ_W2) || (use_w0_for_ex2 && judgment == SKJ_W1)) { ex_score += 1; bms_dance_pts += 10; } - else if (!use_w0_for_ex2 && judgment == SKJ_W3 || use_w0_for_ex2 && judgment == SKJ_W2) + else if ((!use_w0_for_ex2 && judgment == SKJ_W3) || (use_w0_for_ex2 && judgment == SKJ_W2)) { bms_dance_pts += 2; } @@ -572,15 +602,15 @@ namespace Game { void ScoreKeeper::update_lr2(ScoreKeeperJudgment judgment) { - if (!use_w0_for_ex2 && judgment <= SKJ_W1 || use_w0_for_ex2 && judgment == SKJ_W0) + if ((!use_w0_for_ex2 && judgment <= SKJ_W1) || (use_w0_for_ex2 && judgment == SKJ_W0)) { lr2_dance_pts += 10; } - else if (!use_w0_for_ex2 && judgment == SKJ_W2 || use_w0_for_ex2 && judgment == SKJ_W1) + else if ((!use_w0_for_ex2 && judgment == SKJ_W2) || (use_w0_for_ex2 && judgment == SKJ_W1)) { lr2_dance_pts += 5; } - else if (!use_w0_for_ex2 && judgment == SKJ_W3 || use_w0_for_ex2 && judgment == SKJ_W2) + else if ((!use_w0_for_ex2 && judgment == SKJ_W3) || (use_w0_for_ex2 && judgment == SKJ_W2)) { lr2_dance_pts += 1; } @@ -644,17 +674,17 @@ namespace Game { void ScoreKeeper::update_exp2(ScoreKeeperJudgment judgment) { - if (!use_w0_for_ex2 && judgment <= SKJ_W1 || use_w0_for_ex2 && judgment == SKJ_W0) + if ((!use_w0_for_ex2 && judgment <= SKJ_W1) || (use_w0_for_ex2 && judgment == SKJ_W0)) { exp_combo += 4; exp_hit_score += 2; } - else if (!use_w0_for_ex2 && judgment == SKJ_W2 || use_w0_for_ex2 && judgment == SKJ_W1) + else if ((!use_w0_for_ex2 && judgment == SKJ_W2) || (use_w0_for_ex2 && judgment == SKJ_W1)) { exp_combo += 3; exp_hit_score += 2; } - else if (!use_w0_for_ex2 && judgment == SKJ_W3 || use_w0_for_ex2 && judgment == SKJ_W2) + else if ((!use_w0_for_ex2 && judgment == SKJ_W3) || (use_w0_for_ex2 && judgment == SKJ_W2)) { exp_combo += 2; exp_hit_score += 1; @@ -717,6 +747,8 @@ namespace Game { osu_points += 0; bonus_counter = 0; break; + default: + break; } osu_bonus_points += osu_bonus_multiplier * sqrt(double(bonus_counter)); @@ -728,8 +760,8 @@ namespace Game { { if (!use_w0_for_ex2 && judgment <= SKJ_W0) ++rank_w0_count; - if (!use_w0_for_ex2 && judgment <= SKJ_W1 || use_w0_for_ex2 && judgment <= SKJ_W0) ++rank_w1_count; - if (!use_w0_for_ex2 && judgment <= SKJ_W2 || use_w0_for_ex2 && judgment <= SKJ_W1) ++rank_w2_count; + if ((!use_w0_for_ex2 && judgment <= SKJ_W1) || (use_w0_for_ex2 && judgment <= SKJ_W0)) ++rank_w1_count; + if ((!use_w0_for_ex2 && judgment <= SKJ_W2) || (use_w0_for_ex2 && judgment <= SKJ_W1)) ++rank_w2_count; if (judgment <= SKJ_W3) ++rank_w3_count; long long rank_w0_pts = std::max(rank_w0_count * 2 - total_notes, 0LL); diff --git a/src/ScoreKeeper7K.h b/src/ScoreKeeper7K.h index 8a5d5af2..50dd75c3 100644 --- a/src/ScoreKeeper7K.h +++ b/src/ScoreKeeper7K.h @@ -14,7 +14,10 @@ namespace Game { void init(); void setMaxNotes(int notes); - void setLifeTotal(double total); + + // total if multiplier is nan, else default rate * multiplier + void setLifeTotal(double total, double multiplier = NAN); + void setLifeIncrements(double* increments, int inc_n); void setMissDecrement(double decrement); void setEarlyMissDecrement(double decrement); @@ -43,13 +46,14 @@ namespace Game { double getAvgHit() const; ScoreKeeperJudgment hitNote(double ms); + void lifebarHit(double ms, Game::VSRG::ScoreKeeperJudgment judgment); void missNote(bool auto_hold_miss, bool early_miss); double getAccMax() const; double getJudgmentWindow(int judgment); double getMissCutoffMS() const; - double getEarlyMissCutoff() const; + double getEarlyMissCutoffMS() const; double getJudgmentCutoff(); int getScore(int score_type); @@ -74,23 +78,27 @@ namespace Game { uint8_t getPills() const; int getCoolCombo() const; - void set_manual_w0(bool); + void setUseW0(bool); bool usesW0() const; bool usesO2() const; + float getHitStDev() const; + void reset(); private: - void set_beat_timing_windows(); + void setO2JamBeatTimingWindows(); bool use_w0; // whether or not to use ridiculous timing. bool use_w0_for_ex2; // whether or not to require ridiculous for 2 EX score. + // online avg hit and variance double avg_hit; + double hit_variance; // o2jam-specific variable - bool use_bbased; + bool use_o2jam; /* Standard scoring. @@ -239,7 +247,7 @@ namespace Game { double life_increment[9]; - void set_timing_windows(); + void setBMSTimingWindows(); // miss thresholds; notes hit outside here count as misses. // units are in ms @@ -262,4 +270,7 @@ namespace Game { void SetupScorekeeperLuaInterface(void* state); void SetScorekeeperInstance(void* state, ScoreKeeper *Instance); } + + + const double O2_WINDOW = 0.664; } \ No newline at end of file diff --git a/src/ScoreKeeper7K_Lua.cpp b/src/ScoreKeeper7K_Lua.cpp index 64a44b11..35f6a322 100644 --- a/src/ScoreKeeper7K_Lua.cpp +++ b/src/ScoreKeeper7K_Lua.cpp @@ -16,7 +16,7 @@ namespace Game { luabridge::getGlobalNamespace(L) .beginClass ("ScoreKeeper") .addProperty("MaxAccuracy", &ScoreKeeper::getAccMax) - .addProperty("EarlyMissCutoffMS", &ScoreKeeper::getEarlyMissCutoff) + .addProperty("EarlyMissCutoffMS", &ScoreKeeper::getEarlyMissCutoffMS) .addProperty("UsesW0", &ScoreKeeper::usesW0) .addProperty("MissCutoffMS", &ScoreKeeper::getMissCutoffMS) .addProperty("Rank", &ScoreKeeper::getRank) @@ -28,7 +28,9 @@ namespace Game { .addProperty("HistogramPointCount", &ScoreKeeper::getHistogramPointCount) .addProperty("HistogramHighestPoint", &ScoreKeeper::getHistogramHighestPoint) .addProperty("AvgHit", &ScoreKeeper::getAvgHit) + .addProperty("StDev", &ScoreKeeper::getHitStDev) .addProperty("MaxNotes", &ScoreKeeper::getMaxNotes) + .addFunction("GetJudgmentWindow", &ScoreKeeper::getJudgmentWindow) .addFunction("GetHistogramPoint", &ScoreKeeper::getHistogramPoint) .addFunction("GetJudgmentCount", &ScoreKeeper::getJudgmentCount) .addFunction("GetScore", &ScoreKeeper::getScore) @@ -38,54 +40,54 @@ namespace Game { .addFunction("GetLifebarAmount", &ScoreKeeper::getLifebarAmount) .endClass(); -#define cns(x) lua_pushinteger(L,x); lua_setglobal(L, #x); - cns(SKJ_MISS); - cns(SKJ_W0); - cns(SKJ_W1); - cns(SKJ_W2); - cns(SKJ_W3); - cns(SKJ_W4); - cns(SKJ_W5); - cns(ST_SCORE); - cns(ST_COMBO); - cns(ST_DP); - cns(ST_EX); - cns(ST_EXP); - cns(ST_EXP3); - cns(ST_IIDX); - cns(ST_JB2); - cns(ST_LR2); - cns(ST_MAX_COMBO); - cns(ST_NOTES_HIT); - cns(ST_OSUMANIA); - cns(ST_O2JAM); - cns(PST_RANK); - cns(PST_ACC); - cns(PST_EX); - cns(PST_NH); - cns(PST_OSU); - cns(LT_GROOVE); - cns(LT_SURVIVAL); - cns(LT_EXHARD); - cns(LT_DEATH); - cns(LT_EASY); - cns(LT_O2JAM); - cns(LT_STEPMANIA); - cns(LT_NORECOV); - cns(LT_BATTERY); - cns(PMT_AAA); - cns(PMT_AA); - cns(PMT_A); - cns(PMT_B); - cns(PMT_C); - cns(PMT_D); - cns(PMT_E); - cns(PMT_F); - cns(TI_NONE); - cns(TI_BMS); - cns(TI_OSUMANIA); - cns(TI_O2JAM); - cns(TI_STEPMANIA); +#define Constant(x) lua_pushinteger(L,x); lua_setglobal(L, #x); + Constant(SKJ_MISS); + Constant(SKJ_W0); + Constant(SKJ_W1); + Constant(SKJ_W2); + Constant(SKJ_W3); + Constant(SKJ_W4); + Constant(SKJ_W5); + Constant(ST_SCORE); + Constant(ST_COMBO); + Constant(ST_DP); + Constant(ST_EX); + Constant(ST_EXP); + Constant(ST_EXP3); + Constant(ST_IIDX); + Constant(ST_JB2); + Constant(ST_LR2); + Constant(ST_MAX_COMBO); + Constant(ST_NOTES_HIT); + Constant(ST_OSUMANIA); + Constant(ST_O2JAM); + Constant(PST_RANK); + Constant(PST_ACC); + Constant(PST_EX); + Constant(PST_NH); + Constant(PST_OSU); + Constant(LT_GROOVE); + Constant(LT_SURVIVAL); + Constant(LT_EXHARD); + Constant(LT_DEATH); + Constant(LT_EASY); + Constant(LT_O2JAM); + Constant(LT_STEPMANIA); + Constant(LT_NORECOV); + Constant(LT_BATTERY); + Constant(PMT_AAA); + Constant(PMT_AA); + Constant(PMT_A); + Constant(PMT_B); + Constant(PMT_C); + Constant(PMT_D); + Constant(PMT_E); + Constant(PMT_F); + Constant(TI_NONE); + Constant(TI_BMS); + Constant(TI_OSUMANIA); + Constant(TI_O2JAM); + Constant(TI_STEPMANIA); } } } diff --git a/src/ScoreKeeper7K_init.cpp b/src/ScoreKeeper7K_init.cpp index 69986afa..42951099 100644 --- a/src/ScoreKeeper7K_init.cpp +++ b/src/ScoreKeeper7K_init.cpp @@ -11,8 +11,9 @@ namespace Game { use_w0_for_ex2 = false; rank_pts = 0; - use_bbased = false; + use_o2jam = false; avg_hit = 0; + hit_variance = 0; pacemaker_texts[PMT_F] = "F"; pacemaker_texts[PMT_E] = "E"; @@ -103,20 +104,21 @@ namespace Game { setMissDecrement(0.08); setEarlyMissDecrement(0.02); + memset(judgment_time, 0, sizeof(judgment_time)); judge_window_scale = 1.00; - set_timing_windows(); + setBMSTimingWindows(); } - void ScoreKeeper::set_beat_timing_windows() + void ScoreKeeper::setO2JamBeatTimingWindows() { // This in beats. double o2jamTimingAmt[] = { - 0.664 * 0.1, // raindrop!o2jam extension: XCOOL - 0.664 * 0.2, // COOL threshold - 0.664 * 0.5, // GOOD threshold - 0.664 * 0.8, // BAD threshold - 0.664 // MISS threshold + O2_WINDOW * 0.1, // raindrop!o2jam extension: XCOOL + O2_WINDOW * 0.2, // COOL threshold + O2_WINDOW * 0.5, // GOOD threshold + O2_WINDOW * 0.8, // BAD threshold + O2_WINDOW // MISS threshold }; judgment_time[SKJ_W0] = o2jamTimingAmt[0]; @@ -128,17 +130,18 @@ namespace Game { earlymiss_threshold = miss_threshold = o2jamTimingAmt[4]; } - void ScoreKeeper::set_timing_windows() + void ScoreKeeper::setBMSTimingWindows() { - double JudgmentValues[] = { 6.4, 16, 40, 100, 250, 625 }; + // double JudgmentValues[] = { 6.4, 16, 40, 100, 250, -1, 625 }; + double JudgmentValues[] = { 8.8, 22, 55, 137.5, 250, -1, 625 }; miss_threshold = 250; - earlymiss_threshold = 1250; + earlymiss_threshold = 625; for (auto i = 0; i < sizeof(JudgmentValues) / sizeof(double); i++) judgment_time[i] = JudgmentValues[i] * judge_window_scale; - for (auto i = 0; i < 9; i++) + for (auto i = 0; i < sizeof(judgment_amt) / sizeof(double); i++) judgment_amt[i] = 0; for (auto i = -127; i < 128; ++i) @@ -149,16 +152,28 @@ namespace Game { { use_w0 = true; // if chart has OD, use osu!mania scoring. use_w0_for_ex2 = true; - - double JudgmentValues[] = { 16, 34, 67, 97, 121, 158 }; + + /* + double JudgmentValues[] = { 16, 34, 67, 97, 121, -1, 158 }; miss_threshold = 121 + (10 - od) * 3; - earlymiss_threshold = 158 + (10 - od) * 3; + // earlymiss_threshold = 158 + (10 - od) * 3; judgment_time[SKJ_W0] = JudgmentValues[SKJ_W0]; for (int i = 1; i < sizeof(JudgmentValues) / sizeof(double); i++) judgment_time[i] = JudgmentValues[i] + (10 - od) * 3; + */ + + double JudgmentValues[] = { 16, 64, 97, 127, 151, -1, 188 }; + + miss_threshold = 188 - od * 3; + earlymiss_threshold = miss_threshold; + + judgment_time[SKJ_W0] = JudgmentValues[SKJ_W0]; + for (int i = 1; i < sizeof(JudgmentValues) / sizeof(double); i++) + judgment_time[i] = JudgmentValues[i] - od * 3; + for (int i = 0; i < 9; i++) judgment_amt[i] = 0; @@ -170,15 +185,15 @@ namespace Game { { // Ridiculous is included: J7 Marvelous. // No early miss threshold - double JudgmentValues[] = { 11.25, 22.5, 45, 90, 135, 135 }; + double JudgmentValues[] = { 11.25, 22.5, 45, 90, 135, 180, 180 }; - miss_threshold = 135; - earlymiss_threshold = 135; + miss_threshold = 180; + earlymiss_threshold = 180; for (int i = 0; i < sizeof(JudgmentValues) / sizeof(double); i++) judgment_time[i] = JudgmentValues[i]; } - void ScoreKeeper::set_manual_w0(bool on) { use_w0 = on; } // make a config option + void ScoreKeeper::setUseW0(bool on) { use_w0 = on; } // make a config option ScoreKeeper::ScoreKeeper() { @@ -189,13 +204,15 @@ namespace Game { { init(); this->judge_window_scale = judge_window_scale; - set_timing_windows(); + setBMSTimingWindows(); } - void ScoreKeeper::setLifeTotal(double total) + void ScoreKeeper::setLifeTotal(double total, double multiplier) { - if (total != -1) lifebar_total = total; - else lifebar_total = std::max(260.0, 7.605 * max_notes / (6.5 + 0.01 * max_notes)); + double effmul = isnan(multiplier) ? 1 : multiplier; + + if (total != -1 && isnan(multiplier) && !isnan(total)) lifebar_total = total; + else lifebar_total = std::max(260.0, 7.605 * max_notes / (6.5 + 0.01 * max_notes)) * effmul; // recalculate groove lifebar increments. lifebar_easy_increment = Clamp(lifebar_total / max_notes / 50.0, 0.004, 0.8); @@ -231,32 +248,34 @@ namespace Game { if (rank == -100) // We assume we're dealing with beats-based timing. { - use_bbased = true; + use_o2jam = true; use_w0 = false; - set_beat_timing_windows(); + setO2JamBeatTimingWindows(); return; } - use_bbased = false; + use_o2jam = false; + + // old values: 0.5, 0.75, 1.0, 1.5, 2.0 switch (rank) { case 0: - judge_window_scale = 0.50; break; + judge_window_scale = 8.0 / 11.0; break; case 1: - judge_window_scale = 0.75; break; + judge_window_scale = 9.0 / 11.0; break; case 2: judge_window_scale = 1.00; break; case 3: - judge_window_scale = 1.50; break; + judge_window_scale = 13.0 / 11.0; break; case 4: - judge_window_scale = 2.00; break; + judge_window_scale = 16.0 / 11.0; break; } - set_timing_windows(); + setBMSTimingWindows(); } void ScoreKeeper::setJudgeScale(double scale) { judge_window_scale = scale; - set_timing_windows(); + setBMSTimingWindows(); } } } \ No newline at end of file diff --git a/src/ScoreKeeper7K_o2jam.cpp b/src/ScoreKeeper7K_o2jam.cpp index d2d8d933..f8b4024e 100644 --- a/src/ScoreKeeper7K_o2jam.cpp +++ b/src/ScoreKeeper7K_o2jam.cpp @@ -100,7 +100,7 @@ namespace Game { bool ScoreKeeper::usesO2() const { - return use_bbased; + return use_o2jam; } int ScoreKeeper::getCoolCombo() const diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp deleted file mode 100644 index 5d1e052c..00000000 --- a/src/ScreenEdit.cpp +++ /dev/null @@ -1,463 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "GameState.h" -#include "ScreenEdit.h" -#include "GameWindow.h" -#include "ImageLoader.h" -#include "Audio.h" - -SoundSample *SavedSound = NULL; - -double Fracs[] = { - 1, - 2, - 3, - 4, - 6, - 8, - 12, - 16, - 24, - 32, - 48, - 64, - 96, - 192 -}; - -namespace Game { - namespace dotcur { - - ScreenEdit::ScreenEdit() - : ScreenGameplay() - { - ShouldChangeScreenAtEnd = false; // So it doesn't go into screen evaluation. - CurrentFraction = 0; - Measure = 0; - EditScreenState = Editing; - - GhostObject.SetImage(GameState::GetInstance().GetSkinImage("hitcircle.png")); - GhostObject.Alpha = 0.7f; - GhostObject.Centered = true; - GhostObject.SetSize(CircleSize); - - EditInfo.LoadSkinFontImage("font.tga", Vec2(6, 15), Vec2(8, 16), Vec2(6, 15), 0); - EditMode = true; - HeldObject = NULL; - Mode = Select; - GridEnabled = false; - GridCellSize = 16; - - CurrentTotalFraction = 0; - } - - void ScreenEdit::Cleanup() - { - ScreenGameplay::Cleanup(); - } - - void ScreenEdit::Init(dotcur::Song *Other) - { - if (Other != NULL) - { - if (Other->Difficulties.size() == 0) // No difficulties? Create a new one. - Other->Difficulties.push_back(new dotcur::Difficulty()); - - ScreenGameplay::Init(Other, 0); - NotesInMeasure.clear(); - - if (!Other->Difficulties[0]->Timing.size()) - Other->Difficulties[0]->Timing.push_back(TimingSegment()); - } - - if (!SavedSound) - { - SavedSound = new SoundSample(); - SavedSound->Open((GameState::GetInstance().GetSkinFile("save.ogg")).c_str()); - } - - OffsetPrompt.SetPrompt("Please insert offset."); - BPMPrompt.SetPrompt("Please insert BPM."); - OffsetPrompt.SetFont(&EditInfo); - BPMPrompt.SetFont(&EditInfo); - - OffsetPrompt.SetOpen(false); - BPMPrompt.SetOpen(false); - } - - void ScreenEdit::StartPlaying(int32_t _Measure) - { - ScreenGameplay::Init(MySong, 0); - ScreenGameplay::ResetNotes(); - ScreenGameplay::MainThreadInitialization(); - - Measure = _Measure; - seekTime(TimeAtBeat(CurrentDiff->Timing, CurrentDiff->Offset, Measure * MySong->MeasureLength)); - savedMeasure = Measure; - } - - void ScreenEdit::SwitchPreviewMode() - { - MySong->Process(false); - if (EditScreenState == Editing) // if we're editing, start playing the song - { - EditScreenState = Playing; - StartPlaying(Measure); - } - else if (EditScreenState == Playing) // if we're playing, go back to editing - { - EditScreenState = Editing; - Measure = savedMeasure; - stopMusic(); - RemoveTrash(); - } - } - - void ScreenEdit::DecreaseCurrentFraction() - { - if (HeldObject) - { - HeldObject->hold_duration -= 4.0 / Fracs[CurrentTotalFraction]; - if (HeldObject->hold_duration < 0) - HeldObject->hold_duration = 0; - MySong->Process(false); - } - - if (CurrentFraction || Measure > 0) - { - CurrentFraction--; - - if (CurrentFraction / Fracs[CurrentTotalFraction] > 1) // overflow - { - CurrentFraction = Fracs[CurrentTotalFraction] - 1; - - if (Measure > 0) // Go back a measure - Measure--; - } - } - } - - void ScreenEdit::IncreaseCurrentFraction() - { - if (!CurrentDiff->Measures.size()) - return; - - if (HeldObject) - { - HeldObject->hold_duration += 4.0 / Fracs[CurrentTotalFraction]; - MySong->Process(false); - } - - CurrentFraction++; - - if (CurrentFraction / Fracs[CurrentTotalFraction] >= 1) - { - CurrentFraction = 0; - if (Measure + 1 < CurrentDiff->Measures.size()) // Advance a measure - Measure++; - } - } - - void ScreenEdit::SaveChart() - { - std::filesystem::path DefaultPath = MySong->ChartFilename.string().length() ? MySong->ChartFilename : MySong->SongDirectory / "chart.dcf"; - MySong->Repack(); - MySong->Save(DefaultPath.string().c_str()); - SavedSound->Play(); - MySong->Process(); - } - - void ScreenEdit::InsertMeasure() - { - CurrentDiff->Measures.resize(CurrentDiff->Measures.size() + 1); - Measure = CurrentDiff->Measures.size() - 1; - CurrentFraction = 0; - } - - void ScreenEdit::OnMousePress(KeyType tkey) - { - if (Mode != Select) - { - GameObject &G = GetObject(); - if (tkey == KT_Select) - { - uint32_t selFrac = CurrentFraction / Fracs[CurrentTotalFraction] * 192; - - G.SetPositionX(GhostObject.GetPosition().x); - G.Fraction = (double)CurrentFraction / Fracs[CurrentTotalFraction]; - - if (Mode == Hold) - { - HeldObject = &G; - HeldObject->endTime = 0; - HeldObject->hold_duration = 0; - } - } - else - { - HeldObject = NULL; - G.SetPositionX(0); - G.endTime = 0; - G.hold_duration = 0; - } - - MySong->Process(false); - ScreenGameplay::ResetNotes(); - } - } - - void ScreenEdit::OnMouseRelease(KeyType tkey) - { - if (Mode == Hold && tkey == KT_Select) - HeldObject = NULL; - } - - bool ScreenEdit::HandleInput(int32_t key, KeyEventType code, bool isMouseInput) - { - KeyType tkey = BindingsManager::TranslateKey(key); - - if (!isMouseInput) - if (key == 'P') // pressed p? - { - SwitchPreviewMode(); - return true; - } - - // Playing mode input - if (EditScreenState == Playing) - { - ScreenGameplay::HandleInput(key, code, isMouseInput); - return true; - } - - // Editor mode input - if (!isMouseInput) - { - if (key == 'P') // pressed P - SwitchPreviewMode(); - - int R = OffsetPrompt.HandleInput(key, code, isMouseInput); - - if (R == 1) - return true; - else if (R == 2) - { - if (Utility::IsNumeric(OffsetPrompt.GetContents().c_str())) - { - CurrentDiff->Offset = atof(OffsetPrompt.GetContents().c_str()); - } - return true; - } - - R = BPMPrompt.HandleInput(key, code, isMouseInput); - - if (R == 1) - return true; - else if (R == 2) - { - if (Utility::IsNumeric(BPMPrompt.GetContents().c_str())) - { - CurrentDiff->Timing[0].Value = atof(BPMPrompt.GetContents().c_str()); - MySong->Process(false); - } - return true; - } - - if (code == KE_PRESS) - { - switch (tkey) - { - case KT_Right: IncreaseCurrentFraction(); return true; - case KT_Left: DecreaseCurrentFraction(); return true; - case KT_Escape: Running = false; return true; - case KT_FractionDec: if (CurrentDiff->Measures.size()) DecreaseTotalFraction(); return true; - case KT_FractionInc: if (CurrentDiff->Measures.size()) IncreaseTotalFraction(); return true; - case KT_GridDec: GridCellSize--; return true; - case KT_GridInc: GridCellSize++; return true; - case KT_SwitchOffsetPrompt: OffsetPrompt.SwitchOpen(); return true; - case KT_SwitchBPMPrompt: BPMPrompt.SwitchOpen(); return true; - } - - switch (key) - { - case 'S': SaveChart(); return true; - case 'M': CurrentDiff->Measures[Measure].clear(); return true; - case 'Q': - if (Mode == Select) - Mode = Normal; - else if (Mode == Normal) - Mode = Hold; - else - Mode = Select; - return true; - case 'T': - InsertMeasure(); - return true; - case 'X': - if (Measure + 1 < CurrentDiff->Measures.size()) - { - Measure++; - CurrentFraction = 0; - } - return true; - case 'Z': - if (Measure > 0) - { - Measure--; - CurrentFraction = 0; - } - return true; - case 'G': GridEnabled = !GridEnabled; return true; - } - } - } - else // mouse input - { - if (EditScreenState == Editing) - { - if (code == KE_PRESS) - OnMousePress(tkey); - if (code == KE_RELEASE) - OnMouseRelease(tkey); - } - } - - return false; - } - - void ScreenEdit::RunGhostObject() - { - GhostObject.SetPositionY(YLock); - - if (!GridEnabled) - { - GhostObject.SetPositionX(WindowFrame.GetRelativeMPos().x); - } - else - { - auto CellSize = ScreenWidth / GridCellSize; - auto Mod = (int)(WindowFrame.GetRelativeMPos().x - ScreenDifference) % CellSize; - GhostObject.SetPositionX((int)WindowFrame.GetRelativeMPos().x - Mod); - } - - if ((GhostObject.GetPosition().x - ScreenDifference) > PlayfieldWidth) - GhostObject.SetPositionX(PlayfieldWidth + ScreenDifference); - if ((GhostObject.GetPosition().x - ScreenDifference) < 0) - GhostObject.SetPositionX(ScreenDifference); - - if (Mode != Select) - GhostObject.Render(); - } - - void ScreenEdit::DrawInformation() - { - std::stringstream info; - info << "Beat: " << (float)Measure * MySong->MeasureLength + MySong->MeasureLength * ((float)CurrentFraction) / Fracs[CurrentTotalFraction]; - if (CurrentDiff->Measures.size()) - info << "\nMeasureFraction: " << Fracs[CurrentTotalFraction]; - info << "\nMode: "; - if (Mode == Normal) - info << "Normal"; - else if (Mode == Hold) - info << "Hold"; - else - info << "Null"; - if (GridEnabled) - info << "\nGrid Enabled (size " << ScreenWidth / GridCellSize << ")"; - EditInfo.Render(info.str(), Vec2(512, 600)); - } - - void ScreenEdit::CalculateVerticalLock() - { - if (CurrentDiff->Measures.size()) - { - float frac = ((float)CurrentFraction / Fracs[CurrentTotalFraction]); - if (!(Measure % 2)) - YLock = frac * (float)PlayfieldHeight; - else - YLock = PlayfieldHeight - frac * (float)PlayfieldHeight; - - YLock += ScreenOffset; - } - } - - bool ScreenEdit::Run(double delta) - { - // we're playing the song? run the game - if (EditScreenState == Playing) - { - if (!Music->IsPlaying()) - startMusic(); - ScreenGameplay::Run(delta); - } - else // editing the song? run the editor - { - CalculateVerticalLock(); - RenderObjects(delta, false); // No need to draw playable elements since that's what WE do! - - if (CurrentDiff->Measures.size()) - { - double Ratio = ((float)CurrentFraction / Fracs[CurrentTotalFraction]);; - Barline.Run(delta, Ratio); - if (Measure > 0) - DrawVector(CurrentDiff->Measures.at(Measure - 1), delta); - DrawVector(CurrentDiff->Measures[Measure], delta); - } - - RunGhostObject(); - DrawInformation(); - OffsetPrompt.Render(); - BPMPrompt.Render(); - } - - return Running; - } - - void ScreenEdit::IncreaseTotalFraction() - { - double Frac = (CurrentFraction / Fracs[CurrentTotalFraction]); - int ClosestFrac; - - CurrentTotalFraction += 1; - - if (CurrentTotalFraction >= sizeof(Fracs) / sizeof(double)) - CurrentTotalFraction -= 1; - - ClosestFrac = Frac * Fracs[CurrentTotalFraction]; - - // Normalize. - CurrentFraction = ClosestFrac; - } - - void ScreenEdit::DecreaseTotalFraction() - { - double Frac = (CurrentFraction / Fracs[CurrentTotalFraction]); - int ClosestFrac; - - CurrentTotalFraction -= 1; - - // Don't forget it's unsigned, and as such it could overflow instead. - if (CurrentTotalFraction >= sizeof(Fracs) / sizeof(double)) - CurrentTotalFraction = 0; - - ClosestFrac = Frac * Fracs[CurrentTotalFraction]; - - // Normalize. - CurrentFraction = ClosestFrac; - } - - GameObject &ScreenEdit::GetObject() - { - for (std::vector::iterator i = CurrentDiff->Measures.at(Measure).begin(); - i != CurrentDiff->Measures.at(Measure).end(); - i++) - { - if (i->GetFraction() * 192 == (CurrentFraction / Fracs[CurrentTotalFraction]) * 192) - return *i; - } - CurrentDiff->Measures.at(Measure).push_back(GameObject()); - return CurrentDiff->Measures.at(Measure).back(); - } - } -} \ No newline at end of file diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h deleted file mode 100644 index 87e5aaa2..00000000 --- a/src/ScreenEdit.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include "ScreenGameplay.h" -#include "GuiTextPrompt.h" - -namespace Game { - namespace dotcur { - class ScreenEdit : public ScreenGameplay - { - enum - { - Playing, - Editing - }EditScreenState; - - GUI::TextPrompt OffsetPrompt, BPMPrompt; - uint32_t CurrentFraction; - uint32_t CurrentTotalFraction; // basically beat snap - uint32_t savedMeasure; - BitmapFont EditInfo; - - GameObject* HeldObject; - - void IncreaseTotalFraction(); - void DecreaseTotalFraction(); - - GameObject &GetObject(); - - float YLock; - enum - { - Select, - Normal, - Hold - }Mode; - Sprite GhostObject; - - bool GridEnabled; - int32_t GridCellSize; // ScreenSize / GridCellSize - // float GridOffset; - - void DecreaseCurrentFraction(); - void IncreaseCurrentFraction(); - void SaveChart(); - void SwitchPreviewMode(); - void InsertMeasure(); - - void OnMousePress(KeyType tkey); - void OnMouseRelease(KeyType tkey); - - void CalculateVerticalLock(); - void RunGhostObject(); - void DrawInformation(); - public: - ScreenEdit(); - void Init(dotcur::Song *Other); - void StartPlaying(int32_t _Measure); - bool HandleInput(int32_t key, KeyEventType code, bool isMouseInput) override; - bool Run(double Delta) override; - void Cleanup() override; - }; - } -} \ No newline at end of file diff --git a/src/ScreenEvaluation.cpp b/src/ScreenEvaluation.cpp deleted file mode 100644 index 726aa3c9..00000000 --- a/src/ScreenEvaluation.cpp +++ /dev/null @@ -1,112 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "GameState.h" -#include "Sprite.h" -#include "BitmapFont.h" -#include "ScreenEvaluation.h" -#include "GameWindow.h" -#include "ImageLoader.h" -#include "Audio.h" - -AudioStream *ScreenEvaluationMusic = NULL; - -namespace Game { - namespace dotcur { - ScreenEvaluation::ScreenEvaluation() : - Screen("ScreenEvaluation", nullptr) - { - Running = true; - Font = NULL; - } - - int32_t ScreenEvaluation::CalculateScore() - { - return int32_t(1000000.0 * Results.dpScoreSquare / (double)(Results.totalNotes * (Results.totalNotes + 1))); - } - - void ScreenEvaluation::Init(EvaluationData _Data, std::string SongAuthor, std::string SongTitle) - { - if (!ScreenEvaluationMusic) - { - ScreenEvaluationMusic = new SoundStream(); - ScreenEvaluationMusic->Open((GameState::GetInstance().GetSkinFile("screenevaluationloop.ogg")).c_str()); - ScreenEvaluationMusic->SetLoop(true); - } - - ScreenEvaluationMusic->SeekTime(0); - ScreenEvaluationMusic->Play(); - - Background.SetImage(GameState::GetInstance().GetSkinImage(Configuration::GetSkinConfigs("EvaluationBackground"))); - Background.AffectedByLightning = true; - if (!Font) - { - Font = new BitmapFont(); - Font->LoadSkinFontImage("font_screenevaluation.tga", Vec2(10, 20), Vec2(32, 32), Vec2(10, 20), 32); - Font->SetAffectedByLightning(true); - } - Results = _Data; - - char _Results[256]; - const char *Text = "J_EXCELLENT: \n" - "J_PERFECT: \n" - "J_GREAT: \n" - "Bad: \n" - "NG: \n" - "OK: \n" - "Misses: \n" - "\n" - "Max Combo: \n" - "Score: \n"; - - ResultsGString = Text; - - sprintf(_Results, "%d\n%d\n%d\n%d\n%d\n%d\n%d\n\n%d\n%d\n", - Results.NumExcellents, - Results.NumPerfects, - Results.NumGreats, - Results.NumBads, - Results.NumNG, - Results.NumOK, - Results.NumMisses, - Results.MaxCombo, - CalculateScore()); - - ResultsNumerical = _Results; - //WindowFrame.SetLightMultiplier(1); - //WindowFrame.SetLightPosition(glm::vec3(0, 0, 1)); - - TitleFormat = SongTitle + " by " + SongAuthor; - } - - bool ScreenEvaluation::HandleInput(int32_t key, KeyEventType code, bool isMouseInput) - { - if ((BindingsManager::TranslateKey(key) == KT_Escape || BindingsManager::TranslateKey(key) == KT_Select) && code == KE_PRESS) - Running = false; - - return true; - } - - void ScreenEvaluation::Cleanup() - { - ScreenEvaluationMusic->Stop(); - delete Font; - } - - bool ScreenEvaluation::Run(double Delta) - { - //WindowFrame.SetLightMultiplier(sin(GetScreenTime()) * 0.2 + 1); - - Background.Render(); - if (Font) - { - Font->Render(ResultsGString, Vec2(ScreenWidth / 2 - 110, ScreenHeight / 2 - 100)); - Font->Render(ResultsNumerical, Vec2(ScreenWidth / 2, ScreenHeight / 2 - 100)); - Font->Render(std::string("results screen"), Vec2(ScreenWidth / 2 - 70, 0)); - Font->Render(std::string("press space to continue..."), Vec2(ScreenWidth / 2 - 130, ScreenHeight * 7 / 8)); - Font->Render(TitleFormat, Vec2(0, ScreenHeight - 20)); - } - return Running; - } - } -} \ No newline at end of file diff --git a/src/ScreenEvaluation.h b/src/ScreenEvaluation.h deleted file mode 100644 index 7d670152..00000000 --- a/src/ScreenEvaluation.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "Screen.h" - -class BitmapFont; - -namespace Game { - namespace dotcur { - class ScreenEvaluation : public Screen - { - EvaluationData Results; - Sprite Background; - BitmapFont* Font; - - std::string ResultsGString, ResultsNumerical; - std::string TitleFormat; - - int32_t CalculateScore(); - public: - ScreenEvaluation(); - void Init(EvaluationData _Data, std::string SongAuthor, std::string SongTitle); - bool Run(double Delta) override; - void Cleanup() override; - bool HandleInput(int32_t key, KeyEventType code, bool isMouseInput) override; - }; - } -} \ No newline at end of file diff --git a/src/ScreenEvaluation7K.cpp b/src/ScreenEvaluation7K.cpp index f0dea2fa..a2a0c395 100644 --- a/src/ScreenEvaluation7K.cpp +++ b/src/ScreenEvaluation7K.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "GameState.h" #include "ScreenEvaluation7K.h" #include "ScreenGameplay7K.h" @@ -14,7 +14,7 @@ namespace Game { namespace VSRG { ScreenEvaluation::ScreenEvaluation() : - Screen("ScreenEvaluation7K", nullptr) + Screen("ScreenEvaluation7K", false) { Running = true; } diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp deleted file mode 100644 index 59cfabe5..00000000 --- a/src/ScreenGameplay.cpp +++ /dev/null @@ -1,722 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "GameState.h" -#include "ScreenGameplay.h" -#include "ScreenEvaluation.h" -#include "GameWindow.h" -#include "ImageLoader.h" -#include "Audio.h" - -#define ComboSizeX 24 -#define ComboSizeY 48 - - -namespace Game { - namespace dotcur { - ScreenGameplay::ScreenGameplay() : - Screen("ScreenGameplay", nullptr), - Barline(this) - { - Running = true; - IsAutoplaying = false; - ShouldChangeScreenAtEnd = true; - SongInfo.LoadSkinFontImage("font.tga", Vec2(6, 15), Vec2(8, 16), Vec2(6, 15), 0); - MyFont.LoadSkinFontImage("font-combo.tga", Vec2(8, 10), Vec2(8, 10), Vec2(32, 40), 48); - Music = NULL; - FailEnabled = true; - TappingMode = false; - EditMode = false; - IsPaused = false; - LeadInTime = 0; - } - - void ScreenGameplay::RemoveTrash() - { - NotesHeld.clear(); - AnimateOnly.clear(); - NotesInMeasure.clear(); - } - - void ScreenGameplay::Cleanup() - { - // Deleting the song's notes is ScreenSelectMusic's (or fileman's) job. - if (Music != NULL) - { - delete Music; - Music = NULL; - } - WindowFrame.SetVisibleCursor(true); - } - - Vec2 ScreenGameplay::GetScreenOffset(float Alignment) - { - float outx; - float outy; - - outx = (WindowFrame.GetMatrixSize().x*Alignment); - outy = (WindowFrame.GetMatrixSize().y*Alignment); - - return Vec2(outx, outy); - } - - void ScreenGameplay::StoreEvaluation(Judgment Eval) - { - if (Eval > Bad || Eval == OK) - Combo++; - - switch (Eval) - { - case J_EXCELLENT: - Evaluation.NumExcellents++; - Evaluation.dpScore += 2; - Evaluation.dpScoreSquare += Evaluation.dpScore; - break; - case J_PERFECT: - Evaluation.NumPerfects++; - Evaluation.dpScore += 1; - Evaluation.dpScoreSquare += Evaluation.dpScore * 0.5; - break; - case J_GREAT: - Evaluation.NumGreats++; - Evaluation.dpScoreSquare += std::max(1.0, Evaluation.dpScore) * 0.1; - break; - case Bad: - Evaluation.NumBads++; - Evaluation.dpScore = std::max(Evaluation.dpScore - 4, 0.0); - Combo = 0; - break; - case Miss: - Evaluation.NumMisses++; - Evaluation.dpScore = std::max(Evaluation.dpScore - 8, 0.0); - Combo = 0; - break; - case NG: - Evaluation.NumNG++; - Combo = 0; - break; - case OK: - Evaluation.NumOK++; - break; - case None: - break; - } - Evaluation.MaxCombo = std::max(Evaluation.MaxCombo, Combo); - } - - void ScreenGameplay::MainThreadInitialization() - { - Cursor.SetImage(GameState::GetInstance().GetSkinImage("cursor.png")); - Barline.SetImage(GameState::GetInstance().GetSkinImage("barline.png")); - MarkerA.SetImage(GameState::GetInstance().GetSkinImage("barline_marker.png")); - MarkerB.SetImage(GameState::GetInstance().GetSkinImage("barline_marker.png")); - GameplayObjectImage = GameState::GetInstance().GetSkinImage("hitcircle.png"); - - Texture* BackgroundImage = ImageLoader::Load(MySong->SongDirectory / MySong->BackgroundFilename); - - if (BackgroundImage) - Background.SetImage(BackgroundImage); - else - Background.SetImage(GameState::GetInstance().GetSkinImage(Configuration::GetSkinConfigs("DefaultGameplayBackground"))); - - Background.AffectedByLightning = true; - Background.Alpha = 0.8f; - - if (Background.GetImage()) - { - float SizeRatio = 768 / Background.GetHeight(); - Background.SetScale(SizeRatio); - Background.Centered = true; - Background.SetPosition(ScreenWidth / 2, ScreenHeight / 2); - } - - MarkerB.Centered = true; - MarkerA.Centered = true; - - MarkerA.SetPosition(GetScreenOffset(0.5).x, MarkerA.GetHeight() / 2 - Barline.GetHeight()); - MarkerB.SetPosition(GetScreenOffset(0.5).x, ScreenHeight - MarkerB.GetHeight() / 2 + Barline.GetHeight() / 2); - MarkerA.SetRotation(180); - - MarkerB.AffectedByLightning = MarkerA.AffectedByLightning = true; - - Lifebar.UpdateHealth(); - - ReadySign.SetImage(GameState::GetInstance().GetSkinImage("ready.png")); - ReadySign.SetPosition(0, ScreenHeight); - ReadySign.Centered = true; - ReadySign.AffectedByLightning = true; - - Cursor.SetSize(CursorSize); - Cursor.Centered = CursorCentered; - Cursor.AffectedByLightning = true; - - SongTime -= LeadInTime; - - // Start with lights off. - if (ShouldChangeScreenAtEnd) - { - }//WindowFrame.SetLightMultiplier(0); - else - { - } // WindowFrame.SetLightMultiplier(1.2f); - WindowFrame.SetVisibleCursor(false); - } - - void ScreenGameplay::ResetNotes() - { - BarlineRatios = CurrentDiff->BarlineRatios; - NotesInMeasure.resize(CurrentDiff->Measures.size()); - for (auto i = 0U; i < CurrentDiff->Measures.size(); i++) - { - NotesInMeasure[i] = CurrentDiff->Measures[i]; - for (std::vector::iterator k = NotesInMeasure[i].begin(); k != NotesInMeasure[i].end(); k++) - { - if (k->GetPosition().x == 0) - k = NotesInMeasure[i].erase(k); - else - Evaluation.totalNotes++; - - if (k == NotesInMeasure[i].end()) break; - } - } - } - - void ScreenGameplay::LoadResources() - { - char* SkinFiles[] = - { - "cursor.png", - "Barline.png", - "barline_marker.png", - "hitcircle.png", - "Ready.png" - }; - - ImageLoader::LoadFromManifest(SkinFiles, 3, GameState::GetInstance().GetSkinPrefix()); - - memset(&Evaluation, 0, sizeof(Evaluation)); - - Measure = 0; - Combo = 0; - - if (ShouldChangeScreenAtEnd) - Barline.Init(CurrentDiff->Offset + MySong->LeadInTime); - else - { - Barline.Init(0); // edit mode - Barline.Alpha = 1; - } - - Lifebar.UpdateHealth(); - - if (CurrentDiff->Timing.size()) - MeasureTime = (60 * MySong->MeasureLength / CurrentDiff->Timing[0].Value); - else - MeasureTime = 0; - - // We might be retrying- in that case we should probably clean up. - RemoveTrash(); - - ResetNotes(); - - if (!Music) - { - Music = new AudioStream(); - - if (!Music || !Music->Open(MySong->SongDirectory / MySong->SongFilename)) - { - throw std::runtime_error((Utility::Format("couldn't open song %s", MySong->SongFilename.c_str()).c_str())); - } - - seekTime(0); - } - - MeasureRatio = 0; - RatioPerSecond = 0; - - if (Music) - { - if (ShouldChangeScreenAtEnd) - LeadInTime = MySong->LeadInTime; - } - - CursorRotospeed = Configuration::GetSkinConfigf("RotationSpeed", "Cursor"); - CursorCentered = Configuration::GetSkinConfigf("Centered", "Cursor") != 0; - CursorZooming = Configuration::GetSkinConfigf("Zooming", "Cursor") != 0; - CursorSize = Configuration::GetSkinConfigf("Size", "CursorSize"); - - if (CursorSize == 0) - CursorSize = 60; - } - - void ScreenGameplay::Init(dotcur::Song *OtherSong, uint32_t DifficultyIndex) - { - MySong = OtherSong; - CurrentDiff = MySong->Difficulties[DifficultyIndex]; - } - - int32_t ScreenGameplay::GetMeasure() - { - return Measure; - } - - /* Note stuff */ - - void ScreenGameplay::RunVector(std::vector& Vec, float TimeDelta) - { - Judgment Val; - - if (LeadInTime > 0) // No running while leadin is up. - return; - - // For each note in current measure - for (std::vector::iterator i = Vec.begin(); - i != Vec.end(); - i++) - { - // Run the note. - if ((Val = i->Run(TimeDelta, SongTime, IsAutoplaying)) != None) - { - Lifebar.HitJudgment(Val); - aJudgment.ChangeJudgment(Val); - StoreEvaluation(Val); - - // If it's a hold, keep running it until it's done. (Autoplay stuff.) - if (i->IsHold() && Val > Miss) - { - NotesHeld.push_back(*i); - i = Vec.erase(i); // These notes are off the measure. We'll handle them somewhere else. - } - break; - } - } - } - - void ScreenGameplay::RunMeasure(float delta) - { - if (Measure < CurrentDiff->Measures.size()) - { - RunVector(NotesInMeasure[Measure], delta); - - // For each note in PREVIOUS measure (when not in edit mode) - if (Measure > 0 && !EditMode) - RunVector(NotesInMeasure[Measure - 1], delta); - - // Run the ones in the NEXT measure. - if (Measure + 1 < NotesInMeasure.size()) - RunVector(NotesInMeasure[Measure + 1], delta); - } - - if (NotesHeld.size() > 0) - { - Judgment Val; - for (std::vector::iterator i = NotesHeld.begin(); - i != NotesHeld.end(); - i++) - { - // See if something's going on with the hold. - if ((Val = i->Run(delta, SongTime, IsAutoplaying)) != None) - { - // Judge accordingly.. - Lifebar.HitJudgment(Val); - aJudgment.ChangeJudgment(Val); - StoreEvaluation(Val); - AnimateOnly.push_back(*i); // Animate this one. - i = NotesHeld.erase(i); - break; - } - } - } - } - - bool ScreenGameplay::HandleInput(int32_t key, KeyEventType code, bool isMouseInput) - { - if (Screen::HandleInput(key, code, isMouseInput)) - return true; - - /* Notes */ - if (Measure < CurrentDiff->Measures.size() && // if measure is playable - (BindingsManager::TranslateKey(key) == KT_Select || BindingsManager::TranslateKey(key) == KT_SelectRight || - BindingsManager::TranslateKey(key) == KT_GameplayClick)// is mouse input and it's a mouse button.. - ) - { - if (CursorZooming) - { - if (code == KE_PRESS) - Cursor.SetScale(0.85f); - else - Cursor.SetScale(1); - } - - // For all measure notes.. - if (!IsPaused) - { - do - { - if (Measure > 0 && JudgeVector(NotesInMeasure[Measure - 1], code, key)) - break; - - if (JudgeVector(NotesInMeasure[Measure], code, key)) - break; - - if (Measure + 1 < NotesInMeasure.size() && JudgeVector(NotesInMeasure[Measure + 1], code, key)) - break; - } while (false); - - Judgment Val; - Vec2 mpos = WindowFrame.GetRelativeMPos(); - // For all held notes... - for (std::vector::iterator i = NotesHeld.begin(); - i != NotesHeld.end(); - i++) - { - // See if something's going on with the hold. - if ((Val = i->Hit(SongTime, mpos, code != KE_RELEASE, IsAutoplaying, key)) != None) - { - // Judge accordingly.. - Lifebar.HitJudgment(Val); - aJudgment.ChangeJudgment(Val); - StoreEvaluation(Val); - i = NotesHeld.erase(i); // Delete this object. The hold is done! - break; - } - } - return true; - } - } - - /* Functions */ - if (code == KE_PRESS) - { - switch (key) - { -#ifndef NDEBUG - case 'R': - Cleanup(); - Init(MySong, 0); - return true; -#endif - case 'A': // Autoplay - IsAutoplaying = !IsAutoplaying; - break; - case 'F': - FailEnabled = !FailEnabled; - break; - case 'T': - TappingMode = !TappingMode; - break; - case 'Q': - Running = false; - break; - default: - if (BindingsManager::TranslateKey(key) == KT_Escape) - { - /* TODO: pause */ - if (!IsPaused) - stopMusic(); - else - { - Music->SeekTime(SongTime); - startMusic(); - } - IsPaused = !IsPaused; - } - } - return true; - } - - return false; -} - - void ScreenGameplay::seekTime(float Time) - { - Music->SeekTime(Time); - SongTime = Time; - MeasureRatio = 0; - } - - void ScreenGameplay::startMusic() - { - Music->Play(); - } - - void ScreenGameplay::stopMusic() - { - Music->Stop(); - } - - /* TODO: Use measure ratios instead of song time for the barline. */ - bool ScreenGameplay::Run(double TimeDelta) - { - if (Music && LeadInTime <= 0) - { - SongDelta = Music->GetStreamedTime() - SongTime; - - SongTime += SongDelta; - } - - float ScreenTime = Screen::GetScreenTime(); - if (ScreenTime > ScreenPauseTime || !ShouldChangeScreenAtEnd) // we're over the pause? - { - if (SongTime <= 0) - { - if (LeadInTime > 0) - { - LeadInTime -= TimeDelta; - SongTime = -LeadInTime; - } - else - startMusic(); - } - - if (Next) // We have a pending screen? - return RunNested(TimeDelta); // use that instead. - - RunMeasure(SongDelta); - - if (LeadInTime) - Barline.Run(TimeDelta, MeasureRatio); - else - Barline.Run(SongDelta, MeasureRatio); - - if (SongTime > CurrentDiff->Offset) - { - while (BarlineRatios.size() && BarlineRatios.at(0).Time <= SongTime) - { - RatioPerSecond = BarlineRatios.front().Value; - BarlineRatios.erase(BarlineRatios.begin()); - } - - MeasureRatio += RatioPerSecond * SongDelta; - - if (SongDelta == 0 && !IsPaused) - { - if ((!Music || !Music->IsPlaying())) - MeasureRatio += RatioPerSecond * TimeDelta; - } - - if (MeasureRatio > 1.0f) - { - MeasureRatio -= 1.0f; - Measure += 1; - } - Lifebar.Run(SongDelta); - aJudgment.Run(TimeDelta); - } - } - - if (ShouldChangeScreenAtEnd) - { - float TotalTime = (CurrentDiff->Offset + MySong->LeadInTime + ScreenPauseTime); - float X = GetScreenTime() / TotalTime; - float xPos; - - if (X < 0.5) - xPos = ((-2)*X*X + 2 * X) * ScreenWidth; - else - xPos = ScreenWidth - ((-2)*X*X + 2 * X) * ScreenWidth; - - ReadySign.SetPosition(xPos, ScreenHeight / 2); - ReadySign.Alpha = 2 * ((-2)*X*X + 2 * X); - - // Lights - /*float LightProgress = GetScreenTime() / 1.5; - if (LightProgress <= 1) - WindowFrame.SetLightMultiplier(LightProgress * 1.2); - */ - } - else - ReadySign.Alpha = 0; - - RenderObjects(TimeDelta); - - if (ShouldChangeScreenAtEnd && Measure >= CurrentDiff->Measures.size()) - { - auto Eval = std::make_shared(); - Eval->Init(Evaluation, MySong->SongAuthor, MySong->SongName); - Next = Eval; - Music->Stop(); - } - - // You died? Not editing? Failing is enabled? - if (Lifebar.Health <= 0 && !EditMode && FailEnabled) - Running = false; // It's over. - - return Running; - } - - void ScreenGameplay::DrawVector(std::vector& Vec, float TimeDelta) - { - for (std::vector::reverse_iterator i = Vec.rbegin(); - i != Vec.rend(); - i++) - { - if (i->ShouldRemove()) - continue; - - i->SetImage(GameplayObjectImage); - i->SetSize(CircleSize); - i->Animate(TimeDelta, SongTime); - i->ColorInvert = false; - i->Render(); - } - } - - bool ScreenGameplay::JudgeVector(std::vector& Vec, int code, int key) - { - Judgment Val; - Vec2 mpos = WindowFrame.GetRelativeMPos(); - - for (std::vector::iterator i = Vec.begin(); - i != Vec.end(); - i++) - { - // See if it's a hit. - if ((Val = i->Hit(SongTime, TappingMode ? i->GetPosition() : mpos, code != KE_RELEASE, IsAutoplaying, key)) != None) - { - // Judge accordingly. - Lifebar.HitJudgment(Val); - aJudgment.ChangeJudgment(Val); - StoreEvaluation(Val); - - // If it's a hold, keep running it until it's done. - if (i->IsHold() && Val > Miss) - { - NotesHeld.push_back(*i); - i = Vec.erase(i); // These notes are off the measure. We'll handle them somewhere else. - } - - return true; - } - } - return false; - } - - void ScreenGameplay::RenderObjects(float TimeDelta, bool drawPlayable) - { - Vec2 mpos = WindowFrame.GetRelativeMPos(); - - Cursor.SetPosition(mpos); - - Cursor.AddRotation(CursorRotospeed * TimeDelta); - - int Beat = MeasureRatio * MySong->MeasureLength; - float Fraction = (float(MeasureRatio * MySong->MeasureLength) - Beat); - Lifebar.SetScaleX(1.0 - 0.05 * Fraction); - - // Rendering ahead. - - if (IsPaused) - { - Background.Blue = Background.Red = Background.Green = 0.5; - } - else - Background.Blue = Background.Red = Background.Green = 1; - - Background.Render(); - - MarkerA.Render(); - MarkerB.Render(); - - if (!EditMode) - Lifebar.Render(); - - if (drawPlayable) - { - DrawVector(NotesHeld, TimeDelta); - DrawVector(AnimateOnly, TimeDelta); - - if (Measure > 0) - { - if (NotesInMeasure.size() && // there are measures and - Measure - 1 < NotesInMeasure.size() && // the measure is within the range and - NotesInMeasure.at(Measure - 1).size() > 0) // there are notes in this measure - { - DrawVector(NotesInMeasure[Measure - 1], TimeDelta); - } - } - - // Render current measure on front of the next! - if (Measure + 1 < CurrentDiff->Measures.size()) - { - if (NotesInMeasure.size() > Measure + 1 && NotesInMeasure.at(Measure + 1).size() > 0) - { - DrawVector(NotesInMeasure[Measure + 1], TimeDelta); - } - } - - if (Measure < CurrentDiff->Measures.size()) - { - if (NotesInMeasure.size() > Measure && NotesInMeasure.at(Measure).size() > 0) - { - DrawVector(NotesInMeasure[Measure], TimeDelta); - } - } - } - - Barline.Render(); - - if (!EditMode) - aJudgment.Render(); - - // Combo rendering. - std::stringstream str; - str << Combo; - - float textX = GetScreenOffset(0.5).x - (str.str().length() * ComboSizeX / 2); - MyFont.Render(str.str(), Vec2(textX, 0)); - - std::stringstream str2; - str2 << int32_t(1000000.0 * Evaluation.dpScoreSquare / (Evaluation.totalNotes * (Evaluation.totalNotes + 1))); - textX = GetScreenOffset(0.5).x - (str2.str().length() * ComboSizeX / 2); - MyFont.Render(str2.str(), Vec2(textX, 720)); - - /* Lengthy information printing code goes here.*/ - std::stringstream info; - - if (IsAutoplaying) - info << "Autoplay"; - -#ifndef NDEBUG - info << "\nSongTime: " << SongTime << "\nPlaybackTime: "; - if (Music) - info << Music->GetPlayedTime(); - else - info << "???"; - /*info << "\nStreamTime: "; - if(Music) - info << Music->GetStreamedTime(); - else - info << "???"; - */ - - info << "\naudioFactor: " << MixerGetFactor(); - info << "\nSongDelta: " << SongDelta; - info << "\nDevice Latency: " << (int)(MixerGetLatency() * 1000); - /* - if (Music) - info << Music->GetStreamedTime() - Music->GetPlaybackTime(); - else - info << "???"; - */ - info << "\nScreenTime: " << GetScreenTime(); - info << "\nMeasureRatio: " << MeasureRatio; - info << "\nMeasureRatioPerSecond: " << RatioPerSecond; -#endif - if (TappingMode) - info << "\nTapping mode"; - if (!FailEnabled) - info << "\nFailing Disabled"; - -#ifdef NDEBUG - if (EditMode) -#endif - info << "\nMeasure: " << Measure; - SongInfo.Render(info.str(), Vec2(0, 0)); - - ReadySign.Render(); - - Cursor.Render(); - } - } -} \ No newline at end of file diff --git a/src/ScreenGameplay.h b/src/ScreenGameplay.h deleted file mode 100644 index 51888204..00000000 --- a/src/ScreenGameplay.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "Screen.h" -#include "SongDC.h" -#include "Audio.h" -#include "ActorBarline.h" -#include "ActorJudgment.h" -#include "ActorLifebar.h" -#include "BitmapFont.h" - -namespace Game { - namespace dotcur { - class ScreenGameplay : public Screen - { - protected: // shit the edit screen needs - - dotcur::Song *MySong; - dotcur::Difficulty* CurrentDiff; - // Game Data - uint32_t Measure; - - // the point of this is that we can change the barline's position. - float MeasureRatio, RatioPerSecond; - TimingData BarlineRatios; - - bool IsAutoplaying; // true for autoplaying notes - - bool ShouldChangeScreenAtEnd; - bool EditMode; - - void RenderObjects(float TimeDelta, bool drawPlayable = true); - - // Two seconds of pause before actually starting the screen. - static const uint32_t ScreenPauseTime = 2; - - // leave this to who will use it - void startMusic(); - void seekTime(float time); - void stopMusic(); - - // actors we need to access on edit - ActorBarline Barline; - - AudioStream *Music; - - std::vector > NotesInMeasure; - void DrawVector(std::vector& Vec, float TimeDelta); - void ResetNotes(); - - bool CursorZooming, CursorCentered; - float CursorRotospeed, CursorSize; - private: // shit only screengameplay needs - - void ProcessBarlineRatios(); - // Run the current measure (jic func name isn't obvious enough) - void RunMeasure(float delta); - void StoreEvaluation(Judgment Eval); - uint32_t Combo; - BitmapFont MyFont; - BitmapFont SongInfo; - Sprite ReadySign; - - // Notes - std::vector NotesHeld, AnimateOnly; - - // Game Data - // MeasureTime is the time the current measure will last. - // MeasureTimeElapsed is the time so far of the current measure - double MeasureTime; - double LeadInTime; - - // Actors - ActorLifebar Lifebar; - ActorJudgment aJudgment; - Sprite MarkerA, MarkerB, Cursor, Background; - EvaluationData Evaluation; - - double SongTime, SongDelta; - - bool FailEnabled; - bool TappingMode; - bool IsPaused; - - bool JudgeVector(std::vector& Vec, int code, int key); - void RunVector(std::vector& Vec, float TimeDelta); - - Texture* GameplayObjectImage; - public: - ScreenGameplay(); - - virtual void Init(dotcur::Song *OtherSong, uint32_t DifficultyIndex); - - int32_t GetMeasure(); - virtual bool Run(double Delta); - virtual bool HandleInput(int32_t key, KeyEventType code, bool isMouseInput); - Vec2 GetScreenOffset(float alignment); - virtual void Cleanup(); - void RemoveTrash(); - - /* What we call from the ScreenLoading thread! */ - void LoadResources(); - void MainThreadInitialization(); - }; - - } -} \ No newline at end of file diff --git a/src/ScreenGameplay7K.cpp b/src/ScreenGameplay7K.cpp index bcb9855a..ead49af7 100644 --- a/src/ScreenGameplay7K.cpp +++ b/src/ScreenGameplay7K.cpp @@ -112,26 +112,20 @@ namespace Game { if (!Active) Activate(); break; - case KT_Right: - //SpeedMultiplierUser += 0.25; - break; - case KT_Left: - // SpeedMultiplierUser -= 0.25; - break; default: break; } if (BindingsManager::TranslateKey7K(key) != KT_Unknown) { for (auto &player: Players) - player->TranslateKey(BindingsManager::TranslateKey7K(key), true, Time.Stream); + player->TranslateKey(BindingsManager::TranslateKey7K(key), true, Time.Stream + JudgeOffset); } } else { if (BindingsManager::TranslateKey7K(key) != KT_Unknown) { for (auto &player: Players) - player->TranslateKey(BindingsManager::TranslateKey7K(key), false, Time.Stream); + player->TranslateKey(BindingsManager::TranslateKey7K(key), false, Time.Stream + JudgeOffset); } } @@ -146,7 +140,13 @@ namespace Game { while (BGMEvents.size() && BGMEvents.front().Time <= Time.Stream) { for (auto &&s : Keysounds[BGMEvents.front().Sound]) - if (s) s->Play(); + if (s) { + double dt = Time.Stream - BGMEvents.front().Time; + if (dt < s->GetDuration()) { + s->SeekTime(dt); + s->Play(); + } + } BGMEvents.pop(); } } @@ -163,8 +163,11 @@ namespace Game { // post-gameplay failure? if (!ShouldDelayFailure()) { FailSnd.Play(); + // We stop all audio.. - Music->Stop(); + if (Music) + Music->Stop(); + for (auto i = Keysounds.begin(); i != Keysounds.end(); ++i) for (auto &&s : i->second) if (s) @@ -304,6 +307,52 @@ namespace Game { Time.Stream += SongDelta; } + void ScreenGameplay::OnPlayerHit(ScoreKeeperJudgment judgment, double dt, uint32_t lane, bool hold, bool release, int pn) + { + + if (Animations->GetEnv()->CallFunction("HitEvent", 6)) + { + Animations->GetEnv()->PushArgument(judgment); + Animations->GetEnv()->PushArgument(dt); + Animations->GetEnv()->PushArgument((int)lane + 1); + Animations->GetEnv()->PushArgument(hold); + Animations->GetEnv()->PushArgument(release); + Animations->GetEnv()->PushArgument(pn); + Animations->GetEnv()->RunFunction(); + } + + auto PlayerScoreKeeper = Players[pn]->GetScoreKeeper(); + if (PlayerScoreKeeper->getMaxNotes() == PlayerScoreKeeper->getScore(ST_NOTES_HIT)) + Animations->DoEvent("OnFullComboEvent"); + + } + + void ScreenGameplay::OnPlayerMiss(double dt, uint32_t lane, bool hold, bool dontbreakcombo, bool earlymiss, int pn) + { + BGA->OnMiss(); + + if (Animations->GetEnv()->CallFunction("MissEvent", 4)) + { + Animations->GetEnv()->PushArgument(dt); + Animations->GetEnv()->PushArgument((int)lane + 1); + Animations->GetEnv()->PushArgument(hold); + Animations->GetEnv()->PushArgument(pn); + Animations->GetEnv()->RunFunction(); + } + } + + void ScreenGameplay::OnPlayerGearKeyEvent(uint32_t lane, bool keydown, int pn) + { + if (Animations->GetEnv()->CallFunction("GearKeyEvent", 3)) + { + Animations->GetEnv()->PushArgument((int)lane + 1); + Animations->GetEnv()->PushArgument(keydown); + Animations->GetEnv()->PushArgument(pn); + + Animations->GetEnv()->RunFunction(); + } + } + bool ScreenGameplay::Run(double Delta) { if (Next) @@ -363,8 +412,6 @@ namespace Game { for (auto &p : Players) p->Render(Time.InterpolatedStream); - // PlayerContext::Render(Time.InterpolatedStream) - Animations->DrawFromLayer(14); } diff --git a/src/ScreenGameplay7K.h b/src/ScreenGameplay7K.h index 0972487e..f450cf39 100644 --- a/src/ScreenGameplay7K.h +++ b/src/ScreenGameplay7K.h @@ -3,11 +3,11 @@ #include "Audio.h" #include "Song7K.h" #include "BackgroundAnimation.h" +#include "AudioSourceOJM.h" class AudioStream; class Texture; class SceneEnvironment; -class AudioSourceOJM; class LuaManager; #include "PlayerContext.h" @@ -62,11 +62,7 @@ namespace Game { std::unique_ptr BGA; double JudgeOffset; void SetupScriptConstants(); - void UpdateScriptVariables(); - void UpdateScriptScoreVariables(); - - void ChangeNoteTimeToBeats(); - + // Done in loading thread bool LoadChartData(); bool LoadSongAudio(); @@ -84,11 +80,18 @@ namespace Game { bool SongHasFinished(); void UpdateSongTime(float Delta); + void OnPlayerHit(ScoreKeeperJudgment judgment, double dt, uint32_t lane, bool hold, bool release, int pn); void Render(); void PlayKeysound(int Keysound); void Activate(); + + + void OnPlayerMiss(double dt, uint32_t lane, bool hold, bool dontbreakcombo, bool earlymiss, int pn); + + void OnPlayerGearKeyEvent(uint32_t lane, bool keydown, int pn); + friend class Noteskin; public: diff --git a/src/ScreenGameplay7K_Lua.cpp b/src/ScreenGameplay7K_Lua.cpp index 24187652..603a7f09 100644 --- a/src/ScreenGameplay7K_Lua.cpp +++ b/src/ScreenGameplay7K_Lua.cpp @@ -9,5 +9,3 @@ #include "ScoreKeeper7K.h" #include "ScreenGameplay7K.h" - -} \ No newline at end of file diff --git a/src/ScreenGameplay7K_Mechanics.cpp b/src/ScreenGameplay7K_Mechanics.cpp index 834cb3da..6dd02924 100644 --- a/src/ScreenGameplay7K_Mechanics.cpp +++ b/src/ScreenGameplay7K_Mechanics.cpp @@ -13,13 +13,7 @@ #include "ScoreKeeper7K.h" #include "ScreenGameplay7K.h" -#include "ScreenGameplay7K_Mechanics.h" //#include -using namespace VSRG; - -void ScreenGameplay7K::RecalculateMatrix() -{ - PositionMatrix = glm::translate(Mat4(), glm::vec3(0, JudgmentLinePos + CurrentVertical * SpeedMultiplier, 0)); -} +using namespace Game::VSRG; diff --git a/src/ScreenGameplay7K_Setup.cpp b/src/ScreenGameplay7K_Setup.cpp index 7e7195c2..3e76384a 100644 --- a/src/ScreenGameplay7K_Setup.cpp +++ b/src/ScreenGameplay7K_Setup.cpp @@ -54,7 +54,7 @@ namespace Game { void ScreenGameplay::AssignMeasure(uint32_t Measure) { - double mt = Players[0]->GetMeasureTime(Measure); + double mt = Players[0]->GetPlayerState().GetMeasureTime(Measure); double wt = Players[0]->GetPlayerState().GetWarpedSongTime(mt); for (auto& player : Players) { player->SetUnwarpedTime(mt); @@ -83,9 +83,9 @@ namespace Game { ForceActivation = false; for (auto i = 0; i < GameState::GetInstance().GetPlayerCount(); i++) { - Players.push_back(std::make_unique(i, *GameState::GetInstance().GetParameters(i))); + Players.push_back(std::make_unique(i, *GameState::GetInstance().GetParameters(i))); + GameState::GetInstance().SetPlayerContext(Players[i].get(), i); Players[i]->PlayKeysound = std::bind(&ScreenGameplay::PlayKeysound, this, std::placeholders::_1); - Players[i]->SetSceneEnvironment(Animations); } } @@ -134,12 +134,21 @@ namespace Game { if (SkipLoadAudio) return true; auto Rate = GameState::GetInstance().GetParameters(0)->Rate; + + + auto &ps = Players[0]->GetPlayerState(); + auto SoundList = ps.GetSoundList(); if (!Music) { Music = std::make_unique(); Music->SetPitch(Rate); - if (std::filesystem::exists(MySong->SongFilename) - && Music->Open(MySong->SongDirectory / MySong->SongFilename)) + + auto s = MySong->SongDirectory / MySong->SongFilename; + + Log::LogPrintf("Chart Audio: Attempt to load \"%s\"...", s.string().c_str()); + + if (std::filesystem::exists(s) + && Music->Open(s)) { Log::Printf("Stream for %s succesfully opened.\n", MySong->SongFilename.c_str()); } @@ -156,19 +165,22 @@ namespace Game { auto extension = i.path().extension(); if (extension == ".mp3" || extension == ".ogg") if (Music->Open(i.path())) - return true; + if (!SoundList.size()) + return true; } // Quit; couldn't find audio for a chart that requires it. Music = nullptr; - Log::Printf("Unable to load song (Path: %s)\n", MySong->SongFilename.c_str()); - return false; + + // don't abort load if we have keysounds + if (!SoundList.size()) { + Log::Printf("Unable to load song (Path: %s)\n", MySong->SongFilename.c_str()); + return false; + } } } } - auto &ps = Players[0]->GetPlayerState(); - auto SoundList = ps.GetSoundList(); auto ChartType = ps.GetChartType(); // Load samples. @@ -189,7 +201,7 @@ namespace Game { } else if (SoundList.size()) { - Log::Printf("Chart Audio: Loading samples... "); + Log::LogPrintf("Chart Audio: Loading samples... "); LoadSamples(); } @@ -235,11 +247,11 @@ namespace Game { std::map audio; std::mutex audio_data_mutex; std::mutex keysound_data_mutex; - auto &slicedata = ps.GetSliceData(); + const auto &slicedata = ps.GetSliceData(); // do bmson loading - threaded slicing! std::vector> threads; - std::atomic obj_cnt = 0; + std::atomic obj_cnt(0); auto load_start_time = std::chrono::high_resolution_clock::now(); for (auto audiofile : slicedata.AudioFiles) { @@ -330,9 +342,12 @@ namespace Game { auto diff = GameState::GetInstance().GetDifficulty(0); + if (!diff) // possibly preloaded + diff = MySong->GetDifficulty(0); + if (Time.InterpolateStream && // Apply drift is enabled and: ((ApplyDriftVirtual && diff->IsVirtual) || // We want to apply it to a keysounded file and it's virtual - (ApplyDriftDecoder && diff->IsVirtual))) // or we want to apply it to a non-keysounded file and it's not virtual + (ApplyDriftDecoder && diff->IsVirtual))) // or we want to apply it to a non-keysounded file and it's not virtual TimeError.AudioDrift += MixerGetLatency(); TimeError.AudioDrift += Configuration::GetConfigf("Offset7K"); @@ -349,22 +364,20 @@ namespace Game { Log::Printf("Processing song... "); for (auto &&p : Players) { - for (auto sd : MySong->Difficulties) - if (sd->ID == GameState::GetInstance().GetDifficulty(p->GetPlayerNumber())->ID) { + for (auto sd : MySong->Difficulties) { + auto diff = GameState::GetInstance().GetDifficulty(p->GetPlayerNumber()); + // If there's no difficulty assigned, no point in checking. + if ( (diff && sd->ID == diff->ID) || !diff ) { p->SetPlayableData(sd, TimeError.AudioDrift, DesiredDefaultSpeed, Type); p->Init(); - // there's a better way but not right now - GameState::GetInstance().SetCurrentGaugeType(p->GetCurrentGaugeType(), p->GetPlayerNumber()); - GameState::GetInstance().SetCurrentScoreType(p->GetCurrentScoreType(), p->GetPlayerNumber()); - GameState::GetInstance().SetCurrentSystemType(p->GetCurrentSystemType(), p->GetPlayerNumber()); - if (!p->GetPlayerState().HasTimingData()) { Log::Printf("Error loading chart: No timing data for player %d.\n", p->GetPlayerNumber()); return false; } } + } } auto bgm0 = Players[0]->GetBgmData(); @@ -425,7 +438,7 @@ namespace Game { AssignMeasure(StartMeasure); ForceActivation = ForceActivation || (Configuration::GetSkinConfigf("InmediateActivation") == 1); - + // We're done with the data stored in the difficulties that aren't the one we're using. Clear it up. for (auto i = MySong->Difficulties.begin(); i != MySong->Difficulties.end(); ++i) @@ -440,7 +453,7 @@ namespace Game { snd->AwaitLoad(); } } - + auto dur = std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - st).count(); Log::LogPrintf("Done. Taken %I64dms to finish.\n", dur); } @@ -464,11 +477,32 @@ namespace Game { for (auto& p : Players) { p->Validate(); + + p->OnHit = std::bind(&ScreenGameplay::OnPlayerHit, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6); + + p->OnMiss = std::bind(&ScreenGameplay::OnPlayerMiss, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3, + std::placeholders::_4, + std::placeholders::_5, + std::placeholders::_6); + + p->OnGearKeyEvent = std::bind(&ScreenGameplay::OnPlayerGearKeyEvent, this, + std::placeholders::_1, + std::placeholders::_2, + std::placeholders::_3); } Animations->Initialize("", false); Running = true; } - } - } \ No newline at end of file + } +} \ No newline at end of file diff --git a/src/ScreenLoading.cpp b/src/ScreenLoading.cpp index 5fbade36..208ae26b 100644 --- a/src/ScreenLoading.cpp +++ b/src/ScreenLoading.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "GameState.h" #include "Screen.h" #include "SceneEnvironment.h" @@ -35,7 +35,7 @@ class LoadScreenThread } }; -ScreenLoading::ScreenLoading(std::shared_ptr _Next) : Screen("ScreenLoading", nullptr) +ScreenLoading::ScreenLoading(std::shared_ptr _Next) : Screen("ScreenLoading", false) { Next = _Next; LoadThread = nullptr; diff --git a/src/ScreenLoading.h b/src/ScreenLoading.h index ee65d736..840328cf 100644 --- a/src/ScreenLoading.h +++ b/src/ScreenLoading.h @@ -15,5 +15,5 @@ class ScreenLoading : public Screen bool Run(double TimeDelta) override; bool HandleInput(int32_t key, KeyEventType code, bool isMouseInput) override; bool HandleScrollInput(double xOff, double yOff) override; - void Cleanup(); + void Cleanup() override; }; \ No newline at end of file diff --git a/src/ScreenMainMenu.cpp b/src/ScreenMainMenu.cpp index b0cdcf68..bf177e34 100644 --- a/src/ScreenMainMenu.cpp +++ b/src/ScreenMainMenu.cpp @@ -1,8 +1,8 @@ #include "pch.h" -#include "GameGlobal.h" + #include "GameState.h" -#include "Configuration.h" + #include "SceneEnvironment.h" #include "Screen.h" #include "ImageLoader.h" @@ -26,7 +26,7 @@ BitmapFont* MainMenuFont = NULL; LuaManager* MainMenuLua = NULL; TruetypeFont* TTFO = NULL; -ScreenMainMenu::ScreenMainMenu() : Screen("ScreenMainMenu", nullptr) +ScreenMainMenu::ScreenMainMenu() : Screen("ScreenMainMenu", false) { TNext = nullptr; } @@ -47,7 +47,7 @@ void ScreenMainMenu::Init() ChangeState(StateIntro); if (!TTFO) - TTFO = new TruetypeFont(GameState::GetInstance().GetSkinFile("font.ttf"), 16); + TTFO = new TruetypeFont(GameState::GetInstance().GetSkinFile("font.ttf")); } bool ScreenMainMenu::HandleInput(int32_t key, KeyEventType code, bool isMouseInput) @@ -68,9 +68,13 @@ bool ScreenMainMenu::Run(double Delta) if (RunNested(Delta)) return true; - TTFO->Render(std::string("version: " RAINDROP_VERSIONTEXT "\nhttp://github.com/zardoru/raindrop"), Vec2(0, 0), glm::translate(Mat4(), Vec3(0, 0, 30))); + Animations->DrawTargets(Delta); + float f = 24; + auto m = glm::translate(0.f, 0.f, 30.f); + TTFO->Render(std::string("version: " RAINDROP_VERSIONTEXT "\nhttp://github.com/zardoru/raindrop"), + Vec2(0, 0), m, Vec2(1, f)); return Running; } diff --git a/src/ScreenSelectMusic.cpp b/src/ScreenSelectMusic.cpp index b8b7acf6..3106af2b 100644 --- a/src/ScreenSelectMusic.cpp +++ b/src/ScreenSelectMusic.cpp @@ -8,17 +8,16 @@ #include "GameWindow.h" #include "ImageLoader.h" #include "Audio.h" -#include "Configuration.h" + #include "Sprite.h" #include "GuiButton.h" +#include "Line.h" #include "Song.h" #include "ScreenSelectMusic.h" #include "ScreenLoading.h" -#include "ScreenGameplay.h" #include "ScreenGameplay7K.h" -#include "ScreenEdit.h" #include "LuaManager.h" #include "SongDatabase.h" @@ -142,7 +141,6 @@ void ScreenSelectMusic::Cleanup() StopLoops(); - GameState::GetInstance().SetSelectedSong(nullptr); Game::SongWheel::GetInstance().CleanItems(); } @@ -184,23 +182,12 @@ void ScreenSelectMusic::StartGameplayScreen() std::shared_ptr MySong = GameState::GetInstance().GetSelectedSongShared(); auto difindex = Game::SongWheel::GetInstance().GetDifficulty(); - if (MySong->Mode == MODE_DOTCUR) - { -#ifdef DOTCUR_ENABLED - auto DotcurGame = std::make_shared(); - DotcurGame->Init(static_cast(MySong.get()), difindex); + + auto VSRGGame = std::make_shared(); - LoadNext = std::make_shared(DotcurGame); -#endif - } - else - { - auto VSRGGame = std::make_shared(); + VSRGGame->Init(std::dynamic_pointer_cast(MySong)); - VSRGGame->Init(std::dynamic_pointer_cast(MySong)); - - LoadNext = std::make_shared(VSRGGame); - } + LoadNext = std::make_shared(VSRGGame); LoadNext->Init(); Next = LoadNext; @@ -213,6 +200,8 @@ void ScreenSelectMusic::OnSongSelect(std::shared_ptr MySong, uint8_t if (IsTransitioning) return; + if (difindex > MySong->GetDifficultyCount()) return; + if (PreviewStream) PreviewStream->Stop(); IsTransitioning = true; @@ -221,7 +210,8 @@ void ScreenSelectMusic::OnSongSelect(std::shared_ptr MySong, uint8_t StopLoops(); - GameState::GetInstance().SetSelectedSong(MySong); + auto sng = std::static_pointer_cast(MySong); + GameState::GetInstance().SetDifficulty(sng->Difficulties[difindex], 0); Animations->DoEvent("OnSelect", 1); TransitionTime = Animations->GetEnv()->GetFunctionResultF(); @@ -235,7 +225,6 @@ void ScreenSelectMusic::OnSongChange(std::shared_ptr MySong, uint8_t if (MySong) { - GameState::GetInstance().SetSelectedSong(MySong); Animations->DoEvent("OnSongChange"); PreviewWaitTime = 1; @@ -423,11 +412,9 @@ bool ScreenSelectMusic::HandleInput(int32_t key, KeyEventType code, bool isMouse break; case KT_Left: Game::SongWheel::GetInstance().PrevDifficulty(); - //GameState::GetInstance().SetDifficulty(Game::SongW, 0); break; case KT_Right: Game::SongWheel::GetInstance().NextDifficulty(); - //GameState::GetInstance().SetDifficulty(); break; default: break; @@ -447,11 +434,13 @@ bool ScreenSelectMusic::HandleScrollInput(double xOff, double yOff) return true; } + if (IsTransitioning) return false; + Animations->HandleScrollInput(xOff, yOff); return Game::SongWheel::GetInstance().HandleScrollInput(xOff, yOff); } -void ScreenSelectMusic::TransformItem(int Item, std::shared_ptr Song, bool IsSelected, int Index) +void ScreenSelectMusic::TransformItem(int Item, std::shared_ptr Song, bool IsSelected, int Index) { if (Animations->GetEnv()->CallFunction("TransformItem", 4)) { @@ -463,7 +452,7 @@ void ScreenSelectMusic::TransformItem(int Item, std::shared_ptr Song } } -void ScreenSelectMusic::TransformString(int Item, std::shared_ptr Song, bool IsSelected, int Index, std::string text) +void ScreenSelectMusic::TransformString(int Item, std::shared_ptr Song, bool IsSelected, int Index, std::string text) { if (Animations->GetEnv()->CallFunction("TransformString", 5)) { diff --git a/src/ScreenSelectMusic.h b/src/ScreenSelectMusic.h index cd1133bf..7b51d9af 100644 --- a/src/ScreenSelectMusic.h +++ b/src/ScreenSelectMusic.h @@ -1,5 +1,7 @@ #pragma once +#include "Audiofile.h" + class BitmapFont; namespace Game { @@ -56,8 +58,8 @@ namespace Game { void OnItemHover(int32_t Index, uint32_t boundIndex, std::string Line, std::shared_ptr Selected); void OnItemHoverLeave(int32_t Index, uint32_t boundIndex, std::string Line, std::shared_ptr Selected); - void TransformItem(int Item, std::shared_ptr Song, bool IsSelected, int ListItem); - void TransformString(int Item, std::shared_ptr Song, bool IsSelected, int ListItem, std::string text); + void TransformItem(int Item, std::shared_ptr Song, bool IsSelected, int ListItem); + void TransformString(int Item, std::shared_ptr Song, bool IsSelected, int ListItem, std::string text); public: ScreenSelectMusic(); void LoadResources() override; diff --git a/src/ScreenVideoTest.cpp b/src/ScreenVideoTest.cpp index 841fec13..939b68fe 100644 --- a/src/ScreenVideoTest.cpp +++ b/src/ScreenVideoTest.cpp @@ -8,7 +8,7 @@ VideoPlayback play(2); -ScreenVideoTest::ScreenVideoTest() : Screen("ScreenVideoTest", nullptr) +ScreenVideoTest::ScreenVideoTest() : Screen("ScreenVideoTest", false) { clock = 0; play.Open("bga.mp4"); diff --git a/src/Shader.cpp b/src/Shader.cpp index 2eaafebe..81c2221f 100644 --- a/src/Shader.cpp +++ b/src/Shader.cpp @@ -41,10 +41,9 @@ const char* fragShader = "#version 120\n" "uniform sampler2D tex;\n" "uniform bool inverted;\n" "uniform bool replaceColor;\n" -"uniform float clampHSUnder;\n" -"uniform float clampHSOver;\n" -"uniform float clampFLSum;\n" -"uniform float HFactor;\n" +"uniform float hdcenter;\n" +"uniform float flsize;\n" +"uniform float hdsize;\n" "uniform bool BlackToTransparent;\n" // If true, transform r0 g0 b0 aX to a0. "uniform int HiddenLightning;\n" "\n" @@ -55,7 +54,10 @@ const char* fragShader = "#version 120\n" " if (!replaceColor){\n" " tex2D = texture2D(tex, texcoord);\n" " }else{" -" tex2D = vec4(1.0, 1.0, 1.0, texture2D(tex, texcoord).a);" +" float a = texture2D(tex, texcoord).a;" +" float r = 0.5;" +" float rw = fwidth(a);" +" tex2D = vec4(1.0, 1.0, 1.0, smoothstep(r - rw, r + rw, a));" " }" " if (inverted) {\n" " tCol = vec4(1.0, 1.0, 1.0, tex2D.a*2) - tex2D * color;\n" @@ -66,12 +68,15 @@ const char* fragShader = "#version 120\n" " if (tCol.r == 0 && tCol.g == 0 && tCol.b == 0) tCol.a = 0;" " }" " if (HiddenLightning > 0) {\n" -" float clmp = clamp(Pos_world.y, clampHSUnder, clampHSOver) + clampFLSum;\n" -" float ld;\n" -" if (HiddenLightning == 1) ld = 1 - clmp * HFactor;\n" -" else if (HiddenLightning == 2) ld = clmp * HFactor;\n" -" else { clmp = abs(clmp); ld = 1 - clmp * HFactor; } \n" -" gl_FragColor = vec4(tCol.rgb, tCol.a * clamp(ld, 0, 1));\n" +" float ld = 1;\n" +" if (HiddenLightning == 2) " +" ld = smoothstep(hdcenter - hdsize, hdcenter, Pos_world.y);\n" +" else if (HiddenLightning == 1) " +" ld = smoothstep(hdcenter, hdcenter - hdsize, Pos_world.y);\n" +" else { ld = smoothstep(hdcenter - flsize - hdsize / 2, hdcenter - flsize, Pos_world.y) * " +" smoothstep(hdcenter + flsize + hdsize / 2, hdcenter + flsize, Pos_world.y);" +" } \n" +" gl_FragColor = vec4(tCol.rgb, tCol.a * ld);\n" " } else if (HiddenLightning == 0) {\n" " gl_FragColor = tCol;\n" " }\n" @@ -152,15 +157,14 @@ namespace Renderer { uniforms[U_COLOR] = glGetUniformLocation(mProgram, "color"); uniforms[U_INVERT] = glGetUniformLocation(mProgram, "inverted"); uniforms[U_HIDDEN] = glGetUniformLocation(mProgram, "HiddenLightning"); - uniforms[U_HIDLOW] = glGetUniformLocation(mProgram, "clampHSUnder"); - uniforms[U_HIDHIGH] = glGetUniformLocation(mProgram, "clampHSOver"); - uniforms[U_HIDFAC] = glGetUniformLocation(mProgram, "HFactor"); - uniforms[U_HIDSUM] = glGetUniformLocation(mProgram, "clampFLSum"); + uniforms[U_HIDCENTER] = glGetUniformLocation(mProgram, "hdcenter"); + uniforms[U_HIDSIZE] = glGetUniformLocation(mProgram, "hdsize"); + uniforms[U_HIDFLSIZE] = glGetUniformLocation(mProgram, "flsize"); uniforms[U_REPCOLOR] = glGetUniformLocation(mProgram, "replaceColor"); uniforms[U_BTRANSP] = glGetUniformLocation(mProgram, "BlackToTransparent"); - Bind(); + StaticBind(); return true; } @@ -176,7 +180,7 @@ namespace Renderer { glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &proj[0][0]); } - void DefaultShader::Bind() { + void DefaultShader::StaticBind() { CHECKERR(); if (mLastShader != mProgram) { mLastShader = mProgram; diff --git a/src/Shader.h b/src/Shader.h index 2fac6d6a..eef94b23 100644 --- a/src/Shader.h +++ b/src/Shader.h @@ -14,10 +14,9 @@ namespace Renderer { U_COLOR, U_INVERT, U_HIDDEN, - U_HIDLOW, - U_HIDHIGH, - U_HIDFAC, - U_HIDSUM, + U_HIDCENTER, + U_HIDSIZE, + U_HIDFLSIZE, U_REPCOLOR, U_BTRANSP, NUM_SHADERVARS @@ -60,7 +59,7 @@ namespace Renderer { public: static bool Compile(); static void UpdateProjection(Mat4 proj); - static void Bind(); + static void StaticBind(); static uint32_t GetUniform(uint32_t uni); static void SetColor(float r, float g, float b, float a); diff --git a/src/Song.h b/src/Song.h index 71946800..bbd3986d 100644 --- a/src/Song.h +++ b/src/Song.h @@ -75,7 +75,6 @@ inline bool TimeSegmentCompare(const T &t, const double &v) { enum ModeType { - MODE_DOTCUR, MODE_VSRG }; @@ -110,11 +109,6 @@ namespace Game std::filesystem::path Filename; std::string Author; - uint32_t TotalNotes; - uint32_t TotalHolds; - uint32_t TotalObjects; - uint32_t TotalScoringObjects; - int ID; Difficulty() @@ -122,10 +116,6 @@ namespace Game ID = -1; Duration = 0; Offset = 0; - TotalNotes = 0; - TotalHolds = 0; - TotalObjects = 0; - TotalScoringObjects = 0; } }; @@ -155,6 +145,8 @@ namespace Game // Song genre std::string Genre; + virtual uint8_t GetDifficultyCount() { return 0; }; + Song() { ID = -1; PreviewTime = 0; }; virtual ~Song() {}; }; diff --git a/src/Song7K.cpp b/src/Song7K.cpp index a2a34b61..9eb2f7aa 100644 --- a/src/Song7K.cpp +++ b/src/Song7K.cpp @@ -1,7 +1,7 @@ #include "pch.h" #include "GameGlobal.h" -#include "Configuration.h" + #include "Logging.h" #include "Song7K.h" @@ -30,7 +30,10 @@ Difficulty* Song::GetDifficulty(uint32_t i) return Difficulties.at(i).get(); } - +uint8_t Song::GetDifficultyCount() +{ + return Difficulties.size(); +} void Difficulty::Destroy() { @@ -41,3 +44,41 @@ void Difficulty::Destroy() Author.clear(); Author.shrink_to_fit(); Filename = ""; } + + +uint32_t Game::VSRG::DifficultyLoadInfo::GetObjectCount() +{ + uint32_t cnt = 0; + for (auto measure : Measures) { + for (auto i = 0; i < MAX_CHANNELS; i++) { + for (auto note : measure.Notes[i]) { + cnt++; + } + } + } + + return cnt; +} + +uint32_t Game::VSRG::DifficultyLoadInfo::GetScoreItemsCount() +{ + uint32_t cnt = 0; + for (auto measure : Measures) { + for (auto i = 0; i < MAX_CHANNELS; i++) { + for (auto note : measure.Notes[i]) { + if (note.NoteKind == NK_FAKE || + note.NoteKind == NK_INVISIBLE || + note.NoteKind == NK_MINE) + continue; + + if (note.EndTime != 0 && + note.NoteKind == NK_NORMAL) + cnt += 2; + else + cnt++; + } + } + } + + return cnt; +} diff --git a/src/Song7K.h b/src/Song7K.h index 30d2bf2f..d71072b4 100644 --- a/src/Song7K.h +++ b/src/Song7K.h @@ -31,7 +31,7 @@ namespace Game { } }; - typedef std::vector VectorSpeeds; + typedef std::vector VectorInterpolatedSpeedMultipliers; typedef std::vector VectorMeasure; @@ -55,10 +55,15 @@ namespace Game { class BMSChartInfo : public ChartInfo { public: - int JudgeRank; + float JudgeRank; + float GaugeTotal; + // neccesary because of regular BMS DEFEXRANK + bool PercentualJudgerank; + // Whether this uses BMSON features. + // (Also makes GaugeTotal a rate instead of an absolute) bool IsBMSON; BMSChartInfo() @@ -67,6 +72,7 @@ namespace Game { JudgeRank = 3; GaugeTotal = -1; IsBMSON = false; + PercentualJudgerank = false; } }; @@ -134,7 +140,7 @@ namespace Game { VectorMeasure Measures; // For Speed changes. - VectorSpeeds Speeds; + VectorInterpolatedSpeedMultipliers InterpoloatedSpeedMultipliers; // Autoplay Sounds std::vector BGMEvents; @@ -154,12 +160,18 @@ namespace Game { // Background/foreground to show when loading. std::string StageFile; + // Genre (Display only, for the most part) + std::string Genre; + // Whether this difficulty uses the scratch channel (being channel/index 0 always used for this) bool Turntable; // Audio slicing data SliceContainer SliceData; + uint32_t GetObjectCount(); + uint32_t GetScoreItemsCount(); + DifficultyLoadInfo() { Turntable = false; @@ -255,6 +267,7 @@ namespace Game { ~Song(); VSRG::Difficulty* GetDifficulty(uint32_t i); + uint8_t GetDifficultyCount() override; }; } diff --git a/src/SongDC.cpp b/src/SongDC.cpp deleted file mode 100644 index a3ec4717..00000000 --- a/src/SongDC.cpp +++ /dev/null @@ -1,321 +0,0 @@ -#include "pch.h" - -#include "GameGlobal.h" -#include "SongDC.h" - -using namespace Game::dotcur; - -Song::Song() -{ - LeadInTime = 0; - MeasureLength = 4; // MeasureLength/4 - Mode = MODE_DOTCUR; -} - -Song::~Song() -{ -} - -Difficulty* Song::GetDifficulty(uint32_t i) -{ - if (i >= Difficulties.size()) - return nullptr; - else - return Difficulties.at(i); -} - -void CalculateBarlineRatios(Song &MySong, Difficulty &Diff) -{ - TimingData &Timing = Diff.Timing; - TimingData &Ratios = Diff.BarlineRatios; - TimingData ChangesInInterval; - - Ratios.clear(); - - for (uint32_t Measure = 0; Measure < Diff.Measures.size(); Measure++) - { - double CurrentBeat = Measure * MySong.MeasureLength; - double NextBeat = (Measure + 1) * MySong.MeasureLength; - double endMeasureTime = TimeAtBeat(Diff.Timing, Diff.Offset, (Measure + 1)*MySong.MeasureLength); - double startMeasureTime = TimeAtBeat(Diff.Timing, Diff.Offset, Measure*MySong.MeasureLength); - double MeasureDuration = endMeasureTime - startMeasureTime; - - GetTimingChangesInInterval(Timing, CurrentBeat, NextBeat, ChangesInInterval); - - if (ChangesInInterval.size() == 0) - { - double Ratio = 1 / MeasureDuration; - TimingSegment New; - - New.Value = Ratio; - New.Time = TimeAtBeat(Diff.Timing, Diff.Offset, CurrentBeat); - - if (!Ratios.size()) - Ratios.push_back(New); - else - { - if (Ratios.back().Value != Ratio) /* Don't add redundant ratios.*/ - Ratios.push_back(New); - } - } - else - { - // Real show time on calculations is here. - for (TimingData::iterator i = ChangesInInterval.begin(); i != ChangesInInterval.end(); i++) - { - TimingSegment New; - double Duration; - double DurationSeconds; - double Fraction; - double Ratio; - - /* Calculate how long this change lasts. */ - - if ((i + 1) != ChangesInInterval.end()) - { - Duration = (i + 1)->Time - i->Time; - } - else - { - Duration = NextBeat - i->Time; - } - - /* Assign the time the change starts - which we can assume i->Time >= CurrentBeat before this. */ - CurrentBeat = i->Time; - - /* Calculate how much the barline would move in 1 second */ - Fraction = Duration / MySong.MeasureLength; - - /* t/b * b = t */ - DurationSeconds = spb(i->Value) * Duration; - - if (DurationSeconds == 0) - continue; - - /* f/d = r/1 (where 1 and d are 1 second and d seconds) */ - Ratio = Fraction / DurationSeconds; - - /* create new segment at i->Time */ - New.Value = Ratio; - New.Time = TimeAtBeat(Diff.Timing, Diff.Offset, CurrentBeat); - - if (!Ratios.size()) - Ratios.push_back(New); - else - { - if (Ratios.back().Value != Ratio) /* Don't add redundant ratios.*/ - Ratios.push_back(New); - } - } - } - } -} - -void Song::Repack() -{ - for (auto Diff = Difficulties.begin(); Diff != Difficulties.end(); Diff++) - { - for (auto Msr = (*Diff)->Measures.begin(); Msr != (*Diff)->Measures.end(); Msr++) - { - for (auto it = Msr->begin(); it != Msr->end(); it++) - { - if (it->GetPosition().x > ScreenDifference) - it->SetPositionX(it->GetPosition().x - ScreenDifference); - } - } - } -} - -int noteSort(const GameObject &A, const GameObject &B) -{ - return A.GetFraction() < B.GetFraction(); -} - -void Song::Process(bool CalculateXPos) -{ - for (std::vector::iterator Diff = Difficulties.begin(); Diff != Difficulties.end(); Diff++) - { - int32_t CurrentMeasure = 0; - for (auto Msr = (*Diff)->Measures.begin(); Msr != (*Diff)->Measures.end(); Msr++) - { - uint32_t CurNote = 0; - - std::sort(Msr->begin(), Msr->end(), noteSort); - - for (std::vector::iterator it = Msr->begin(); it != Msr->end(); it++) - { - // all measures are 4/4 (good enough for now, change both 4s in the future, maybe) - it->beat = ((float)CurrentMeasure * MeasureLength) + ((float)it->Fraction * MeasureLength); - - if (it->hold_duration > 0) - it->endTime = TimeAtBeat((*Diff)->Timing, (*Diff)->Offset, it->beat + it->hold_duration); - - it->startTime = TimeAtBeat((*Diff)->Timing, (*Diff)->Offset, it->beat); - - (*Diff)->Duration = std::max(it->startTime, (*Diff)->Duration); - - double frac = it->Fraction; - - it->Green = 0; - - if (CurrentMeasure % 2) - { - it->SetPositionY(PlayfieldHeight - (PlayfieldHeight * frac) + ScreenOffset); - it->Red = 1; - it->Blue = 0; - } - else - { - it->SetPositionY(PlayfieldHeight * frac + ScreenOffset); - it->Red = 0; - it->Blue = 200.0f / 255.0f; - } - - if (it->endTime > 0) - { - (*Diff)->Duration = std::max(it->endTime, (*Diff)->Duration); - it->Green = 0.5; - } - - if (CalculateXPos) - { - if (it->GetPosition().x > 0) - { - CurNote++; - it->SetPositionX(it->GetPosition().x + ScreenDifference); - } - } - - it->waiting_time = 0.05f * CurNote; - it->fadein_time = 0.15f; - } - CurrentMeasure++; - } - CalculateBarlineRatios(*this, **Diff); - } -} - -int GetFractionKindMeasure(double frac); - -bool Song::Save(const char* Filename) -{ - std::ofstream Out(Filename); - - if (!Out.is_open()) // couldn't open file for writing. - { - return false; - } - - Out << "#NAME:" << SongName << ";\n"; - Out << "#SONG:" << SongFilename << ";\n"; - Out << "#AUTHOR:" << SongAuthor << ";\n"; - Out << "#BACKGROUNDIMAGE:" << BackgroundFilename << ";\n"; - - if (MeasureLength != 4) - Out << "#MLEN:" << MeasureLength << ";\n"; - - for (std::vector::iterator i = Difficulties.begin(); i != Difficulties.end(); i++) - { - if ((*i)->Timing.size() == 1) - Out << "#BPM:" << (*i)->Timing[0].Value << ";\n"; - else - { - Out << "#BPM:"; - - for (uint32_t k = 0; k < (*i)->Timing.size(); k++) - { - Out << (*i)->Timing[k].Time << "=" << (*i)->Timing[k].Value; - if (k + 1 < (*i)->Timing.size()) - Out << ","; - } - - Out << ";\n"; - } - Out << "#OFFSET:" << (*i)->Offset << ";\n"; - - if (LeadInTime) - Out << "#LEADIN:" << LeadInTime << ";\n"; - - Out << "#NOTES:"; - - int MNum = 0; - - // for each measure of this difficulty - for (auto m = (*i)->Measures.begin(); m != (*i)->Measures.end(); m++) - { - dotcur::Measure old = *m; // Copy temporarily - int mAdvance = 192; - - std::sort(m->begin(), m->end(), noteSort); - - // get lowest fraction that is valid - for (uint32_t n = 0; n != m->size(); n++) - { - GameObject &G = (*m)[n]; - if (G.GetPosition().x != 0) - { - int advance = 192 / GetFractionKindMeasure(G.GetFraction()); - mAdvance = std::min(advance, mAdvance); - } - } - - // for each note of this difficulty - - // For the first note, we must leave a gap so we can pretend there was a note "before" - // and that gap between a null note and the first note can be filled in - // if the first note WERE to be fraction == 0 - // limit would be 1, -1 to account for the previous note so it doesn't get filled - // if it weren't it's going to add one so the limit isn't below what we expect - int prevRow = -mAdvance; - for (uint32_t n = 0; n != m->size(); n++) - { - if ((*m)[n].GetPosition().x == 0) - continue; - - // what row is this? - int nRow = (*m)[n].GetFraction() * 192.0; - - // Fill the gap between previous and current note - if (nRow) - { - // how many rows are between this and the previous note - int limit = (nRow - prevRow) / mAdvance; - - // at this point a negative limit will be effectively impossible - limit -= 1; - assert(limit >= 0); - for (int k = 0; k < limit; k++) - Out << "{0}"; - } - - // Fill the current note. - Out << "{" << (int)(*m)[n].GetPosition().x; - - if ((*m)[n].hold_duration) - Out << " " << (*m)[n].hold_duration; - - Out << "}"; - - prevRow = nRow; - } // For each note - - // fill end - // total rows from last to end of measure, minus the last note itself - // it should usually be > 1 when there are notes in the measure - int limit = (192 - prevRow) / mAdvance - 1; - for (int k = 0; k < limit; k++) - Out << "{0}"; - - Out << ",\n"; - - *m = old; // Copy back - Out.flush(); - - MNum++; - } // For each measure - Out << ";\n"; - } // For each difficulty - - return true; -} \ No newline at end of file diff --git a/src/SongDC.h b/src/SongDC.h deleted file mode 100644 index 922b2664..00000000 --- a/src/SongDC.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include "Song.h" -#include "GameObject.h" - -namespace Game { - namespace dotcur - { - typedef std::vector Measure; - - struct Difficulty : public Game::Song::Difficulty - { - // Notes - std::vector Measures; - - // Stores the ratio barline should move at a certain time - TimingData BarlineRatios; - }; - - /* Dotcur Song */ - class Song : public Game::Song - { - public: - Song(); - ~Song(); - - std::vector Difficulties; - - /* chart filename*/ - std::filesystem::path ChartFilename; - - double LeadInTime; - int MeasureLength; - - dotcur::Difficulty* GetDifficulty(uint32_t i); - void Process(bool CalculateXPos = true); - void Repack(); - bool Save(const char* Filename); - }; - } -} \ No newline at end of file diff --git a/src/SongDatabase.cpp b/src/SongDatabase.cpp index a331f7d9..1a3d26dd 100644 --- a/src/SongDatabase.cpp +++ b/src/SongDatabase.cpp @@ -1,12 +1,12 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Logging.h" #include "Song7K.h" #include "SongDatabase.h" -char *DatabaseQuery = +auto DatabaseQuery = "CREATE TABLE IF NOT EXISTS [songdb] (\ [id] INTEGER, \ [songtitle] varchar(260), \ @@ -14,7 +14,6 @@ char *DatabaseQuery = [subtitle] varchar(260), \ [songfilename] varchar(260), \ [songbackground] varchar(260), \ - [mode] INT, \ [previewsong] varchar(260), \ [previewtime] float, \ PRIMARY KEY ([id]));\ @@ -30,46 +29,72 @@ CREATE TABLE IF NOT EXISTS [diffdb] (\ [name] VARCHAR(260), \ [objcount] INT, \ [scoreobjectcount] INT, \ - [holdcount] INT, \ - [notecount] INT, \ [duration] DOUBLE, \ [isvirtual] INTEGER, \ [keys] INTEGER,\ - [bpmtype] INT,\ [level] INT,\ [author] VARCHAR(256),\ + [genre] VARCHAR(256),\ [stagefile] varchar(260));\ CREATE INDEX IF NOT EXISTS song_index ON songfiledb(filename);\ CREATE INDEX IF NOT EXISTS diff_index ON diffdb(diffid, songid, fileid);\ CREATE INDEX IF NOT EXISTS songid_index ON songdb(id);\ "; -const char* InsertSongQuery = "INSERT INTO songdb VALUES (NULL,?,?,?,?,?,?,?,?)"; -const char* InsertDifficultyQuery = "INSERT INTO diffdb VALUES (?,NULL,?,?,?,?,?,?,?,?,?,?,?,?,?)"; -const char* GetFilenameIDQuery = "SELECT id, lastmodified FROM songfiledb WHERE filename=?"; -const char* InsertFilenameQuery = "INSERT INTO songfiledb VALUES (NULL,?,?,?)"; -const char* GetDiffNameQuery = "SELECT name FROM diffdb \ +auto InsertSongQuery = "INSERT INTO songdb VALUES (NULL,$title,$author,$subtitle,$fn,$bg,$psong,$ptime)"; + +auto InsertDifficultyQuery = "INSERT INTO diffdb VALUES (\ + $sid,NULL,$fid,$name,\ + $objcnt,$scoreobjcnt,\ + $dur,$virtual,$keys,$level,\ + $author,$genre,$stagefile)"; + +auto GetFilenameIDQuery = "SELECT id, lastmodified FROM songfiledb WHERE filename=$fn"; + +auto InsertFilenameQuery = "INSERT INTO songfiledb VALUES (NULL,$fn,$lmt,$hash)"; + +auto GetDiffNameQuery = "SELECT name FROM diffdb \ WHERE (diffdb.fileid = (SELECT songfiledb.id FROM songfiledb WHERE filename=?))"; -const char* GetLMTQuery = "SELECT lastmodified FROM songfiledb WHERE filename=?"; -const char* GetSongInfo = "SELECT songtitle, songauthor, songfilename, subtitle, songbackground, mode, previewtime FROM songdb WHERE id=?"; -const char* GetDiffInfo = "SELECT diffid, name, objcount, scoreobjectcount, holdcount, notecount, duration, isvirtual, \ - keys, fileid, bpmtype, level FROM diffdb WHERE songid=?"; -const char* GetFileInfo = "SELECT filename, lastmodified FROM songfiledb WHERE id=?"; -const char* UpdateLMT = "UPDATE songfiledb SET lastmodified=?, hash=? WHERE filename=?"; -const char* UpdateDiff = "UPDATE diffdb SET name=?,objcount=?,scoreobjectcount=?,holdcount=?,notecount=?,\ - duration=?,isvirtual=?,keys=?,bpmtype=?,level=?,author=?,stagefile=? WHERE diffid=?"; - -const char* GetDiffFilename = "SELECT filename FROM songfiledb WHERE (songfiledb.id = (SELECT diffdb.fileid FROM diffdb WHERE diffid=?))"; - -const char* GetDiffIDFileID = "SELECT diffid FROM diffdb \ - WHERE diffdb.fileid=? AND\ - diffdb.name = ?"; - -const char* GetSongIDFromFilename = "SELECT songid FROM diffdb WHERE (diffdb.fileid = (SELECT id FROM songfiledb WHERE filename=?))"; -const char* GetLatestSongID = "SELECT MAX(id) FROM songdb"; -const char* GetAuthorOfDifficulty = "SELECT author FROM diffdb WHERE diffid=?"; -const char* GetPreviewOfSong = "SELECT previewsong, previewtime FROM songdb WHERE id=?"; -const char* sGetStageFile = "SELECT stagefile FROM diffdb WHERE diffid=?"; + +auto GetLMTQuery = "SELECT lastmodified FROM songfiledb WHERE filename=$fn"; + +auto GetSongInfo = "SELECT songtitle, \ + songauthor, songfilename,\ + subtitle, songbackground, \ + previewtime FROM songdb WHERE id=$sid"; + +auto GetDiffInfo = "SELECT diffid, name, objcount,\ + scoreobjectcount, duration, isvirtual, \ + keys, fileid, level, genre FROM diffdb WHERE songid=$sid"; + +auto GetFileInfo = "SELECT filename, lastmodified FROM songfiledb WHERE id=$fid"; + +auto UpdateLMT = "UPDATE songfiledb SET lastmodified=$lmt, hash=$hash WHERE filename=$fn"; + +auto UpdateDiff = "UPDATE diffdb SET name=$name,objcount=$objcnt,scoreobjectcount=$scoreobjcnt,\ + duration=$dur,\ + isvirtual=$virtual,\ + keys=$keys,\ + level=$level,\ + author=$author,\ + stagefile=$stagefile,\ + genre=$genre\ + WHERE diffid=$did"; + +auto GetDiffFilename = "SELECT filename FROM songfiledb WHERE\ + (songfiledb.id = (SELECT diffdb.fileid FROM diffdb WHERE diffid=$did))"; + +auto GetDiffIDFileID = "SELECT diffid FROM diffdb \ + WHERE diffdb.fileid=$fid AND\ + diffdb.name = $name"; + +auto GetSongIDFromFilename = "SELECT songid FROM diffdb WHERE\ + (diffdb.fileid = (SELECT id FROM songfiledb WHERE filename=$fn))"; +auto GetLatestSongID = "SELECT MAX(id) FROM songdb"; +auto GetAuthorOfDifficulty = "SELECT author FROM diffdb WHERE diffid=$did"; +auto GetPreviewOfSong = "SELECT previewsong, previewtime FROM songdb WHERE id=$sid"; +auto sGetStageFile = "SELECT stagefile FROM diffdb WHERE diffid=$did"; +auto GetGenre = "SELECT genre FROM diffdb WHERE diffid=$did"; #define SC(x) \ {ret=x; if(ret!=SQLITE_OK && ret != SQLITE_DONE) \ @@ -96,9 +121,9 @@ SongDatabase::SongDatabase(std::string Database) SC(sqlite3_prepare_v2(db, InsertDifficultyQuery, strlen(InsertDifficultyQuery), &st_DiffInsertQuery, &tail)); SC(sqlite3_prepare_v2(db, GetFilenameIDQuery, strlen(GetFilenameIDQuery), &st_FilenameQuery, &tail)); SC(sqlite3_prepare_v2(db, InsertFilenameQuery, strlen(InsertFilenameQuery), &st_FilenameInsertQuery, &tail)); - SC(sqlite3_prepare_v2(db, GetLMTQuery, strlen(GetLMTQuery), &st_LMTQuery, &tail)); - SC(sqlite3_prepare_v2(db, GetSongInfo, strlen(GetSongInfo), &st_GetSongInfo, &tail)); - SC(sqlite3_prepare_v2(db, GetDiffInfo, strlen(GetDiffInfo), &st_GetDiffInfo, &tail)); + SC(sqlite3_prepare_v2(db, GetLMTQuery, strlen(GetLMTQuery), &stGetLMTQuery, &tail)); + SC(sqlite3_prepare_v2(db, GetSongInfo, strlen(GetSongInfo), &stGetSongInfo, &tail)); + SC(sqlite3_prepare_v2(db, GetDiffInfo, strlen(GetDiffInfo), &stGetDiffInfo, &tail)); SC(sqlite3_prepare_v2(db, GetFileInfo, strlen(GetFileInfo), &st_GetFileInfo, &tail)); SC(sqlite3_prepare_v2(db, UpdateLMT, strlen(UpdateLMT), &st_UpdateLMT, &tail)); SC(sqlite3_prepare_v2(db, GetDiffIDFileID, strlen(GetDiffIDFileID), &st_GetDiffIDFile, &tail)); @@ -109,6 +134,7 @@ SongDatabase::SongDatabase(std::string Database) SC(sqlite3_prepare_v2(db, GetAuthorOfDifficulty, strlen(GetAuthorOfDifficulty), &st_GetDiffAuthor, &tail)); SC(sqlite3_prepare_v2(db, GetPreviewOfSong, strlen(GetPreviewOfSong), &st_GetPreviewInfo, &tail)); SC(sqlite3_prepare_v2(db, sGetStageFile, strlen(sGetStageFile), &st_GetStageFile, &tail)); + SC(sqlite3_prepare_v2(db, GetGenre, strlen(GetGenre), &st_GetDiffGenre, &tail)); } } @@ -120,9 +146,9 @@ SongDatabase::~SongDatabase() sqlite3_finalize(st_DiffInsertQuery); sqlite3_finalize(st_FilenameQuery); sqlite3_finalize(st_FilenameInsertQuery); - sqlite3_finalize(st_LMTQuery); - sqlite3_finalize(st_GetSongInfo); - sqlite3_finalize(st_GetDiffInfo); + sqlite3_finalize(stGetLMTQuery); + sqlite3_finalize(stGetSongInfo); + sqlite3_finalize(stGetDiffInfo); sqlite3_finalize(st_UpdateLMT); sqlite3_finalize(st_GetDiffIDFile); sqlite3_finalize(st_DiffUpdateQuery); @@ -150,15 +176,25 @@ int SongDatabase::InsertFilename(std::filesystem::path Fn) idOut = sqlite3_column_int(st_FilenameQuery, 0); lmt = sqlite3_column_int(st_FilenameQuery, 1); - int lastLmt = Utility::GetLMT(Fn); + int lastLmt = Utility::GetLastModifiedTime(Fn); // Update the last-modified-time of this file, and its hash if it has changed. if (lmt != lastLmt) { std::string Hash = Utility::GetSha256ForFile(Fn); - SC(sqlite3_bind_int(st_UpdateLMT, 1, lastLmt)); - SC(sqlite3_bind_text(st_UpdateLMT, 2, Hash.c_str(), Hash.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_UpdateLMT, 3, u8p.c_str(), u8p.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_int(st_UpdateLMT, + sqlite3_bind_parameter_index(st_UpdateLMT, "$lmt"), + lastLmt)); + + SC(sqlite3_bind_text(st_UpdateLMT, + sqlite3_bind_parameter_index(st_UpdateLMT, "$hash"), + Hash.c_str(), Hash.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_UpdateLMT, + sqlite3_bind_parameter_index(st_UpdateLMT, "$fn"), + u8p.c_str(), u8p.length(), SQLITE_STATIC)); + SCS(sqlite3_step(st_UpdateLMT)); SC(sqlite3_reset(st_UpdateLMT)); } @@ -168,15 +204,28 @@ int SongDatabase::InsertFilename(std::filesystem::path Fn) std::string Hash = Utility::GetSha256ForFile(Fn); // There's no entry, got to insert it. - SC(sqlite3_bind_text(st_FilenameInsertQuery, 1, u8p.c_str(), u8p.length(), SQLITE_STATIC)); - SC(sqlite3_bind_int(st_FilenameInsertQuery, 2, Utility::GetLMT(Fn))); - SC(sqlite3_bind_text(st_FilenameInsertQuery, 3, Hash.c_str(), Hash.length(), SQLITE_STATIC)); - SCS(sqlite3_step(st_FilenameInsertQuery)); // This should not fail. Otherwise, there are bigger problems to worry about... + SC(sqlite3_bind_text(st_FilenameInsertQuery, + sqlite3_bind_parameter_index(st_FilenameInsertQuery, "$fn"), + u8p.c_str(), u8p.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_int(st_FilenameInsertQuery, + sqlite3_bind_parameter_index(st_FilenameInsertQuery, "$lmt"), + Utility::GetLastModifiedTime(Fn))); + + SC(sqlite3_bind_text(st_FilenameInsertQuery, + sqlite3_bind_parameter_index(st_FilenameInsertQuery, "$hash"), + Hash.c_str(), Hash.length(), SQLITE_STATIC)); + + // This should not fail. Otherwise, there are bigger problems to worry about... + SCS(sqlite3_step(st_FilenameInsertQuery)); SC(sqlite3_reset(st_FilenameInsertQuery)); // okay, then return the ID. SC(sqlite3_reset(st_FilenameQuery)); - SC(sqlite3_bind_text(st_FilenameQuery, 1, u8p.c_str(), u8p.length(), SQLITE_STATIC)); + SC(sqlite3_bind_text(st_FilenameQuery, + sqlite3_bind_parameter_index(st_FilenameQuery, "$fn"), + u8p.c_str(), u8p.length(), SQLITE_STATIC)); + sqlite3_step(st_FilenameQuery); idOut = sqlite3_column_int(st_FilenameQuery, 0); } @@ -214,83 +263,71 @@ bool SongDatabase::DifficultyExists(int FileID, std::string DifficultyName, int return r == SQLITE_ROW; } +void SongDatabase::UpdateDiffInternal(int &ret, int DiffID, Game::Song::Difficulty * Diff) +{ + SC(sqlite3_bind_int(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$did"), + DiffID)); + + SC(sqlite3_bind_text(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$name"), + Diff->Name.c_str(), Diff->Name.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_double(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$dur"), + Diff->Duration)); + + auto VDiff = static_cast(Diff); + assert(VDiff->Data != NULL); + + SC(sqlite3_bind_int(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$objcnt"), + VDiff->Data->GetObjectCount())); + + SC(sqlite3_bind_int(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$scoreobjcnt"), + VDiff->Data->GetScoreItemsCount())); + + SC(sqlite3_bind_int(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$virtual"), + VDiff->IsVirtual)); + + SC(sqlite3_bind_int(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$keys"), + VDiff->Channels)); + + SC(sqlite3_bind_int(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$level"), + VDiff->Level)); + + SC(sqlite3_bind_text(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$author"), + VDiff->Author.c_str(), VDiff->Author.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$stagefile"), + VDiff->Data->StageFile.c_str(), VDiff->Data->StageFile.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_DiffUpdateQuery, + sqlite3_bind_parameter_index(st_DiffUpdateQuery, "$genre"), + VDiff->Data->Genre.c_str(), VDiff->Data->Genre.length(), SQLITE_STATIC)); + + + SCS(sqlite3_step(st_DiffUpdateQuery)); + SC(sqlite3_reset(st_DiffUpdateQuery)); +} + // Adds a difficulty to the database, or updates it if it already exists. -void SongDatabase::AddDifficulty(int SongID, std::filesystem::path Filename, Game::Song::Difficulty* Diff, int Mode) +void SongDatabase::AddDifficulty(int SongID, std::filesystem::path Filename, Game::Song::Difficulty* Diff) { int FileID = InsertFilename(Filename); int DiffID; int ret; if (!DifficultyExists(FileID, Diff->Name, &DiffID)) - { - SC(sqlite3_bind_int(st_DiffInsertQuery, 1, SongID)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 2, FileID)); - SC(sqlite3_bind_text(st_DiffInsertQuery, 3, Diff->Name.c_str(), Diff->Name.length(), SQLITE_STATIC)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 4, Diff->TotalObjects)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 5, Diff->TotalScoringObjects)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 6, Diff->TotalHolds)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 7, Diff->TotalNotes)); - SC(sqlite3_bind_double(st_DiffInsertQuery, 8, Diff->Duration)); - - if (Mode == MODE_VSRG) - { - auto VDiff = static_cast(Diff); - - SC(sqlite3_bind_int(st_DiffInsertQuery, 9, VDiff->IsVirtual)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 10, VDiff->Channels)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 11, VDiff->BPMType)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 12, VDiff->Level)); - SC(sqlite3_bind_text(st_DiffInsertQuery, 13, VDiff->Author.c_str(), VDiff->Author.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_DiffInsertQuery, 14, VDiff->Data->StageFile.c_str(), VDiff->Data->StageFile.length(), SQLITE_STATIC)); - } - else if (Mode == MODE_DOTCUR) - { - SC(sqlite3_bind_int(st_DiffInsertQuery, 9, 0)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 10, 0)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 11, 0)); - SC(sqlite3_bind_int(st_DiffInsertQuery, 12, 0)); - SC(sqlite3_bind_text(st_DiffInsertQuery, 13, Diff->Author.c_str(), Diff->Author.length(), SQLITE_STATIC)); - } - - SCS(sqlite3_step(st_DiffInsertQuery)); - SC(sqlite3_reset(st_DiffInsertQuery)); - } - else - { - SC(sqlite3_bind_text(st_DiffUpdateQuery, 1, Diff->Name.c_str(), Diff->Name.length(), SQLITE_STATIC)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 2, Diff->TotalObjects)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 3, Diff->TotalScoringObjects)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 4, Diff->TotalHolds)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 5, Diff->TotalNotes)); - SC(sqlite3_bind_double(st_DiffUpdateQuery, 6, Diff->Duration)); - - if (Mode == MODE_VSRG) - { - auto VDiff = static_cast(Diff); - assert(VDiff->Data != NULL); - - SC(sqlite3_bind_int(st_DiffUpdateQuery, 7, VDiff->IsVirtual)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 8, VDiff->Channels)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 9, VDiff->BPMType)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 10, VDiff->Level)); - SC(sqlite3_bind_text(st_DiffUpdateQuery, 11, VDiff->Author.c_str(), VDiff->Author.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_DiffUpdateQuery, 12, VDiff->Data->StageFile.c_str(), VDiff->Data->StageFile.length(), SQLITE_STATIC)); - } - else if (Mode == MODE_DOTCUR) - { - SC(sqlite3_bind_int(st_DiffUpdateQuery, 7, 0)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 8, 0)); - SC(sqlite3_bind_int(st_DiffUpdateQuery, 9, 0)); - - SC(sqlite3_bind_int(st_DiffUpdateQuery, 10, 0)); - SC(sqlite3_bind_text(st_DiffUpdateQuery, 11, Diff->Author.c_str(), Diff->Author.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_DiffUpdateQuery, 12, "", 0, SQLITE_STATIC)); - } - - SC(sqlite3_bind_int(st_DiffUpdateQuery, 12, DiffID)); - SCS(sqlite3_step(st_DiffUpdateQuery)); - SC(sqlite3_reset(st_DiffUpdateQuery)); - } + InsertDiffInternal(ret, SongID, FileID, Diff); + else // Update + UpdateDiffInternal(ret, DiffID, Diff); if (DiffID == 0) { @@ -303,44 +340,150 @@ void SongDatabase::AddDifficulty(int SongID, std::filesystem::path Filename, Gam Diff->ID = DiffID; } +int SongDatabase::AddSongToDatabase(Game::VSRG::Song * Song) +{ + int ret = 0; + auto u8sfn = Utility::ToU8(Song->SongFilename.wstring()); + auto u8bfn = Utility::ToU8(Song->BackgroundFilename.wstring()); + auto u8pfn = Utility::ToU8(Song->SongPreviewSource.wstring()); + + // Okay then, insert the song. + // So now the latest entry is what we're going to insert difficulties and files into. + SC(sqlite3_bind_text(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$title"), + Song->SongName.c_str(), Song->SongName.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$author"), + Song->SongAuthor.c_str(), Song->SongAuthor.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$subtitle"), + Song->Subtitle.c_str(), Song->Subtitle.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$fn"), + u8sfn.c_str(), u8sfn.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$bg"), + u8bfn.c_str(), u8bfn.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$psong"), + u8pfn.c_str(), u8pfn.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_double(st_SngInsertQuery, + sqlite3_bind_parameter_index(st_SngInsertQuery, "$ptime"), + Song->PreviewTime)); + + SCS(sqlite3_step(st_SngInsertQuery)); + SC(sqlite3_reset(st_SngInsertQuery)); + + sqlite3_step(st_GetLastSongID); + int Out = sqlite3_column_int(st_GetLastSongID, 0); + sqlite3_reset(st_GetLastSongID); + + return Out; +} + +void SongDatabase::InsertDiffInternal(int &ret, int SongID, int FileID, Game::Song::Difficulty * Diff) +{ + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$sid"), + SongID)); + + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$fid"), + FileID)); + + SC(sqlite3_bind_text(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$name"), + Diff->Name.c_str(), Diff->Name.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_double(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$dur"), Diff->Duration)); + + SC(sqlite3_bind_text(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$author"), + Diff->Author.c_str(), Diff->Author.length(), SQLITE_STATIC)); + + auto VDiff = static_cast(Diff); + + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$objcnt"), + VDiff->Data->GetObjectCount())); + + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$scoreobjcnt"), + VDiff->Data->GetScoreItemsCount())); + + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$virtual"), + VDiff->IsVirtual)); + + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$keys"), + VDiff->Channels)); + + SC(sqlite3_bind_int(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$level"), + VDiff->Level)); + + SC(sqlite3_bind_text(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$genre"), + VDiff->Data->Genre.c_str(), VDiff->Data->Genre.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_DiffInsertQuery, + sqlite3_bind_parameter_index(st_DiffInsertQuery, "$stagefile"), + VDiff->Data->StageFile.c_str(), VDiff->Data->StageFile.length(), SQLITE_STATIC)); + + + SCS(sqlite3_step(st_DiffInsertQuery)); + SC(sqlite3_reset(st_DiffInsertQuery)); +} + std::filesystem::path SongDatabase::GetDifficultyFilename(int ID) { - int ret; - SC(sqlite3_bind_int(st_GetDiffFilename, 1, ID)); - SCS(sqlite3_step(st_GetDiffFilename)); + int ret; + SC(sqlite3_bind_int(st_GetDiffFilename, 1, ID)); + SCS(sqlite3_step(st_GetDiffFilename)); #ifdef _WIN32 - std::filesystem::path out = Utility::Widen((char*)sqlite3_column_text(st_GetDiffFilename, 0)); + std::filesystem::path out = Utility::Widen((char*)sqlite3_column_text(st_GetDiffFilename, 0)); #else std::filesystem::path out = (char*)sqlite3_column_text(st_GetDiffFilename, 0); #endif - SC(sqlite3_reset(st_GetDiffFilename)); - return out; + SC(sqlite3_reset(st_GetDiffFilename)); + return out; } bool SongDatabase::CacheNeedsRenewal(std::filesystem::path Dir) { // must match what we put at InsertFilename time, so turn into absolute path on both places! std::string u8p = Utility::ToU8(std::filesystem::absolute(Dir).wstring()); - int CurLMT = Utility::GetLMT(Dir); - bool NeedsRenewal; - int res, ret; - - SC(sqlite3_bind_text(st_LMTQuery, 1, u8p.c_str(), u8p.length(), SQLITE_STATIC)); - res = sqlite3_step(st_LMTQuery); - - if (res == SQLITE_ROW) // entry exists - { - int OldLMT = sqlite3_column_int(st_LMTQuery, 0); - bool IsLMTCurrent = (CurLMT == OldLMT); // file was not modified since last time - NeedsRenewal = !IsLMTCurrent; - } - else - { + int CurLMT = Utility::GetLastModifiedTime(Dir); + bool NeedsRenewal; + int res, ret; + + SC(sqlite3_bind_text(stGetLMTQuery, + sqlite3_bind_parameter_index(stGetLMTQuery, "$fn"), + u8p.c_str(), u8p.length(), SQLITE_STATIC)); + + res = sqlite3_step(stGetLMTQuery); + + if (res == SQLITE_ROW) // entry exists + { + int OldLMT = sqlite3_column_int(stGetLMTQuery, 0); + bool IsLMTCurrent = (CurLMT == OldLMT); // file was not modified since last time + NeedsRenewal = !IsLMTCurrent; + } + else + { NeedsRenewal = true; } - SC(sqlite3_reset(st_LMTQuery)); + SC(sqlite3_reset(stGetLMTQuery)); return NeedsRenewal; } @@ -361,7 +504,10 @@ std::string SongDatabase::GetArtistForDifficulty(int ID) int rs; std::string out; - sqlite3_bind_int(st_GetDiffAuthor, 1, ID); + sqlite3_bind_int(st_GetDiffAuthor, + sqlite3_bind_parameter_index(st_GetDiffAuthor, "$did"), + ID); + rs = sqlite3_step(st_GetDiffAuthor); if (rs == SQLITE_ROW) @@ -371,68 +517,84 @@ std::string SongDatabase::GetArtistForDifficulty(int ID) return out; } +std::string SongDatabase::GetGenreForDifficulty(int DiffID) +{ + std::string out; + + sqlite3_bind_int(st_GetDiffGenre, + sqlite3_bind_parameter_index(st_GetDiffGenre, "$did"), + DiffID); + + if (sqlite3_step(st_GetDiffGenre) == SQLITE_ROW) + out = (char*)sqlite3_column_text(st_GetDiffGenre, 0); + + sqlite3_reset(st_GetDiffGenre); + return out; +} + #ifdef _WIN32 -#define _T(x) Utility::Widen((char*)x) +#define _W(x) Utility::Widen((char*)x) #else -#define _T(x) ((char*)x) +#define _W(x) ((char*)x) #endif void SongDatabase::GetSongInformation(int ID, Game::VSRG::Song* Out) { int ret; - SC(sqlite3_bind_int(st_GetSongInfo, 1, ID)); - ret = sqlite3_step(st_GetSongInfo); + + SC(sqlite3_bind_int(stGetSongInfo, + sqlite3_bind_parameter_index(stGetSongInfo, "$sid"), + ID)); + + ret = sqlite3_step(stGetSongInfo); if (ret != SQLITE_ROW) { - Log::Printf("SongDatabase::GetSongInformation: Chart %d does not exist.\n", ID); + Log::Printf("SongDatabase::GetSongInformation: Song %d does not exist.\n", ID); return; } // Main metadata is up in this query. - Out->SongName = (char*)sqlite3_column_text(st_GetSongInfo, 0); - Out->SongAuthor = (char*)sqlite3_column_text(st_GetSongInfo, 1); - Out->SongFilename = _T(sqlite3_column_text(st_GetSongInfo, 2)); - Out->Subtitle = (char*)sqlite3_column_text(st_GetSongInfo, 3); - Out->BackgroundFilename = _T(sqlite3_column_text(st_GetSongInfo, 4)); + // oh god there is no better way without keeping track of column names for a + // statement by yourself... + Out->SongName = (char*)sqlite3_column_text(stGetSongInfo, 0); + Out->SongAuthor = (char*)sqlite3_column_text(stGetSongInfo, 1); + Out->SongFilename = _W(sqlite3_column_text(stGetSongInfo, 2)); + Out->Subtitle = (char*)sqlite3_column_text(stGetSongInfo, 3); + Out->BackgroundFilename = _W(sqlite3_column_text(stGetSongInfo, 4)); Out->ID = ID; - int mode = sqlite3_column_int(st_GetSongInfo, 5); - Out->PreviewTime = sqlite3_column_double(st_GetSongInfo, 6); - - SC(sqlite3_reset(st_GetSongInfo)); + Out->PreviewTime = sqlite3_column_double(stGetSongInfo, 5); - if (mode != MODE_VSRG) - return; // Sowwy. + SC(sqlite3_reset(stGetSongInfo)); // Now, difficulty information. - SC(sqlite3_bind_int(st_GetDiffInfo, 1, ID)); - while (sqlite3_step(st_GetDiffInfo) != SQLITE_DONE) + SC(sqlite3_bind_int(stGetDiffInfo, + sqlite3_bind_parameter_index(stGetDiffInfo, "$sid"), + ID)); + + while (sqlite3_step(stGetDiffInfo) != SQLITE_DONE) { auto Diff = std::make_shared(); // diffid associated data - Diff->ID = sqlite3_column_int(st_GetDiffInfo, 0); - Diff->Name = (char*)sqlite3_column_text(st_GetDiffInfo, 1); - Diff->TotalObjects = sqlite3_column_int(st_GetDiffInfo, 2); - Diff->TotalScoringObjects = sqlite3_column_int(st_GetDiffInfo, 3); - Diff->TotalHolds = sqlite3_column_int(st_GetDiffInfo, 4); - Diff->TotalNotes = sqlite3_column_int(st_GetDiffInfo, 5); - Diff->Duration = sqlite3_column_double(st_GetDiffInfo, 6); - Diff->IsVirtual = (sqlite3_column_int(st_GetDiffInfo, 7) == 1); - Diff->Channels = sqlite3_column_int(st_GetDiffInfo, 8); - - int colInt = sqlite3_column_int(st_GetDiffInfo, 10); - Diff->BPMType = (Game::VSRG::Difficulty::ETimingType)colInt; + Diff->ID = sqlite3_column_int(stGetDiffInfo, 0); + Diff->Name = (char*)sqlite3_column_text(stGetDiffInfo, 1); + Diff->Duration = sqlite3_column_double(stGetDiffInfo, 4); + Diff->IsVirtual = (sqlite3_column_int(stGetDiffInfo, 5) == 1); + Diff->Channels = sqlite3_column_int(stGetDiffInfo, 6); // We don't include author information to force querying it from the database. // Diff->Author - Diff->Level = sqlite3_column_int(st_GetDiffInfo, 11); + Diff->Level = sqlite3_column_int(stGetDiffInfo, 8); // File ID associated data - int FileID = sqlite3_column_int(st_GetDiffInfo, 9); + int FileID = sqlite3_column_int(stGetDiffInfo, 7); + + SC(sqlite3_bind_int(st_GetFileInfo, + sqlite3_bind_parameter_index(st_GetFileInfo, "$fid"), + FileID)); - SC(sqlite3_bind_int(st_GetFileInfo, 1, FileID)); sqlite3_step(st_GetFileInfo); // This copy is dangerous, so we should reset the info _before_ we try to copy. @@ -450,7 +612,7 @@ void SongDatabase::GetSongInformation(int ID, Game::VSRG::Song* Out) catch (std::exception &e) { // We failed copying this thing - clean up and rethrow. SC(sqlite3_reset(st_GetFileInfo)); - SC(sqlite3_reset(st_GetDiffInfo)); + SC(sqlite3_reset(stGetDiffInfo)); throw e; } @@ -458,15 +620,18 @@ void SongDatabase::GetSongInformation(int ID, Game::VSRG::Song* Out) Out->Difficulties.push_back(Diff); } - SC(sqlite3_reset(st_GetDiffInfo)); + SC(sqlite3_reset(stGetDiffInfo)); } -int SongDatabase::GetSongIDForFile(std::filesystem::path File, Game::VSRG::Song* In) +int SongDatabase::GetSongIDForFile(std::filesystem::path File) { int ret; int Out = -1; std::string u8path = Utility::ToU8(std::filesystem::absolute(File).wstring()); - SC(sqlite3_bind_text(st_GetSIDFromFilename, 1, u8path.c_str(), u8path.length(), SQLITE_STATIC)); + + SC(sqlite3_bind_text(st_GetSIDFromFilename, + sqlite3_bind_parameter_index(st_GetSIDFromFilename, "$fn"), + u8path.c_str(), u8path.length(), SQLITE_STATIC)); int r = sqlite3_step(st_GetSIDFromFilename); if (r == SQLITE_ROW) @@ -474,42 +639,19 @@ int SongDatabase::GetSongIDForFile(std::filesystem::path File, Game::VSRG::Song* // We found a song with ID and everything.. Out = sqlite3_column_int(st_GetSIDFromFilename, 0); } - else - { - assert(In); // Okay, this is a query isn't it? Why doesn't the song exist? - - auto u8sfn = Utility::ToU8(In->SongFilename.wstring()); - auto u8bfn = Utility::ToU8(In->BackgroundFilename.wstring()); - auto u8pfn = Utility::ToU8(In->SongPreviewSource.wstring()); - - // Okay then, insert the song. - // So now the latest entry is what we're going to insert difficulties and files into. - SC(sqlite3_bind_text(st_SngInsertQuery, 1, In->SongName.c_str(), In->SongName.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_SngInsertQuery, 2, In->SongAuthor.c_str(), In->SongAuthor.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_SngInsertQuery, 3, In->Subtitle.c_str(), In->Subtitle.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_SngInsertQuery, 4, u8sfn.c_str(), u8sfn.length(), SQLITE_STATIC)); - SC(sqlite3_bind_text(st_SngInsertQuery, 5, u8bfn.c_str(), u8bfn.length(), SQLITE_STATIC)); - SC(sqlite3_bind_int(st_SngInsertQuery, 6, In->Mode)); - SC(sqlite3_bind_text(st_SngInsertQuery, 7, u8pfn.c_str(), u8pfn.length(), SQLITE_STATIC)); - SC(sqlite3_bind_double(st_SngInsertQuery, 8, In->PreviewTime)); - - SCS(sqlite3_step(st_SngInsertQuery)); - SC(sqlite3_reset(st_SngInsertQuery)); - - sqlite3_step(st_GetLastSongID); - Out = sqlite3_column_int(st_GetLastSongID, 0); - sqlite3_reset(st_GetLastSongID); - } sqlite3_reset(st_GetSIDFromFilename); - if (In) In->ID = Out; return Out; } std::string SongDatabase::GetStageFile(int DiffID) { int ret; - SC(sqlite3_bind_int(st_GetStageFile, 1, DiffID)); + + SC(sqlite3_bind_int(st_GetStageFile, + sqlite3_bind_parameter_index(st_GetStageFile, "$did"), + DiffID)); + SCS(sqlite3_step(st_GetStageFile)); const char* sOut = (const char*)sqlite3_column_text(st_GetStageFile, 0); @@ -522,7 +664,10 @@ std::string SongDatabase::GetStageFile(int DiffID) void SongDatabase::GetPreviewInfo(int SongID, std::string &Filename, float &PreviewStart) { int ret; - SC(sqlite3_bind_int(st_GetPreviewInfo, 1, SongID)); + SC(sqlite3_bind_int(st_GetPreviewInfo, + sqlite3_bind_parameter_index(st_GetPreviewInfo, "$sid"), + SongID)); + SCS(sqlite3_step(st_GetPreviewInfo)); const char* sOut = (const char*)sqlite3_column_text(st_GetPreviewInfo, 0); diff --git a/src/SongDatabase.h b/src/SongDatabase.h index ea024d2d..362f3f64 100644 --- a/src/SongDatabase.h +++ b/src/SongDatabase.h @@ -20,10 +20,10 @@ class SongDatabase *st_FilenameQuery, *st_FilenameInsertQuery, *st_DiffIDQuery, - *st_LMTQuery, + *stGetLMTQuery, *st_DelDiffsQuery, - *st_GetSongInfo, - *st_GetDiffInfo, + *stGetSongInfo, + *stGetDiffInfo, *st_GetFileInfo, *st_UpdateLMT, *st_GetDiffIDFile, @@ -32,12 +32,16 @@ class SongDatabase *st_GetSIDFromFilename, *st_GetLastSongID, *st_GetDiffAuthor, + *st_GetDiffGenre, *st_GetPreviewInfo, *st_GetStageFile; // Returns the ID. int InsertFilename(std::filesystem::path Fn); bool DifficultyExists(int FileID, std::string DifficultyName, int *IDOut = NULL); + + void UpdateDiffInternal(int &ret, int DiffID, Game::Song::Difficulty * Diff); + void InsertDiffInternal(int &ret, int SongID, int FileID, Game::Song::Difficulty * Diff); public: SongDatabase(std::string Database); @@ -45,16 +49,19 @@ class SongDatabase void ClearDifficulties(int SongID); bool CacheNeedsRenewal(std::filesystem::path Dir); - void AddDifficulty(int SongID, std::filesystem::path Filename, Game::Song::Difficulty* Diff, int Mode); + void AddDifficulty(int SongID, std::filesystem::path Filename, Game::Song::Difficulty* Diff); + + int AddSongToDatabase(Game::VSRG::Song *Song); void GetPreviewInfo(int SongID, std::string &Filename, float &PreviewStart); // Difficulty information std::filesystem::path GetDifficultyFilename(int DiffID); std::string GetArtistForDifficulty(int DiffID); + std::string GetGenreForDifficulty(int DiffID); std::string GetStageFile(int DiffID); - int GetSongIDForFile(std::filesystem::path File, Game::VSRG::Song* In); + int GetSongIDForFile(std::filesystem::path File); void GetSongInformation(int ID, Game::VSRG::Song* Out); diff --git a/src/SongList.cpp b/src/SongList.cpp index f07c97f1..8a508800 100644 --- a/src/SongList.cpp +++ b/src/SongList.cpp @@ -2,10 +2,9 @@ #include "GameGlobal.h" #include "Song.h" +#include "Song7K.h" #include "SongList.h" -#include "Song7K.h" -#include "SongDC.h" #include "SongLoader.h" ListEntry::ListEntry() { @@ -14,6 +13,7 @@ ListEntry::ListEntry() { SongList::SongList(SongList* Parent) : mParent(Parent) + , IsInUse(false) { } @@ -21,6 +21,39 @@ SongList::~SongList() { } +void SongList::Clear() +{ + mChildren.clear(); +} + +void SongList::SetInUse(bool inuse) +{ + IsInUse = inuse; +} + +bool SongList::InUse() +{ + return IsInUse; +} + +void SongList::ClearEmpty() +{ + for (auto it = mChildren.begin(); it != mChildren.end(); ) { + bool increase = true; + + if (it->Kind == it->Directory) { + auto list = std::static_pointer_cast(it->Data); + if (list->GetNumEntries() == 0 && !list->InUse()) { + it = mChildren.erase(it); + increase = false; + } + } + + if (increase) + ++it; + } +} + void SongList::AddSong(std::shared_ptr Song) { ListEntry NewEntry; @@ -40,7 +73,11 @@ const std::vector& SongList::GetEntries() return mChildren; } -void SongList::AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir, std::string Name, bool VSRGActive, bool DotcurActive) +void SongList::AddNamedDirectory( + std::mutex &loadMutex, + SongLoader *Loader, + std::filesystem::path Dir, + std::string Name) { bool EntryWasPushed = false; SongList* NewList = new SongList(this); @@ -52,22 +89,20 @@ void SongList::AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std: NewEntry.Data = std::shared_ptr(NewList); std::vector Songs7K; - std::vector SongsDC; std::vector Listing; + // boost throws with nonexisting directories + if (!std::filesystem::exists(Dir)) return; + for (auto i : std::filesystem::directory_iterator (Dir)) { if (i == "." || i == "..") continue; if (!std::filesystem::is_directory(i.path())) continue; - if (VSRGActive) - Loader->LoadSong7KFromDir(i, Songs7K); - - if (DotcurActive) - Loader->LoadSongDCFromDir(i.path().string(), SongsDC); + Loader->LoadSong7KFromDir(i, Songs7K); - if (!SongsDC.size() && !Songs7K.size()) // No songs, so, time to recursively search. + if (!Songs7K.size()) // No songs, so, time to recursively search. { if (!EntryWasPushed) { @@ -76,11 +111,11 @@ void SongList::AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std: EntryWasPushed = true; } - NewList->AddDirectory(loadMutex, Loader, i, VSRGActive, DotcurActive); + NewList->AddDirectory(loadMutex, Loader, i); { std::unique_lock lock(loadMutex); - if (!NewList->GetNumEntries()) + if (!NewList->GetNumEntries() && !NewList->InUse()) { if (mChildren.size()) mChildren.erase(mChildren.end() - 1); @@ -90,7 +125,7 @@ void SongList::AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std: } else { - size_t tSize = Songs7K.size() + SongsDC.size(); + auto tSize = Songs7K.size(); if (Songs7K.size()) { @@ -106,20 +141,6 @@ void SongList::AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std: Songs7K.clear(); } - if (SongsDC.size()) - { - std::unique_lock lock(loadMutex); - - for (auto j = SongsDC.begin(); - j != SongsDC.end(); - j++) - { - NewList->AddSong(std::shared_ptr(*j)); - } - - SongsDC.clear(); - } - if (tSize > 0) // There's a song in here. { if (!EntryWasPushed) @@ -133,9 +154,9 @@ void SongList::AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std: } } -void SongList::AddDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir, bool VSRGActive, bool DotcurActive) +void SongList::AddDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir) { - AddNamedDirectory(loadMutex, Loader, Dir, Utility::ToU8(Dir.filename().wstring()), VSRGActive, DotcurActive); + AddNamedDirectory(loadMutex, Loader, Dir, Utility::ToU8(Dir.filename().wstring())); } void SongList::AddVirtualDirectory(std::string NewEntryName, Game::Song* List, int Count) @@ -166,10 +187,10 @@ std::shared_ptr SongList::GetListEntry(unsigned int Entry) return std::static_pointer_cast (mChildren[Entry].Data); } -std::shared_ptr SongList::GetSongEntry(unsigned int Entry) +std::shared_ptr SongList::GetSongEntry(unsigned int Entry) { if (!IsDirectory(Entry)) - return std::static_pointer_cast (mChildren[Entry].Data); + return std::static_pointer_cast (mChildren[Entry].Data); else return nullptr; } @@ -249,24 +270,10 @@ void SongList::SortBy(ESortCriteria criteria) { auto dur = [](std::shared_ptr a) { - if (a->Mode == MODE_DOTCUR) - { -#ifdef DOTCUR_ENABLED - auto sng = std::static_pointer_cast(a); - auto dif = sng->GetDifficulty(0); - if (dif) return dif->Duration; -#else - return -1.0; -#endif - } - - if (a->Mode == MODE_VSRG) - { - auto sng = std::static_pointer_cast(a); - auto dif = sng->GetDifficulty(0); - if (dif) return dif->Duration; - } - + auto sng = std::static_pointer_cast(a); + auto dif = sng->GetDifficulty(0); + if (dif) return dif->Duration; + return 0.0; }; @@ -278,33 +285,18 @@ void SongList::SortBy(ESortCriteria criteria) return lena < lenb; }); break; - case SORT_MINNPS: + case SORT_MINLEVEL: SortByFn([](const ListEntry&A, const ListEntry&B) { auto nps = [](std::shared_ptr a) { - if (a->Mode == MODE_DOTCUR) - { - auto sng = std::static_pointer_cast(a); - auto minnps = std::numeric_limits::infinity(); - for (auto diff : sng->Difficulties) { - minnps = std::min(minnps, float(diff->TotalNotes / diff->Duration)); - } - - return minnps; + auto sng = std::static_pointer_cast(a); + auto minnps = 10000000; + for (auto diff : sng->Difficulties) { + minnps = std::min(minnps, diff->Level); } - if (a->Mode == MODE_VSRG) - { - auto sng = std::static_pointer_cast(a); - auto minnps = std::numeric_limits::infinity(); - for (auto diff : sng->Difficulties) { - minnps = std::min(minnps, float(diff->TotalNotes / diff->Duration)); - } - - return minnps; - } - return 0.0f; + return minnps; }; auto a = std::static_pointer_cast(A.Data); @@ -315,34 +307,18 @@ void SongList::SortBy(ESortCriteria criteria) return npsa < npsb; }); break; - case SORT_MAXNPS: + case SORT_MAXLEVEL: SortByFn([](const ListEntry&A, const ListEntry&B) { auto nps = [](std::shared_ptr a) { - if (a->Mode == MODE_DOTCUR) - { - auto sng = std::static_pointer_cast(a); - auto maxnps = -std::numeric_limits::infinity(); - for (auto diff : sng->Difficulties) { - maxnps = std::max(maxnps, float(diff->TotalNotes / diff->Duration)); - } - - return maxnps; + auto sng = std::static_pointer_cast(a); + auto maxnps = -10000000; + for (auto diff : sng->Difficulties) { + maxnps = std::max(maxnps, diff->Level); } - if (a->Mode == MODE_VSRG) - { - auto sng = std::static_pointer_cast(a); - auto maxnps = -std::numeric_limits::infinity(); - for (auto diff : sng->Difficulties) { - maxnps = std::max(maxnps, float(diff->TotalNotes / diff->Duration)); - } - - return maxnps; - } - - return 0.0f; + return maxnps; }; auto a = std::static_pointer_cast(A.Data); diff --git a/src/SongList.h b/src/SongList.h index ecb8ad87..e83066dc 100644 --- a/src/SongList.h +++ b/src/SongList.h @@ -20,8 +20,8 @@ enum ESortCriteria SORT_TITLE, SORT_AUTHOR, SORT_LENGTH, - SORT_MAXNPS, - SORT_MINNPS, + SORT_MAXLEVEL, + SORT_MINLEVEL, SORT_COUNT }; @@ -29,15 +29,22 @@ class SongList { SongList* mParent; std::vector mChildren; - + std::atomic IsInUse; void SortByFn(std::function fn); public: SongList(SongList *Parent = nullptr); ~SongList(); - void AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir, std::string Name, bool VSRGActive, bool DotcurActive); - void AddDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir, bool VSRGActive, bool DotcurActive); + void Clear(); + + void SetInUse(bool use); + bool InUse(); + + void ClearEmpty(); + + void AddNamedDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir, std::string Name); + void AddDirectory(std::mutex &loadMutex, SongLoader *Loader, std::filesystem::path Dir); void AddVirtualDirectory(std::string NewEntryName, Game::Song* List, int Count); void AddSong(std::shared_ptr Song); @@ -47,7 +54,7 @@ class SongList // if false, it's a song bool IsDirectory(unsigned int Entry) const; std::shared_ptr GetListEntry(unsigned int Entry); - std::shared_ptr GetSongEntry(unsigned int Entry); + std::shared_ptr GetSongEntry(unsigned int Entry); std::string GetEntryTitle(unsigned int Entry); unsigned int GetNumEntries() const; diff --git a/src/SongLoader.cpp b/src/SongLoader.cpp index 610379be..041913e4 100644 --- a/src/SongLoader.cpp +++ b/src/SongLoader.cpp @@ -5,11 +5,9 @@ #include "Song.h" #include "SongDatabase.h" -#include "SongDC.h" #include "Song7K.h" #include "SongLoader.h" #include "NoteLoader7K.h" -#include "NoteLoaderDC.h" using namespace Game; @@ -35,68 +33,6 @@ SongLoader::SongLoader(SongDatabase* Database) DB = Database; } -void SongLoader::LoadSongDCFromDir(std::filesystem::path songPath, std::vector &VecOut) -{ -#ifdef DOTCUR_ENABLED - bool FoundDCF = false; - auto Listing = Utility::GetFileListing(songPath); - - // First, search for .dcf files. - for (auto i: Listing) - { - if (i.extension() != ".dcf") continue; - - // found a .dcf- load it. - dotcur::Song *New = NoteLoaderDC::LoadObjectsFromFile(i, songPath); - if (New) - { - New->SongDirectory = songPath; - New->ChartFilename = i; - VecOut.push_back(New); - FoundDCF = true; - } - } - - // If we didn't find any chart, add this song to the list as edit-only. - if (!FoundDCF && (Configuration::GetConfigf("OggListing") != 0)) - { - dotcur::Song *NewS = nullptr; - std::string PotentialBG, PotentialBGRelative; - - Listing = Utility::GetFileListing(songPath); - - for (auto i : Listing) - { - std::string Ext = i.extension().string(); - if (Ext == ".ogg") - { - bool UsesFilename = false; - NewS = new dotcur::Song(); - NewS->SongDirectory = songPath; - NewS->SongName = i.filename().string(); - UsesFilename = true; - - NewS->SongFilename = i.filename().string(); - // todo: dcf - - // NewS->ChartFilename = UsesFilename ? Utility::RemoveExtension(NewS->SongName) + ".dcf" : NewS->SongName + ".dcf"; - VecOut.push_back(NewS); - } - else if (Ext == ".png" || Ext == ".jpg") - { - PotentialBG = i.string(); - PotentialBGRelative = i.filename().string(); - } - } - - if (NewS) - { - NewS->BackgroundFilename = PotentialBG; - } - } -#endif -} - bool VSRGValidExtension(std::wstring Ext) { for (int i = 0; i < sizeof(LoadersVSRG) / sizeof(loaderVSRGEntry_t); i++) @@ -205,7 +141,7 @@ std::shared_ptr LoadSong7KFromFilename(std::filesystem::path F return nullptr; } -void VSRGUpdateDatabaseDifficulties(SongDatabase* DB, Game::VSRG::Song *New) +void PushSongToDatabase(SongDatabase* DB, Game::VSRG::Song *New) { int ID; @@ -213,19 +149,24 @@ void VSRGUpdateDatabaseDifficulties(SongDatabase* DB, Game::VSRG::Song *New) return; // All difficulties have the same song ID, so.. - ID = DB->GetSongIDForFile(New->Difficulties.at(0)->Filename, New); + ID = DB->GetSongIDForFile(New->Difficulties.at(0)->Filename); + if (ID == -1) { + ID = DB->AddSongToDatabase(New); + } + + New->ID = ID; // Do the update, with the either new or old difficulty. for (auto k = New->Difficulties.begin(); k != New->Difficulties.end(); ++k) { - DB->AddDifficulty(ID, (*k)->Filename, k->get(), MODE_VSRG); + DB->AddDifficulty(ID, (*k)->Filename, k->get()); (*k)->Destroy(); } } -void PushVSRGSong(std::vector &VecOut, Game::VSRG::Song* Sng) +void AddSongToList(std::vector &VecOut, Game::VSRG::Song* Sng) { if (Sng->Difficulties.size()) VecOut.push_back(Sng); @@ -350,8 +291,8 @@ void SongLoader::LoadSong7KFromDir(std::filesystem::path songPath, std::vectorSongDirectory = SongDirectory; } @@ -371,8 +312,8 @@ void SongLoader::LoadSong7KFromDir(std::filesystem::path songPath, std::vectorSongDirectory = SongDirectory; } @@ -382,18 +323,18 @@ void SongLoader::LoadSong7KFromDir(std::filesystem::path songPath, std::vectorsecond); - PushVSRGSong(VecOut, i->second); + PushSongToDatabase(DB, i->second); + AddSongToList(VecOut, i->second); } - VSRGUpdateDatabaseDifficulties(DB, OJNSong); - PushVSRGSong(VecOut, OJNSong); + PushSongToDatabase(DB, OJNSong); + AddSongToList(VecOut, OJNSong); - VSRGUpdateDatabaseDifficulties(DB, osuSong); - PushVSRGSong(VecOut, osuSong); + PushSongToDatabase(DB, osuSong); + AddSongToList(VecOut, osuSong); - VSRGUpdateDatabaseDifficulties(DB, smSong); - PushVSRGSong(VecOut, smSong); + PushSongToDatabase(DB, smSong); + AddSongToList(VecOut, smSong); } else // We can reload from cache. We do this on a per-file basis. { @@ -406,7 +347,7 @@ void SongLoader::LoadSong7KFromDir(std::filesystem::path songPath, std::vectorGetSongIDForFile(File, nullptr); + int CurrentID = DB->GetSongIDForFile(File); if (CurrentID != ID) { ID = CurrentID; @@ -430,7 +371,7 @@ void SongLoader::LoadSong7KFromDir(std::filesystem::path songPath, std::vectorSongDirectory)); - PushVSRGSong(VecOut, New); + AddSongToList(VecOut, New); Log::Logf(" ok\n"); } catch (std::exception &e) { @@ -440,18 +381,6 @@ void SongLoader::LoadSong7KFromDir(std::filesystem::path songPath, std::vector &OutVec, std::filesystem::path Dir) -{ - std::vector Listing = Utility::GetFileListing(Dir); - - for (auto i: Listing) - { - Log::Printf("%s... ", i.c_str()); - LoadSongDCFromDir(i, OutVec); - Log::Printf("ok\n"); - } -} - void SongLoader::GetSongList7K(std::vector &OutVec,std::filesystem::path Dir) { std::vector Listing = Utility::GetFileListing(Dir); @@ -464,7 +393,7 @@ void SongLoader::GetSongList7K(std::vector &OutVec,std::filesystem: } } -std::shared_ptr SongLoader::LoadFromMeta(const Game::VSRG::Song* Meta, std::shared_ptr &CurrentDiff, std::filesystem::path &FilenameOut, uint8_t &Index) +std::shared_ptr SongLoader::LoadFromMeta(const Game::VSRG::Song* Meta, std::shared_ptr CurrentDiff, std::filesystem::path &FilenameOut, uint8_t &Index) { std::shared_ptr Out; @@ -482,7 +411,7 @@ std::shared_ptr SongLoader::LoadFromMeta(const Game::VSRG::Song* Met bool DifficultyFound = false; for (auto k : Out->Difficulties) { - DB->AddDifficulty(Meta->ID, k->Filename, k.get(), MODE_VSRG); + DB->AddDifficulty(Meta->ID, k->Filename, k.get()); if (k->ID == CurrentDiff->ID) // We've got a match; move onward. { CurrentDiff = k; diff --git a/src/SongLoader.h b/src/SongLoader.h index 75808e63..a8b39bdd 100644 --- a/src/SongLoader.h +++ b/src/SongLoader.h @@ -10,10 +10,8 @@ class SongLoader SongLoader(SongDatabase* usedDatabase); void LoadSong7KFromDir(std::filesystem::path songPath, std::vector &VecOut); - void LoadSongDCFromDir(std::filesystem::path songPath, std::vector &VecOut); - void GetSongListDC(std::vector &OutVec, std::filesystem::path Dir); void GetSongList7K(std::vector &OutVec, std::filesystem::path Dir); - std::shared_ptr LoadFromMeta(const Game::VSRG::Song* Meta, std::shared_ptr& CurrentDiff, std::filesystem::path& FilenameOut, uint8_t& Index); + std::shared_ptr LoadFromMeta(const Game::VSRG::Song* Meta, std::shared_ptr CurrentDiff, std::filesystem::path& FilenameOut, uint8_t& Index); }; std::shared_ptr LoadSong7KFromFilename(std::filesystem::path Filename, std::filesystem::path Prefix, Game::VSRG::Song *Sng); diff --git a/src/SongTiming.cpp b/src/SongTiming.cpp index fce52452..4ea629b6 100644 --- a/src/SongTiming.cpp +++ b/src/SongTiming.cpp @@ -2,20 +2,11 @@ #include "Song.h" -int gcd(int a, int b) -{ - if (b == 0) return a; - else return gcd(b, a % b); -} -int lcm(int a, int b) -{ - return a * b / gcd(a, b); -} int LCM(const std::vector &Set) { - return std::accumulate(Set.begin() + 1, Set.end(), *Set.begin(), lcm); + return std::accumulate(Set.begin() + 1, Set.end(), *Set.begin(), lcm); } int SectionIndex(const TimingData &Timing, double Beat) diff --git a/src/SongWheel.cpp b/src/SongWheel.cpp index a41a6ced..76ee671c 100644 --- a/src/SongWheel.cpp +++ b/src/SongWheel.cpp @@ -3,7 +3,6 @@ #include "GameGlobal.h" #include "GameState.h" #include "Logging.h" -#include "SongDC.h" #include "Song7K.h" #include "GameWindow.h" #include "SongLoader.h" @@ -22,9 +21,6 @@ SongWheel::SongWheel() IsInitialized = false; mLoadMutex = nullptr; mLoadThread = nullptr; - VSRGModeActive = (Configuration::GetConfigf("VSRGEnabled") != 0); - dotcurModeActive = false; - // dotcurModeActive = (Configuration::GetConfigf("dotcurEnabled") != 0); DifficultyIndex = 0; DisplayStartIndex = 0; @@ -79,16 +75,12 @@ class LoadThread std::mutex* mLoadMutex; SongDatabase* DB; std::shared_ptr ListRoot; - bool VSRGActive; - bool DCActive; std::atomic& isLoading; public: - LoadThread(std::mutex* m, SongDatabase* d, std::shared_ptr r, bool va, bool da, std::atomic& loadingstatus) + LoadThread(std::mutex* m, SongDatabase* d, std::shared_ptr r, std::atomic& loadingstatus) : mLoadMutex(m), DB(d), ListRoot(r), - VSRGActive(va), - DCActive(da), isLoading(loadingstatus) { isLoading = true; @@ -108,8 +100,9 @@ class LoadThread for (auto i = Directories.begin(); i != Directories.end(); ++i) + { - ListRoot->AddNamedDirectory(*mLoadMutex, &Loader, i->second, i->first, VSRGActive, DCActive); + ListRoot->AddNamedDirectory(*mLoadMutex, &Loader, i->second, i->first); SongWheel::GetInstance().ReapplyFilters(); } @@ -134,15 +127,13 @@ void SongWheel::ReloadSongs(SongDatabase* Database) DB = Database; Join(); - ListRoot = nullptr; - ListRoot = std::make_shared(); CurrentList = ListRoot.get(); if (!mLoadMutex) mLoadMutex = new std::mutex; - LoadThread L(mLoadMutex, DB, ListRoot, VSRGModeActive, dotcurModeActive, mLoading); + LoadThread L(mLoadMutex, DB, ListRoot, mLoading); mLoadThread = new std::thread(&LoadThread::Load, L); } @@ -189,16 +180,8 @@ int SongWheel::PrevDifficulty() if (!FilteredCurrentList.IsDirectory(SelectedBoundItem)) { DifficultyIndex--; - if (FilteredCurrentList.GetSongEntry(SelectedBoundItem)->Mode == MODE_VSRG) - { - auto Song = std::static_pointer_cast (FilteredCurrentList.GetSongEntry(SelectedBoundItem)); - max_index = Song->Difficulties.size() - 1; - } - else - { - auto Song = std::static_pointer_cast (FilteredCurrentList.GetSongEntry(SelectedBoundItem)); - max_index = Song->Difficulties.size() - 1; - } + auto Song = std::static_pointer_cast (FilteredCurrentList.GetSongEntry(SelectedBoundItem)); + max_index = Song->Difficulties.size() - 1; DifficultyIndex = std::min(max_index, DifficultyIndex); OnSongTentativeSelect(GetSelectedSong(), DifficultyIndex); @@ -214,18 +197,11 @@ int SongWheel::NextDifficulty() if (!FilteredCurrentList.IsDirectory(SelectedBoundItem)) { DifficultyIndex++; - if (FilteredCurrentList.GetSongEntry(SelectedBoundItem)->Mode == MODE_VSRG) - { - auto Song = std::static_pointer_cast (FilteredCurrentList.GetSongEntry(SelectedBoundItem)); - if (DifficultyIndex >= Song->Difficulties.size()) - DifficultyIndex = 0; - } - else - { - auto Song = std::static_pointer_cast (FilteredCurrentList.GetSongEntry(SelectedBoundItem)); - if (DifficultyIndex >= Song->Difficulties.size()) - DifficultyIndex = 0; - } + + auto Song = std::static_pointer_cast (FilteredCurrentList.GetSongEntry(SelectedBoundItem)); + if (DifficultyIndex >= Song->Difficulties.size()) + DifficultyIndex = 0; + OnSongTentativeSelect(GetSelectedSong(), DifficultyIndex); } @@ -316,7 +292,9 @@ void SongWheel::GoUp() if (CurrentList->HasParentDirectory()) { + CurrentList->SetInUse(false); CurrentList = CurrentList->GetParentDirectory(); + CurrentList->ClearEmpty(); ReapplyFilters(); OnDirectoryChange(); OnSongTentativeSelect(GetSelectedSong(), 0); @@ -328,9 +306,9 @@ bool SongWheel::HandleScrollInput(const double dx, const double dy) return true; } -std::shared_ptr SongWheel::GetSelectedSong() +std::shared_ptr SongWheel::GetSelectedSong() { - return FilteredCurrentList.GetSongEntry(SelectedBoundItem); + return std::static_pointer_cast(FilteredCurrentList.GetSongEntry(SelectedBoundItem)); } void SongWheel::Update(float Delta) @@ -370,7 +348,6 @@ void SongWheel::Update(float Delta) if (OldCursorPos != CursorPos) { OldCursorPos = CursorPos; - DifficultyIndex = 0; if (OnItemHover) { std::shared_ptr Notify = GetSelectedSong(); @@ -390,7 +367,7 @@ void SongWheel::DisplayItem(int32_t ListItem, int32_t ListPosition, float itemFr if (screen_box.Intersects(item_box)) { bool IsSelected = false; - std::shared_ptr Song = nullptr; + std::shared_ptr Song = nullptr; std::string Text; if (ListItem != -1) @@ -475,7 +452,6 @@ void SongWheel::SetSelectedItem(int32_t Item) // Set bound item index to this. SelectedBoundItem = Item; - GameState::GetInstance().SetSelectedSong(GetSelectedSong()); OnSongTentativeSelect(GetSelectedSong(), DifficultyIndex); } @@ -530,11 +506,13 @@ void SongWheel::ConfirmSelection() { if (!FilteredCurrentList.IsDirectory(SelectedBoundItem)) { - OnSongConfirm(GetSelectedSong(), DifficultyIndex); + if (DifficultyIndex < GetSelectedSong()->GetDifficultyCount()) + OnSongConfirm(GetSelectedSong(), DifficultyIndex); } else { CurrentList = CurrentList->GetListEntry(SelectedBoundItem).get(); + CurrentList->SetInUse(true); ReapplyFilters(); SetSelectedItem(SelectedUnboundItem); // Update our selected item to new bounderies. @@ -567,7 +545,7 @@ void Game::SongWheel::ReapplyFilters() { if (!CurrentList) return; - FilteredCurrentList = SongList(); + FilteredCurrentList.Clear(); for (auto entry : CurrentList->GetEntries()) { bool add = true; diff --git a/src/SongWheel.h b/src/SongWheel.h index d1914d37..2646ef87 100644 --- a/src/SongWheel.h +++ b/src/SongWheel.h @@ -2,11 +2,6 @@ #include "SongList.h" namespace Game { - namespace dotcur - { - class Song; - } - namespace VSRG { class Song; @@ -33,10 +28,10 @@ class GraphicalString; namespace Game { - typedef std::function, uint8_t)> SongNotification; + typedef std::function, uint8_t)> SongNotification; typedef std::function)> ItemNotification; - typedef std::function, bool, int32_t)> ItemTransformFunction; - typedef std::function, bool, int32_t, std::string)> StringTransformFunction; + typedef std::function, bool, int32_t)> ItemTransformFunction; + typedef std::function, bool, int32_t, std::string)> StringTransformFunction; typedef std::function ListTransformFunction; typedef std::function DirectoryChangeNotifyFunction; typedef std::function FuncFilterCriteria; @@ -70,8 +65,6 @@ namespace Game bool InWheelBounds(Vec2 Pos); bool IsInitialized; - bool dotcurModeActive; - bool VSRGModeActive; bool IsHovering; bool LoadedSongsOnce; @@ -116,7 +109,7 @@ namespace Game bool HandleInput(int32_t key, KeyEventType code, bool isMouseInput); bool HandleScrollInput(const double dx, const double dy); - std::shared_ptr GetSelectedSong(); + std::shared_ptr GetSelectedSong(); void ReloadSongs(SongDatabase* Database); void LoadSongsOnce(SongDatabase* Database); diff --git a/src/SwRescale.cpp b/src/SwRescale.cpp new file mode 100644 index 00000000..6557fdc2 --- /dev/null +++ b/src/SwRescale.cpp @@ -0,0 +1,8 @@ +#include "pch.h" + +#include "SwRescale.h" + +Vec2 RescaleL8(const uint8_t* tex, Vec2 size, float scale) +{ + return Vec2(); +} \ No newline at end of file diff --git a/src/SwRescale.h b/src/SwRescale.h new file mode 100644 index 00000000..ff68da3d --- /dev/null +++ b/src/SwRescale.h @@ -0,0 +1,4 @@ +#pragma once + +// 8-bit alpha/luminance texture rescale +Vec2 RescaleL8(const uint8_t* tex, Vec2 size, float scale); \ No newline at end of file diff --git a/src/TTFCache.cpp b/src/TTFCache.cpp new file mode 100644 index 00000000..a4606c56 --- /dev/null +++ b/src/TTFCache.cpp @@ -0,0 +1,67 @@ +#include "pch.h" +#include "TTFCache.h" + +TTFCache::TTFCache() +{ +} + + +bool TTFCache::LoadCache(std::filesystem::path cachepath) +{ + std::ifstream in(cachepath.string(), std::ios::binary); + if (!in.is_open()) return false; + + mCharBuffer.clear(); + int size; + + BinRead(in, size); + while (!in.eof() && size > 0) { + size_t chsize; + int id; + BinRead(in, id); + BinRead(in, chsize); + + std::vector buf(chsize); + in.read((char*)buf.data(), chsize); + + mCharBuffer[id] = std::move(buf); + size--; + } + + return true; +} + +bool TTFCache::SaveCache(std::filesystem::path cachepath) +{ + std::ofstream out(cachepath.string(), std::ios::binary); + if (!out.is_open()) return false; + + auto size = mCharBuffer.size(); + BinWrite(out, size); + + for (const auto &it : mCharBuffer) { + BinWrite(out, it.first); + + auto csize = it.second.size(); + BinWrite(out, csize); + + out.write((char*)it.second.data(), it.second.size()); + } + + return true; +} + +const uint8_t * const TTFCache::GetCharacterBuffer(int id) +{ + if (mCharBuffer.find(id) != mCharBuffer.end()) { + return mCharBuffer[id].data(); + } + + return nullptr; +} + +void TTFCache::SetCharacterBuffer(int id, uint8_t * data, size_t size) +{ + mCharBuffer[id].resize(size); + memcpy(mCharBuffer[id].data(), data, size); +} diff --git a/src/TTFCache.h b/src/TTFCache.h new file mode 100644 index 00000000..7e47cf49 --- /dev/null +++ b/src/TTFCache.h @@ -0,0 +1,11 @@ +#pragma once + +class TTFCache { + std::map> mCharBuffer; +public: + TTFCache(); + bool LoadCache(std::filesystem::path cachepath); + bool SaveCache(std::filesystem::path cachepath); + const uint8_t* const GetCharacterBuffer(int id); + void SetCharacterBuffer(int id, uint8_t *data, size_t size); +}; \ No newline at end of file diff --git a/src/Texture.cpp b/src/Texture.cpp index e83721d0..4d4f1ed9 100644 --- a/src/Texture.cpp +++ b/src/Texture.cpp @@ -39,6 +39,10 @@ void Texture::CreateTexture() LastBound = this; IsValid = true; + } else if (texture != -1 && IsValid) { + glBindTexture(GL_TEXTURE_2D, texture); + + LastBound = this; } } diff --git a/src/TextureConfig.cpp b/src/TextureConfig.cpp index 03400fb9..f94f5adf 100644 --- a/src/TextureConfig.cpp +++ b/src/TextureConfig.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "Logging.h" #include "GameState.h" diff --git a/src/TrackNote.cpp b/src/TrackNote.cpp index ae875a2a..3099f791 100644 --- a/src/TrackNote.cpp +++ b/src/TrackNote.cpp @@ -1,6 +1,6 @@ #include "pch.h" -#include "GameGlobal.h" + #include "TrackNote.h" //#include diff --git a/src/TruetypeFont.cpp b/src/TruetypeFont.cpp index 4cc26fed..f629dc05 100644 --- a/src/TruetypeFont.cpp +++ b/src/TruetypeFont.cpp @@ -1,15 +1,23 @@ #include "pch.h" #include "TruetypeFont.h" + +#include "GameState.h" #include "GameWindow.h" +#include "TTFCache.h" #include "Logging.h" +#include "SDF.h" + +const std::filesystem::path CACHE_PATH = "GameData/fontcache/"; + class TTFMan { public: struct FontData { std::shared_ptr> data; std::shared_ptr info; + std::shared_ptr > texels; }; private: @@ -17,7 +25,10 @@ class TTFMan { public: static void Load(std::filesystem::path Filename, - std::shared_ptr>& data, std::shared_ptr& info, bool &IsValid) { + std::shared_ptr>& data, + std::shared_ptr& info, + std::shared_ptr > &Texels, + bool &IsValid) { // accelerate loading if font is on registers if (font_data.find(Filename) != font_data.end()) @@ -25,6 +36,7 @@ class TTFMan { auto &fnt = font_data[Filename]; info = fnt.info; data = fnt.data; + Texels = fnt.texels; IsValid = true; return; } @@ -33,6 +45,7 @@ class TTFMan { info = nullptr; data = nullptr; + Texels = nullptr; if (!ifs.is_open()) { @@ -48,7 +61,7 @@ class TTFMan { data = std::make_shared>(offs); info = std::make_shared(); - + Texels = std::make_shared>(); // read data ifs.read((char*)data.get()->data(), offs); @@ -60,7 +73,8 @@ class TTFMan { if (IsValid) { font_data[Filename] = { data, - info + info, + Texels }; } else { @@ -71,17 +85,13 @@ class TTFMan { std::map < std::filesystem::path, TTFMan::FontData > TTFMan::font_data; -TruetypeFont::TruetypeFont(std::filesystem::path Filename, float Scale) +TruetypeFont::TruetypeFont(std::filesystem::path Filename) { - TTFMan::Load(Filename, this->data, this->info, IsValid); - - scale = Scale; - windowscale = 0; - + TTFMan::Load(Filename, this->data, this->info, this->Texes, IsValid); + if (IsValid) { - UpdateWindowScale(); - + realscale = stbtt_ScaleForPixelHeight(info.get(), SDF_SIZE); WindowFrame.AddTTF(this); } else @@ -91,52 +101,23 @@ TruetypeFont::TruetypeFont(std::filesystem::path Filename, float Scale) TruetypeFont::~TruetypeFont() { WindowFrame.RemoveTTF(this); - ReleaseTextures(); -} - -void TruetypeFont::UpdateWindowScale() -{ - if (windowscale == WindowFrame.GetWindowVScale()) - return; - - float oldscale = windowscale; - windowscale = WindowFrame.GetWindowVScale(); - - float oldrealscale = realscale; - realscale = stbtt_ScaleForPixelHeight(info.get(), scale * windowscale); - virtualscale = stbtt_ScaleForPixelHeight(info.get(), scale); -#ifdef VERBOSE_DEBUG - wprintf(L"change scale %f -> %f, realscale %f -> %f\n", oldscale, windowscale, oldrealscale, realscale); -#endif + // ReleaseTextures(); } void TruetypeFont::Invalidate() { - for (std::map ::iterator i = Texes.begin(); - i != Texes.end(); + for (std::map ::iterator i = Texes->begin(); + i != Texes->end(); i++) { i->second.gltx = 0; } } -void TruetypeFont::CheckCodepoint(int cp) -{ - if (Texes.find(cp) != Texes.end()) - { - if (Texes[cp].scl != windowscale) - { -#ifdef VERBOSE_DEBUG - wprintf(L"releasing %d\n", cp); -#endif - ReleaseCodepoint(cp); // force regeneration if scale changed - } - } -} TruetypeFont::codepdata &TruetypeFont::GetTexFromCodepoint(int cp) { - if (Texes.find(cp) == Texes.end()) + if (Texes->find(cp) == Texes->end()) { codepdata newcp; int w, h, xofs, yofs; @@ -145,30 +126,38 @@ TruetypeFont::codepdata &TruetypeFont::GetTexFromCodepoint(int cp) #endif if (IsValid) { - newcp.tex = stbtt_GetCodepointBitmap(info.get(), 0, realscale, cp, &w, &h, &xofs, &yofs); - newcp.gltx = 0; - newcp.scl = WindowFrame.GetWindowVScale(); - newcp.tw = w; - newcp.th = h; + unsigned char* nonsdf = stbtt_GetCodepointBitmap(info.get(), 0, realscale, cp, &w, &h, &xofs, &yofs); + if (nonsdf) { + unsigned char* sdf = new unsigned char[w * h]; - // get size etc.. for how it'd be if the screen weren't resized - void * tx = stbtt_GetCodepointBitmap(info.get(), 0, virtualscale, cp, &w, &h, &xofs, &yofs); - newcp.xofs = xofs; + // convert our non-sdf texture to a SDF texture + ConvertToSDF(sdf, nonsdf, w, h); + + // free our non-sdf + free(nonsdf); + + // use our SDF texture. alpha testing is up by default + newcp.tex = sdf; + } + else + newcp.tex = NULL; + + newcp.gltx = 0; + newcp.xofs = xofs; newcp.yofs = yofs; newcp.w = w; newcp.h = h; - free(tx); } else memset(&newcp, 0, sizeof(codepdata)); - Texes[cp] = newcp; - return Texes[cp]; + (*Texes)[cp] = newcp; + return Texes->at(cp); } else { - return Texes[cp]; + return Texes->at(cp); } } @@ -186,7 +175,6 @@ float TruetypeFont::GetHorizontalLength(const char *In) utf8::iterator itend(Text + len, Text, Text + len); for (; it != itend; ++it) { - CheckCodepoint(*it); // Force a regeneration of this if necessary codepdata &cp = GetTexFromCodepoint(*it); auto it_nx = it; @@ -196,7 +184,7 @@ float TruetypeFont::GetHorizontalLength(const char *In) float aW = stbtt_GetCodepointKernAdvance(info.get(), *it, *it_nx); int bW; stbtt_GetCodepointHMetrics(info.get(), *it, &bW, NULL); - Out += aW * virtualscale + bW * virtualscale; + Out += aW * realscale + bW * realscale; } else Out += cp.w; @@ -208,3 +196,30 @@ float TruetypeFont::GetHorizontalLength(const char *In) return Out; } + +void TruetypeFont::GenerateFontCache(std::filesystem::path u8charin, + std::filesystem::path inputttf) +{ + TTFCache cache; + TruetypeFont ttf(inputttf); + + // make sure our base path exists + auto path = CACHE_PATH / Game::GameState::GetInstance().GetSkin(); + std::filesystem::create_directory(CACHE_PATH); + std::filesystem::create_directory(path); + + auto cachename = path / inputttf.replace_extension("").filename(); + + std::ifstream in(u8charin.string(), std::ios::binary); + + std::istreambuf_iterator it(in.rdbuf()); + std::istreambuf_iterator end; + + while (it != end) { + auto ch = utf8::next(it, end); + auto tx = ttf.GetTexFromCodepoint(ch); + cache.SetCharacterBuffer(ch, tx.tex, tx.w * tx.h); + } + + cache.SaveCache(cachename); +} diff --git a/src/TruetypeFont.h b/src/TruetypeFont.h index 6d05b697..318c9835 100644 --- a/src/TruetypeFont.h +++ b/src/TruetypeFont.h @@ -11,39 +11,32 @@ class TruetypeFont : public Font std::shared_ptr> data; size_t offs; bool IsValid; - float scale; - float virtualscale; float realscale; - float windowscale; - struct codepdata { unsigned char* tex; uint32_t gltx; int xofs; int yofs; - float scl; int w; int h; - int tw; - int th; }; std::string filename; - std::map Texes; - void SetupTexture(); + std::shared_ptr > Texes; codepdata& GetTexFromCodepoint(int cp); - void CheckCodepoint(int cp); void ReleaseCodepoint(int cp); void ReleaseTextures(); - void UpdateWindowScale(); + friend class TTFMan; public: - TruetypeFont(std::filesystem::path filename, float Scale); + TruetypeFont(std::filesystem::path filename); ~TruetypeFont(); float GetHorizontalLength(const char *Text); + static void GenerateFontCache(std::filesystem::path u8charin, std::filesystem::path inputttf); + void Invalidate(); - void Render(const std::string &Text, const Vec2 &Position, const Mat4 &Transform = Mat4()); + void Render(const std::string &Text, const Vec2 &Position, const Mat4 &Transform = Mat4(), const Vec2 &Scale = Vec2(1,1)); }; diff --git a/src/Utility.cpp b/src/Utility.cpp index df5a8abe..7a3b1f7a 100644 --- a/src/Utility.cpp +++ b/src/Utility.cpp @@ -173,12 +173,16 @@ namespace Utility for (; next != nullptr; next = strpbrk(it, token.c_str())) { if (!compress || it - next != 0) - ret.push_back(std::string(it, next)); + { + ret.push_back(str.substr(it - str.c_str(), next - it)); + } it = next + 1; } if (it != next && len) - ret.push_back(std::string(it, &str[len])); + { + ret.push_back(str.substr(it - str.c_str(), next - it)); + } return ret; } @@ -212,11 +216,15 @@ namespace Utility } } - int GetLMT(std::filesystem::path Path) + int GetLastModifiedTime(std::filesystem::path Path) { if (std::filesystem::exists(Path)) { +#ifndef STD_FILESYSTEM // boost + return std::filesystem::last_write_time(Path); +#else // stl auto a = std::filesystem::last_write_time(Path); return decltype(a)::clock::to_time_t(a); +#endif } else return -1; } @@ -236,7 +244,7 @@ namespace Utility std::string GetSha256ForFile(std::filesystem::path Filename) { SHA256 SHA; - std::ifstream InStream(Filename.string()); + CreateIfstream(InStream, Filename); unsigned char tmpbuf[256]; if (!InStream.is_open()) @@ -256,6 +264,8 @@ namespace Utility std::vector GetFileListing(std::filesystem::path path) { std::vector out; + + if (!std::filesystem::exists(path)) return out; for (auto &p : std::filesystem::directory_iterator(path)) { out.push_back(p); } diff --git a/src/VBO.h b/src/VBO.h index 443127de..2e459775 100644 --- a/src/VBO.h +++ b/src/VBO.h @@ -25,7 +25,7 @@ class VBO static uint32_t LastBoundIndex; bool IsValid; Type mType; - void *VboData; + char *VboData; IdxKind mKind; uint32_t ElementCount; uint32_t ElementSize; diff --git a/src/VSRGMechanics.cpp b/src/VSRGMechanics.cpp index 031a2843..f47c7c3e 100644 --- a/src/VSRGMechanics.cpp +++ b/src/VSRGMechanics.cpp @@ -11,34 +11,47 @@ #include "ScoreKeeper7K.h" #include "VSRGMechanics.h" #include "TrackNote.h" +#include "NoteTransformations.h" namespace Game { namespace VSRG { bool Mechanics::IsLateHeadMiss(double t, TrackNote * note) { - return (t - note->GetStartTime()) * 1000.0 > score_keeper->getMissCutoffMS(); + return (t - note->GetStartTime()) * 1000.0 > PlayerScoreKeeper->getMissCutoffMS(); } bool Mechanics::InJudgeCutoff(double t, TrackNote * note) { - return (abs(t - note->GetStartTime()) < score_keeper->getJudgmentCutoff()) || - (abs(t - note->GetEndTime()) < score_keeper->getJudgmentCutoff()); + return (abs(t - note->GetStartTime()) < PlayerScoreKeeper->getJudgmentCutoff()) || + (abs(t - note->GetEndTime()) < PlayerScoreKeeper->getJudgmentCutoff()); } bool Mechanics::IsEarlyMiss(double t, TrackNote * note) { - return (t - note->GetStartTime()) * 1000. < -score_keeper->getMissCutoffMS(); + double dt = (t - note->GetStartTime()) * 1000.; + return dt < -PlayerScoreKeeper->getMissCutoffMS() && dt > -PlayerScoreKeeper->getEarlyMissCutoffMS(); } bool Mechanics::IsBmBadJudge(double t, TrackNote * note) { - return abs(t - note->GetStartTime()) > score_keeper->getJudgmentWindow(SKJ_W3); + double dt = abs(t - note->GetStartTime()); + return dt > PlayerScoreKeeper->getJudgmentWindow(SKJ_W3) && dt < PlayerScoreKeeper->getJudgmentWindow(SKJ_W4); } + void Mechanics::TransformNotes(PlayerChartState & ChartState) + { + if (GetTimingKind() == TT_BEATS) { + NoteTransform::TransformToBeats( + CurrentDifficulty->Channels, + ChartState.NotesByChannel, + ChartState.BPS); + } + } + void Mechanics::Setup(VSRG::Difficulty *Difficulty, std::shared_ptr scoreKeeper) { CurrentDifficulty = Difficulty; - score_keeper = scoreKeeper; + PlayerScoreKeeper = scoreKeeper; } RaindropMechanics::RaindropMechanics(bool forcedRelease) @@ -53,7 +66,12 @@ namespace Game { // Condition A: Hold tail outside accuracy cutoff (can't be hit any longer), // note wasn't hit at the head and it's a hold - if ((SongTime - m->GetEndTime()) > 0 && !m->WasHit() && m->IsHold()) + if ( + (SongTime - m->GetEndTime()) > 0 // outside judgment + && !m->WasHit() && m->IsHold() // not hit yet + // and head can't be hit + && (abs(m->GetStartTime() - SongTime) > PlayerScoreKeeper->getMissCutoffMS() / 1000.0) + ) { // ^ no need for delays here. // remove hold notes that were never hit. @@ -61,7 +79,7 @@ namespace Game { MissNotify(abs(SongTime - m->GetEndTime()) * 1000, k, m->IsHold(), true, false); m->Hit(); } // Condition B: Regular note or hold head outside cutoff, wasn't hit and it's enabled. - else if ((SongTime - m->GetStartTime()) * 1000 > score_keeper->getMissCutoffMS() && + else if (IsLateHeadMiss(SongTime, m) && (!m->WasHit() && m->IsHeadEnabled())) { MissNotify(abs(SongTime - m->GetStartTime()) * 1000, k, m->IsHold(), false, false); @@ -85,7 +103,7 @@ namespace Game { else if (m->IsHold() && m->IsEnabled()) { // Condition C-1: Forced release is enabled - if ((SongTime - m->GetEndTime()) * 1000 > score_keeper->getMissCutoffMS() && forcedRelease) + if ((SongTime - m->GetEndTime()) * 1000 > PlayerScoreKeeper->getMissCutoffMS() && forcedRelease) { m->FailHit(); // Take away health and combo (1st false) @@ -106,7 +124,7 @@ namespace Game { else { // Only take away health, but not combo (1st true) - MissNotify(score_keeper->getMissCutoffMS(), k, m->IsHold(), true, false); + MissNotify(PlayerScoreKeeper->getMissCutoffMS(), k, m->IsHold(), true, false); } SetLaneHoldingState(k, false); @@ -170,7 +188,7 @@ namespace Game { double releaseWindow; if (forcedRelease) - releaseWindow = score_keeper->getJudgmentWindow(SKJ_W3); + releaseWindow = PlayerScoreKeeper->getJudgmentWindow(SKJ_W3); else releaseWindow = 250; // 250 ms @@ -214,7 +232,7 @@ namespace Game { double dev = (SongBeat - m->GetEndTime()); double tD = abs(dev); - if (tD < score_keeper->getJudgmentWindow(SKJ_W3)) /* Released in time */ + if (tD < PlayerScoreKeeper->getJudgmentWindow(SKJ_W3)) /* Released in time */ { HitNotify(dev, Lane, m->IsHold(), true); SetLaneHoldingState(Lane, false); @@ -242,7 +260,7 @@ namespace Game { double dev = (SongBeat - m->GetStartTime()); double tD = abs(dev); - if (tD < score_keeper->getJudgmentWindow(SKJ_W3)) // If the note was hit inside judging range + if (tD < PlayerScoreKeeper->getJudgmentWindow(SKJ_W3)) // If the note was hit inside judging range { m->Hit(); @@ -255,7 +273,7 @@ namespace Game { m->Disable(); // BADs stay visible. - if (tD < score_keeper->getJudgmentWindow(SKJ_W2)) + if (tD < PlayerScoreKeeper->getJudgmentWindow(SKJ_W2)) m->MakeInvisible(); } @@ -263,37 +281,47 @@ namespace Game { return true; } + else if (tD > PlayerScoreKeeper->getJudgmentWindow(SKJ_W3) && tD < PlayerScoreKeeper->getMissCutoffMS()) { + m->FailHit(); + m->Disable(); + + MissNotify(dev, Lane, m->IsHold(), false, false); + PlayNoteSoundEvent(m->GetSound()); + } + return false; } bool O2JamMechanics::OnUpdate(double SongBeat, VSRG::TrackNote* m, uint32_t Lane) { auto k = Lane; - double tD = SongBeat - m->GetEndTime(); + double tTail = SongBeat - m->GetEndTime(); double tHead = SongBeat - m->GetStartTime(); + if (!m->IsEnabled()) return false; // keep looking + // Condition A: Hold tail outside accuracy cutoff (can't be hit any longer), // note wasn't hit at the head and it's a hold - if (tD > 0 && !m->WasHit() && m->IsHold()) + if (tTail > 0 && !m->WasHit() && m->IsHold()) { // remove hold notes that were never hit. m->FailHit(); - MissNotify(abs(tD), k, m->IsHold(), true, false); + MissNotify(abs(tTail), k, m->IsHold(), true, false); m->Disable(); } // Condition B: Regular note or hold head outside cutoff, wasn't hit and it's enabled. - else if (tHead > score_keeper->getJudgmentWindow(SKJ_W3) && !m->WasHit() && m->IsEnabled()) + else if (tHead > PlayerScoreKeeper->getJudgmentWindow(SKJ_W3) && !m->WasHit() && m->IsEnabled()) { m->FailHit(); - MissNotify(abs(tD), k, m->IsHold(), false, false); + MissNotify(abs(tHead), k, m->IsHold(), false, false); // remove from judgment completely m->Disable(); } // Condition C: Hold head was hit, but hold tail was not released. - else if (tD > score_keeper->getJudgmentWindow(SKJ_W3) && + else if (tTail > PlayerScoreKeeper->getJudgmentWindow(SKJ_W3) && m->IsHold() && m->WasHit() && m->IsEnabled()) { m->FailHit(); - MissNotify(abs(tD), k, m->IsHold(), false, false); + MissNotify(abs(tTail), k, m->IsHold(), false, false); SetLaneHoldingState(k, false); m->Disable(); diff --git a/src/VSRGMechanics.h b/src/VSRGMechanics.h index 027c2828..68ca0aa3 100644 --- a/src/VSRGMechanics.h +++ b/src/VSRGMechanics.h @@ -7,13 +7,6 @@ namespace Game { class TrackNote; class Song; - enum TimingType - { - TT_TIME, - TT_BEATS, - TT_PIXELS - }; - class Mechanics { public: @@ -24,7 +17,7 @@ namespace Game { protected: Difficulty *CurrentDifficulty; - std::shared_ptr score_keeper; + std::shared_ptr PlayerScoreKeeper; bool IsLateHeadMiss(double t, TrackNote *note); bool InJudgeCutoff(double t, TrackNote *note); @@ -41,6 +34,8 @@ namespace Game { HitEvent HitNotify; MissEvent MissNotify; + virtual void TransformNotes(PlayerChartState &ChartState); + virtual void Setup(VSRG::Difficulty *Difficulty, std::shared_ptr scoreKeeper); // If returns true, don't judge any more notes. diff --git a/src/VideoPlayback.cpp b/src/VideoPlayback.cpp index 3633d571..2dde77c6 100644 --- a/src/VideoPlayback.cpp +++ b/src/VideoPlayback.cpp @@ -2,6 +2,7 @@ #include "Texture.h" #include "VideoPlayback.h" +#include "Logging.h" extern "C" { #include @@ -112,15 +113,15 @@ class VideoPlaybackData } } - VideoPlaybackData::~VideoPlaybackData() { + ~VideoPlaybackData() { av_frame_free(&DisplayFrame.frame); AVFrame* f; - while (f = GetCleanFrame().frame) { + while ((f = GetCleanFrame().frame)) { av_frame_free(&f); } - while (f = GetPendingFrame().frame) { + while ((f = GetPendingFrame().frame)) { av_frame_free(&f); } @@ -229,7 +230,7 @@ bool VideoPlayback::Open(std::filesystem::path path) } // Find the first video stream - for (int i = 0; i < newctx->AV->nb_streams; i++) + for (size_t i = 0; i < newctx->AV->nb_streams; i++) { if (newctx->AV->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) { newctx->videoStreamIndex = i; @@ -258,6 +259,9 @@ bool VideoPlayback::Open(std::filesystem::path path) newctx->InitializeBuffers(mFrameQueueItems, newctx->UsableCodecCtx->width, newctx->UsableCodecCtx->height); + /*Log::Printf("Video delay (frames): %d\n", newctx->UsableCodecCtx->delay); + av_seek_frame(newctx->AV, newctx->videoStreamIndex, newctx->UsableCodecCtx->delay * 2, 0);*/ + auto f = avpicture_get_size(newctx->UsableCodecCtx->pix_fmt, newctx->UsableCodecCtx->width, newctx->UsableCodecCtx->height) + AV_INPUT_BUFFER_PADDING_SIZE; @@ -307,17 +311,27 @@ void VideoPlayback::StartDecodeThread() void VideoPlayback::UpdateClock(double clock) { - if (Context->DisplayFrame.frame) { - if (Context->DisplayFrame.pts <= clock) { - UpdateVideoTexture(Context->DisplayFrame.frame); - Context->PutCleanFrame(Context->DisplayFrame); - Context->DisplayFrame.frame = nullptr; + bool update = true; + + if (!Context) return; + if (clock < 0) return; + + while (update) { + if (Context->DisplayFrame.frame) { + if (Context->DisplayFrame.pts <= clock) { + UpdateVideoTexture(Context->DisplayFrame.frame); + Context->PutCleanFrame(Context->DisplayFrame); + Context->DisplayFrame.frame = nullptr; + } + else break; + } + else { + Context->DisplayFrame = Context->GetPendingFrame(); + Context->CleanFrameAvailable = true; + Context->ringbuffer_has_space.notify_one(); + + if (!Context->DisplayFrame.frame) update = false; } - } - else { - Context->DisplayFrame = Context->GetPendingFrame(); - Context->CleanFrameAvailable = true; - Context->ringbuffer_has_space.notify_one(); } } @@ -338,7 +352,7 @@ void VideoPlayback::UpdateVideoTexture(void * data) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, frame->data[0]); + glTexImage2D(GL_TEXTURE_2D, 0, GL_SRGB8, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, frame->data[0]); TextureAssigned = true; } else { diff --git a/src/ext/ConvertUTF.h b/src/ext/ConvertUTF.h new file mode 100644 index 00000000..14d7b70d --- /dev/null +++ b/src/ext/ConvertUTF.h @@ -0,0 +1,149 @@ +/* + * Copyright 2001-2004 Unicode, Inc. + * + * Disclaimer + * + * This source code is provided as is by Unicode, Inc. No claims are + * made as to fitness for any particular purpose. No warranties of any + * kind are expressed or implied. The recipient agrees to determine + * applicability of information provided. If this file has been + * purchased on magnetic or optical media from Unicode, Inc., the + * sole remedy for any claim will be exchange of defective media + * within 90 days of receipt. + * + * Limitations on Rights to Redistribute This Code + * + * Unicode, Inc. hereby grants the right to freely use the information + * supplied in this file in the creation of products supporting the + * Unicode Standard, and to make copies of this file in any form + * for internal or external distribution as long as this notice + * remains attached. + */ + +/* --------------------------------------------------------------------- + + Conversions between UTF32, UTF-16, and UTF-8. Header file. + + Several funtions are included here, forming a complete set of + conversions between the three formats. UTF-7 is not included + here, but is handled in a separate source file. + + Each of these routines takes pointers to input buffers and output + buffers. The input buffers are const. + + Each routine converts the text between *sourceStart and sourceEnd, + putting the result into the buffer between *targetStart and + targetEnd. Note: the end pointers are *after* the last item: e.g. + *(sourceEnd - 1) is the last item. + + The return result indicates whether the conversion was successful, + and if not, whether the problem was in the source or target buffers. + (Only the first encountered problem is indicated.) + + After the conversion, *sourceStart and *targetStart are both + updated to point to the end of last text successfully converted in + the respective buffers. + + Input parameters: + sourceStart - pointer to a pointer to the source buffer. + The contents of this are modified on return so that + it points at the next thing to be converted. + targetStart - similarly, pointer to pointer to the target buffer. + sourceEnd, targetEnd - respectively pointers to the ends of the + two buffers, for overflow checking only. + + These conversion functions take a ConversionFlags argument. When this + flag is set to strict, both irregular sequences and isolated surrogates + will cause an error. When the flag is set to lenient, both irregular + sequences and isolated surrogates are converted. + + Whether the flag is strict or lenient, all illegal sequences will cause + an error return. This includes sequences such as: , , + or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code + must check for illegal sequences. + + When the flag is set to lenient, characters over 0x10FFFF are converted + to the replacement character; otherwise (when the flag is set to strict) + they constitute an error. + + Output parameters: + The value "sourceIllegal" is returned from some routines if the input + sequence is malformed. When "sourceIllegal" is returned, the source + value will point to the illegal value that caused the problem. E.g., + in UTF-8 when a sequence is malformed, it points to the start of the + malformed sequence. + + Author: Mark E. Davis, 1994. + Rev History: Rick McGowan, fixes & updates May 2001. + Fixes & updates, Sept 2001. + +------------------------------------------------------------------------ */ + +/* --------------------------------------------------------------------- + The following 4 definitions are compiler-specific. + The C standard does not guarantee that wchar_t has at least + 16 bits, so wchar_t is no less portable than unsigned short! + All should be unsigned values to avoid sign extension during + bit mask & shift operations. +------------------------------------------------------------------------ */ + +typedef unsigned int UTF32; /* at least 32 bits */ +typedef unsigned short UTF16; /* at least 16 bits */ +typedef unsigned char UTF8; /* typically 8 bits */ +typedef unsigned char Boolean; /* 0 or 1 */ + +/* Some fundamental constants */ +#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD +#define UNI_MAX_BMP (UTF32)0x0000FFFF +#define UNI_MAX_UTF16 (UTF32)0x0010FFFF +#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF +#define UNI_MAX_LEGAL_UTF32 (UTF32)0x0010FFFF + +typedef enum { + conversionOK, /* conversion successful */ + sourceExhausted, /* partial character in source, but hit end */ + targetExhausted, /* insuff. room in target for conversion */ + sourceIllegal /* source sequence is illegal/malformed */ +} ConversionResult; + +typedef enum { + strictConversion = 0, + lenientConversion +} ConversionFlags; + +/* This is for C++ and does no harm in C */ +#ifdef __cplusplus +extern "C" { +#endif + +ConversionResult ConvertUTF8toUTF16 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF8 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF8toUTF32 ( + const UTF8** sourceStart, const UTF8* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF8 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF16toUTF32 ( + const UTF16** sourceStart, const UTF16* sourceEnd, + UTF32** targetStart, UTF32* targetEnd, ConversionFlags flags); + +ConversionResult ConvertUTF32toUTF16 ( + const UTF32** sourceStart, const UTF32* sourceEnd, + UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); + +Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); + +#ifdef __cplusplus +} +#endif + +/* --------------------------------------------------------------------- */ diff --git a/src/osuBackgroundAnimation.cpp b/src/osuBackgroundAnimation.cpp index dd60a315..84d59647 100644 --- a/src/osuBackgroundAnimation.cpp +++ b/src/osuBackgroundAnimation.cpp @@ -1,5 +1,5 @@ #include "pch.h" -#include "GameGlobal.h" + #include "BackgroundAnimation.h" #include "Song.h" #include "Song7K.h" @@ -10,7 +10,10 @@ #include "Texture.h" #include "Logging.h" +#include "VideoPlayback.h" + const float OSB_WIDTH = 640; +const float OSB_WIDTH_WIDE = 853; const float OSB_HEIGHT = 480; CfgVar OSBDebug("OSB", "Debug"); @@ -217,6 +220,7 @@ namespace osb { mTransform.ChainTransformation(&mParent->GetScreenTransformation()); // flip -> pivot + mPivot.SetPosition(OriginPivots[mOrigin].x, OriginPivots[mOrigin].y); mPivot.ChainTransformation(&mTransform); // sprite -> flip vertices @@ -224,7 +228,6 @@ namespace osb { // No op from sprite. mSprite->ChainTransformation(&mFlip); - mPivot.SetPosition(OriginPivots[mOrigin].x, OriginPivots[mOrigin].y); // Set the image. mSprite->SetImage(mParent->GetImageFromIndex(mImageIndex), false); @@ -243,7 +246,7 @@ namespace osb { } template - bool clamp_iter(typename T::iterator &it, T& vec) + bool ValidateEventIterator(typename T::iterator &it, T& vec) { if (vec.begin() == vec.end()) return false; // don't use this iter if (it != vec.begin()) it--; @@ -259,7 +262,7 @@ namespace osb { // Okay, a pretty long function follows. Fade first. auto fade_evt = GetEvent(Time, evFade); - if (clamp_iter(fade_evt, evFade)) { + if (ValidateEventIterator(fade_evt, evFade)) { if (WithinEvents(Time)) mSprite->Alpha = fade_evt->LerpValue(Time); else { @@ -282,12 +285,12 @@ namespace osb { // Now position. auto movx_evt = GetEvent(Time, evMoveX); - if (clamp_iter(movx_evt, evMoveX)) + if (ValidateEventIterator(movx_evt, evMoveX)) mTransform.SetPositionX(movx_evt->LerpValue(Time)); else mTransform.SetPositionX(mStartPos.x); auto movy_evt = GetEvent(Time, evMoveY); - if (clamp_iter(movy_evt, evMoveY)) + if (ValidateEventIterator(movy_evt, evMoveY)) mTransform.SetPositionY(movy_evt->LerpValue(Time)); else mTransform.SetPositionY(mStartPos.y); @@ -299,10 +302,10 @@ namespace osb { // Now scale and rotation. float scale = 1; auto scale_evt = GetEvent(Time, evScale); - if (clamp_iter(scale_evt, evScale)) + if (ValidateEventIterator(scale_evt, evScale)) scale = scale_evt->LerpValue(Time); else if (mLayer == osb::LAYER_SP_BACKGROUND && mSprite->GetImage()) - scale *= OSB_HEIGHT / mSprite->GetImage()->h; + scale *= OSB_WIDTH_WIDE / mSprite->GetImage()->w; else scale = 1; // we want to scale it to fit - but we don't want to alter the scale set by the user // scales just get multiplied so we'll do that @@ -311,13 +314,13 @@ namespace osb { // defaulting at 1,1 to be our vector scale. That way they'll pile up. Vec2 vscale; auto vscale_evt = GetEvent(Time, evScaleVec); - if (clamp_iter(vscale_evt, evScaleVec)) + if (ValidateEventIterator(vscale_evt, evScaleVec)) vscale = vscale_evt->LerpValue(Time); else vscale = Vec2(1, 1); auto rot_evt = GetEvent(Time, evRotate); float rot = 0; - if (clamp_iter(rot_evt, evRotate)) { + if (ValidateEventIterator(rot_evt, evRotate)) { rot = rot_evt->LerpValue(Time); mTransform.SetRotation(glm::degrees(rot)); } @@ -331,15 +334,18 @@ namespace osb { // therefore, pivot is applied, then scale // then both size and scale on mTransform are free for usage. - // For some reason it's not applying scale then rotate - but rotate then scale. - // To get around this, we undo the transformation on the object's x-axis - // by multiplying this value to X. + // Set active scales. mTransform.SetSize(i->w * scale * vscale.x, i->h * scale * vscale.y); + + auto vid = dynamic_cast(i); + if (vid) { + vid->UpdateClock(Time - evFade.begin()->GetTime()); + } } auto colorization_evt = GetEvent(Time, evColorize); - if (clamp_iter(colorization_evt, evColorize)) { + if (ValidateEventIterator(colorization_evt, evColorize)) { auto lerp = colorization_evt->LerpValue(Time); mSprite->Red = lerp.r; mSprite->Green = lerp.g; @@ -406,7 +412,7 @@ namespace osb { } template - void VecSort(T& vec) + void SortEventList(T& vec) { auto cmp = [](const typename T::value_type& A, const typename T::value_type& B) { @@ -419,16 +425,16 @@ namespace osb { void EventComponent::SortEvents() { - VecSort(evMoveX); - VecSort(evMoveY); - VecSort(evScale); - VecSort(evScaleVec); - VecSort(evRotate); - VecSort(evColorize); - VecSort(evFade); - VecSort(evFlipH); - VecSort(evFlipV); - VecSort(evAdditive); + SortEventList(evMoveX); + SortEventList(evMoveY); + SortEventList(evScale); + SortEventList(evScaleVec); + SortEventList(evRotate); + SortEventList(evColorize); + SortEventList(evFade); + SortEventList(evFlipH); + SortEventList(evFlipV); + SortEventList(evAdditive); GetDuration(); } @@ -504,6 +510,8 @@ namespace osb { case EVT_VFLIP: evFlipV.push_back(*std::static_pointer_cast(event)); break; + default: + break; } } else // Unpack move events. @@ -875,13 +883,6 @@ osb::SpriteList ReadOSBEvents(std::istream& event_str) bgsprite.AddEvent(evt); - /*if (previous_background) { - float T = 1; - auto prev_evt = previous_background->GetEvent(T, osb::EVT_FADE); - if (previous_background->IsValidEvent(prev_evt, osb::EVT_FADE)) - previous_background->GetEventList(osb::EVT_FADE)))->SetEndTime(time); - }*/ - previous_background = &bgsprite; list.insert(list.begin(), bgsprite); @@ -892,6 +893,27 @@ osb::SpriteList ReadOSBEvents(std::istream& event_str) if (split_result[0] == "2") return true; + if (split_result[0] == "video") { + float time = latof(split_result[1]); + auto bgsprite = osb::BGASprite( + strip_quotes(split_result[2]), + osb::PP_CENTER, + Vec2(320, 240), + osb::LAYER_SP_BACKGROUND); + + auto evt = std::make_shared(); + evt->SetEndValue(1); + evt->SetValue(1); + evt->SetTime(latof(split_result[1]) / 1000.0f); + evt->SetEndTime(std::numeric_limits::infinity()); + + bgsprite.AddEvent(evt); + + previous_background = &bgsprite; + + list.insert(list.end(), bgsprite); + } + return false; }; @@ -1000,21 +1022,41 @@ osuBackgroundAnimation::osuBackgroundAnimation(Interruptible* parent, osb::Sprit : BackgroundAnimation(parent), mImageList(this) { - Transform.SetSize(OSB_WIDTH, OSB_HEIGHT); - mScreenTransformation.SetSize(1 / OSB_WIDTH, 1 / OSB_HEIGHT); + Transform.SetSize(OSB_WIDTH_WIDE, OSB_HEIGHT); + mScreenTransformation.SetPositionX( (OSB_WIDTH_WIDE - OSB_WIDTH) / 2 / OSB_WIDTH_WIDE); + mScreenTransformation.SetSize(1 / OSB_WIDTH_WIDE, 1 / OSB_HEIGHT); mScreenTransformation.ChainTransformation(&Transform); Song = song; CanValidate = false; + int video_index = 0; if (existing_mSprites) { for (auto sp : *existing_mSprites) { sp.SetParent(this); - sp.SetImageIndex(AddImageToList(sp.GetImageFilename())); + + auto vpath = song->SongDirectory / sp.GetImageFilename(); + if (IsVideoPath(vpath)) { + video_index--; + auto vid = mVideoList[video_index] = new VideoPlayback(); + if (vid->Open(vpath)) { + vid->StartDecodeThread(); + mImageList.AddToListIndex(vid, video_index); + } + } else { + sp.SetImageIndex(AddImageToList(sp.GetImageFilename())); + } mSprites.push_back(sp); } } } +osuBackgroundAnimation::~osuBackgroundAnimation() +{ + for (auto v: mVideoList) { + delete v.second; + } +} + Transformation& osuBackgroundAnimation::GetScreenTransformation() { return mScreenTransformation; @@ -1022,7 +1064,10 @@ Transformation& osuBackgroundAnimation::GetScreenTransformation() Texture* osuBackgroundAnimation::GetImageFromIndex(int m_image_index) { - return mImageList.GetFromIndex(m_image_index); + if (m_image_index >= 0) + return mImageList.GetFromIndex(m_image_index); + else + return mVideoList[m_image_index]; } int osuBackgroundAnimation::GetIndexFromFilename(std::string filename) diff --git a/src/osuBackgroundAnimation.h b/src/osuBackgroundAnimation.h index e6240778..3b187133 100644 --- a/src/osuBackgroundAnimation.h +++ b/src/osuBackgroundAnimation.h @@ -285,6 +285,8 @@ namespace osb typedef std::vector SpriteList; } +class VideoPlayback; + class osuBackgroundAnimation : public BackgroundAnimation { osb::SpriteList mSprites; @@ -293,16 +295,19 @@ class osuBackgroundAnimation : public BackgroundAnimation std::vector mForegroundLayer; std::map mFileIndices; ImageList mImageList; + std::map mVideoList; int AddImageToList(std::string image_filename); Game::VSRG::Song *Song; - Transformation mScreenTransformation; - bool CanValidate; + Transformation mScreenTransformation; + bool CanValidate; + public: osuBackgroundAnimation(Interruptible* parent, osb::SpriteList* existing_sprites, Game::VSRG::Song* song); + ~osuBackgroundAnimation(); Texture* GetImageFromIndex(int m_image_index); int GetIndexFromFilename(std::string filename); - Transformation& GetScreenTransformation(); + Transformation& GetScreenTransformation(); void Load() override; void Validate() override; diff --git a/src/pch.h b/src/pch.h index 57b8718e..461b9fea 100644 --- a/src/pch.h +++ b/src/pch.h @@ -12,6 +12,7 @@ #define _USE_MATH_DEFINES #define ENABLE_SNDFILE_WINDOWS_PROTOTYPES 1 +#define WIN32_LEAN_AND_MEAN #include #include #include @@ -44,6 +45,7 @@ { namespace filesystem = experimental::filesystem; } + #define STD_FILESYSTEM #else #ifndef __GNUC__ @@ -53,12 +55,14 @@ namespace filesystem = boost::filesystem; } #else // it's GCC - #if __GNU_PREREQ(6,1) + #if __GNUC_PREREQ(6,1) // We can alias the filesystem + #include namespace std { namespace filesystem = experimental::filesystem; } + #define STD_FILESYSTEM #else // okay then, use boost #include @@ -239,6 +243,21 @@ namespace Color extern const ColorRGB Blue; } +template + +T gcd(T a, T b) +{ + if (b == 0) return a; + else return gcd(b, a % b); +} + +template + +T lcm(T a, T b) +{ + return a * b / gcd(a, b); +} + template struct Fraction { @@ -269,6 +288,19 @@ struct Fraction d = static_cast(Num) / Den; } } + + Fraction Simplify() { + T t = gcd(Num, Den); + return Fraction{ a / t, b / t }; + } + + operator double() { + return Num / Den; + } + + bool operator<(Fraction other) { + return this->operator double() < other->operator double(); + } }; using LFraction = Fraction; @@ -298,7 +330,7 @@ namespace Utility std::string SJIStoU8(std::string Line); void CheckDir(std::string Dirname); - int GetLMT(std::filesystem::path Path); + int GetLastModifiedTime(std::filesystem::path Path); std::string GetSha256ForFile(std::filesystem::path Filename); std::string IntToStr(int num); std::string CharToStr(char c); @@ -410,4 +442,15 @@ double latof(std::string s); #define CreateBinIfstream(name, fn) std::fstream name(fn.string(), std::ios::in | std::ios::binary); #endif +template +void BinWrite(std::ofstream &of, T obj) { + of.write((char*)&obj, sizeof(T)); +} + +template +void BinRead(std::ifstream &of, T& obj) { + of.read((char*)&obj, sizeof(T)); +} + +#include "Configuration.h" #include "GameGlobal.h" diff --git a/tests/TestSetA.cpp b/tests/TestSetA.cpp index 80f4f7a6..41ebf4f9 100644 --- a/tests/TestSetA.cpp +++ b/tests/TestSetA.cpp @@ -1,7 +1,6 @@ #include "pch.h" #include "../src/GameGlobal.h" #include "../src/Song.h" -#include "../src/SongDC.h" #include "../src/Song7K.h" #include "../src/SongLoader.h" #include "../src/BackgroundAnimation.h" @@ -51,7 +50,7 @@ TEST_CASE("Noteskin state") TEST_CASE("Speed support") { auto sng = LoadSong7KFromFilename("tests/files/jnight.ssc"); - auto pcd = Game::VSRG::GameChartData::FromDifficulty(sng->GetDifficulty(0)); + auto pcd = Game::VSRG::PlayerChartState::FromDifficulty(sng->GetDifficulty(0)); auto tbeat = pcd.GetTimeAtBeat(93. + 4.); REQUIRE(pcd.GetSpeedMultiplierAt(tbeat) == 0.250); } \ No newline at end of file