mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-12-08 04:58:48 +00:00
Compare commits
59 commits
e1d4781203
...
0dcabdf54b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dcabdf54b | ||
|
|
9abb0c9dea | ||
|
|
758f712eb5 | ||
|
|
01029817c0 | ||
|
|
8a31e12d10 | ||
|
|
90ab04caa1 | ||
|
|
ba7f89b9a2 | ||
|
|
ee36cbbbcf | ||
|
|
d74b8fb4ca | ||
|
|
8fa622e147 | ||
|
|
a12950d5a7 | ||
|
|
71bc847698 | ||
|
|
a560bf51a9 | ||
|
|
866e5aa008 | ||
|
|
3c213ad45c | ||
|
|
a0b933ce8f | ||
|
|
671e1435d2 | ||
|
|
369dad3951 | ||
|
|
8ecde809e6 | ||
|
|
c4394f0d12 | ||
|
|
63cbaef4c4 | ||
|
|
4120b20a24 | ||
|
|
a40972926d | ||
|
|
368531f6fa | ||
|
|
b9d4ce70cc | ||
|
|
6ef3fe4faf | ||
|
|
8abc9ab306 | ||
|
|
8349999833 | ||
|
|
cf52bcdf65 | ||
|
|
4e03cf0a5d | ||
|
|
8952acae30 | ||
|
|
16c685ac98 | ||
|
|
5472bf691f | ||
|
|
6a71f95cf9 | ||
|
|
48f3e984f4 | ||
|
|
b125a49410 | ||
|
|
de19d65ad6 | ||
|
|
6334215be4 | ||
|
|
a3b7891b42 | ||
|
|
53c62e219b | ||
|
|
adb96897dc | ||
|
|
9a77539664 | ||
|
|
0d91d68300 | ||
|
|
d49fd76bd1 | ||
|
|
329ec524c0 | ||
|
|
3f89464608 | ||
|
|
118238105b | ||
|
|
0d32ccc214 | ||
|
|
ccbae3da7d | ||
|
|
b4058fd7b0 | ||
|
|
c6add57710 | ||
|
|
c33ec8c0ea | ||
|
|
fe683eba43 | ||
|
|
1534eb2422 | ||
|
|
c2fd17c551 | ||
|
|
ec804394b7 | ||
|
|
04ce409f33 | ||
|
|
635f6c094e | ||
|
|
0fbd7a7998 |
112
.vscode/settings.json
vendored
112
.vscode/settings.json
vendored
|
|
@ -1,31 +1,22 @@
|
|||
{
|
||||
"[haxe]": {
|
||||
// Automatically keep Haxe files formatted.
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.organizeImports": "never"
|
||||
},
|
||||
"editor.codeActionsOnSave": { "source.organizeImports": "never" },
|
||||
"editor.defaultFormatter": "nadako.vshaxe",
|
||||
"editor.tabSize": 2
|
||||
},
|
||||
|
||||
"[json]": {
|
||||
// Automatically keep JSON files formatted.
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
|
||||
"[jsonc]": {
|
||||
// Automatically keep JSONC files formatted.
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"prettier.tabWidth": 2,
|
||||
|
||||
// XML formatting style configuration
|
||||
// XML Formatting
|
||||
"xml.format.enabled": true,
|
||||
"xml.format.legacy": false,
|
||||
"xml.format.emptyElements": "collapse",
|
||||
|
|
@ -33,7 +24,7 @@
|
|||
"xml.format.enforceQuoteStyle": "preferred",
|
||||
"xml.format.preserveAttributeLineBreaks": false,
|
||||
"xml.format.preservedNewlines": 0,
|
||||
"xml.format.splitAttributes": false,
|
||||
"xml.format.splitAttributes": "preserve",
|
||||
"xml.format.joinCDATALines": true,
|
||||
"xml.format.preserveEmptyContent": false,
|
||||
"xml.format.joinCommentLines": false,
|
||||
|
|
@ -56,26 +47,19 @@
|
|||
"xml.format.maxLineWidth": 0,
|
||||
"xml.format.grammarAwareFormatting": true,
|
||||
|
||||
// Generic file formatting style configuration
|
||||
// General formatting
|
||||
"files.insertFinalNewline": true,
|
||||
"files.trimFinalNewlines": false,
|
||||
"files.trimTrailingWhitespace": true,
|
||||
|
||||
// Automatically detect indentation.
|
||||
"editor.detectIndentation": true,
|
||||
"editor.insertSpaces": true,
|
||||
"editor.tabSize": 2,
|
||||
|
||||
// Automatically enforce Linux style line endings.
|
||||
"prettier.tabWidth": 2,
|
||||
"files.eol": "\n",
|
||||
|
||||
"haxe.displayPort": "auto",
|
||||
"haxe.enableCompilationServer": true,
|
||||
"haxe.enableServerView": true,
|
||||
"haxe.displayServer": {
|
||||
"arguments": ["-v"]
|
||||
},
|
||||
// Fix file associations for HScript.
|
||||
"haxe.displayServer": { "arguments": ["-v"] },
|
||||
"files.associations": {
|
||||
"*.hxp": "haxe",
|
||||
"*.hscript": "haxe",
|
||||
|
|
@ -84,14 +68,27 @@
|
|||
"*.hxc": "haxe"
|
||||
},
|
||||
"projectManager.git.baseFolders": ["./"],
|
||||
|
||||
"haxecheckstyle.sourceFolders": ["src", "source"],
|
||||
"haxecheckstyle.externalSourceRoots": [],
|
||||
"haxecheckstyle.configurationFile": "checkstyle.json",
|
||||
"haxecheckstyle.codeSimilarityBufferSize": 100,
|
||||
|
||||
"lime.projectFile": "project.hxp",
|
||||
|
||||
"lime.targets": [
|
||||
{ "name": "windows", "enabled": true, "label": "Windows" },
|
||||
{ "name": "mac", "enabled": true, "label": "macOS" },
|
||||
{ "name": "linux", "enabled": true, "label": "Linux" },
|
||||
{ "name": "html5", "enabled": true, "label": "HTML5" },
|
||||
{ "name": "android", "enabled": true, "label": "Android" },
|
||||
{ "name": "ios", "enabled": true, "label": "iOS" },
|
||||
// Disabled targets
|
||||
{ "name": "hl", "enabled": false, "label": "HashLink" },
|
||||
{ "name": "air", "enabled": false },
|
||||
{ "name": "electron", "enabled": false },
|
||||
{ "name": "flash", "enabled": false },
|
||||
{ "name": "neko", "enabled": false },
|
||||
{ "name": "tvos", "enabled": false }
|
||||
],
|
||||
"lime.defaultTargetConfiguration": "Windows / Debug",
|
||||
"lime.targetConfigurations": [
|
||||
{
|
||||
"label": "Windows / Debug (Discord)",
|
||||
|
|
@ -103,21 +100,11 @@
|
|||
"target": "windows",
|
||||
"args": ["-debug", "-DANIMATE", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (FlxAnimate Test)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DANIMATE"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Straight to Freeplay)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DFREEPLAY", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Freeplay)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DFREEPLAY"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Straight to Play - Bopeebo Normal)",
|
||||
"target": "windows",
|
||||
|
|
@ -132,26 +119,6 @@
|
|||
"target": "windows",
|
||||
"args": ["-debug", "-DSONG=2hot", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Play - Bopeebo Normal)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DSONG=bopeebo -DDIFFICULTY=normal"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Conversation Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DDIALOGUE", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Conversation Test)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DDIALOGUE"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Results Screen Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DRESULTS"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Straight to Stage Editor)",
|
||||
"target": "windows",
|
||||
|
|
@ -172,36 +139,16 @@
|
|||
"target": "windows",
|
||||
"args": ["-debug", "-DHXVLC_LOGGING", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Straight to Animation Editor)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DANIMDEBUG"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Latency Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DLATENCY", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Latency Test)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DLATENCY"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Debug (Waveform Test)",
|
||||
"target": "windows",
|
||||
"args": ["-debug", "-DWAVEFORM", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "Windows / Release (GitHub Actions)",
|
||||
"target": "windows",
|
||||
"args": ["-release", "-DGITHUB_BUILD"]
|
||||
},
|
||||
{
|
||||
"label": "HashLink / Debug (Waveform Test)",
|
||||
"target": "hl",
|
||||
"args": ["-debug", "-DWAVEFORM"]
|
||||
},
|
||||
{
|
||||
"label": "HTML5 / Debug (Watch)",
|
||||
"target": "html5",
|
||||
|
|
@ -209,10 +156,7 @@
|
|||
}
|
||||
],
|
||||
"lime.buildTypes": [
|
||||
{
|
||||
"label": "Debug",
|
||||
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{ "label": "Debug", "args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"] },
|
||||
{
|
||||
"label": "Debug (Unlock Everything)",
|
||||
"args": ["-debug", "-DUNLOCK_EVERYTHING"]
|
||||
|
|
@ -225,10 +169,7 @@
|
|||
"label": "Debug (Straight to Chart Editor)",
|
||||
"args": ["-debug", "-DCHARTING", "-DFEATURE_DEBUG_FUNCTIONS"]
|
||||
},
|
||||
{
|
||||
"label": "Release",
|
||||
"args": ["-release"]
|
||||
},
|
||||
{ "label": "Release", "args": ["-release"] },
|
||||
{
|
||||
"label": "Release (GitHub Actions)",
|
||||
"args": ["-release", "-DGITHUB_BUILD"]
|
||||
|
|
@ -244,9 +185,6 @@
|
|||
],
|
||||
"vscord.app.privacyMode.enable": true,
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": ["/hmm.json"],
|
||||
"url": "./.vscode/schema/hmm.json"
|
||||
}
|
||||
{ "fileMatch": ["/hmm.json"], "url": "./.vscode/schema/hmm.json" }
|
||||
]
|
||||
}
|
||||
|
|
|
|||
2
assets
2
assets
|
|
@ -1 +1 @@
|
|||
Subproject commit d1ab5ea1199c5750fe749c3efd095ffc7bcf779b
|
||||
Subproject commit 1a961f111381eb3bfc452166c4e4b5a18b409781
|
||||
6
hmm.json
6
hmm.json
|
|
@ -111,7 +111,7 @@
|
|||
"name": "hxcpp",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "6546fa5c3ad1bac065f144745122ab5a6d4195ff",
|
||||
"ref": "5932340d095a7eea8635fe4d1355f1c0efd0b3c2",
|
||||
"url": "https://github.com/FunkinCrew/hxcpp"
|
||||
},
|
||||
{
|
||||
|
|
@ -146,7 +146,7 @@
|
|||
{
|
||||
"name": "hxvlc",
|
||||
"type": "haxelib",
|
||||
"version": "2.2.4"
|
||||
"version": "2.2.5"
|
||||
},
|
||||
{
|
||||
"name": "json2object",
|
||||
|
|
@ -173,7 +173,7 @@
|
|||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "27092822abf7b8c2ec2905053cf5435be4de838e",
|
||||
"ref": "d1596fe7daa9c479384ad2705e7813dbccf28839",
|
||||
"url": "https://github.com/FunkinCrew/lime"
|
||||
},
|
||||
{
|
||||
|
|
|
|||
87
project.hxp
87
project.hxp
|
|
@ -2049,7 +2049,7 @@ class Project extends HXProject
|
|||
@SuppressWarnings('checkstyle:Dynamic')
|
||||
function checkLibraries():Void
|
||||
{
|
||||
var outdatedLibraries:Map<String, Array<String>> = new Map<String, Array<String>>();
|
||||
var diffrentLibraries:Map<String, Array<String>> = new Map<String, Array<String>>();
|
||||
|
||||
var hmmData:Dynamic = haxe.Json.parse(sys.io.File.getContent(#if ios '../../../../../' + #end 'hmm.json'));
|
||||
|
||||
|
|
@ -2063,53 +2063,71 @@ class Project extends HXProject
|
|||
|
||||
var libraryCurrentVersion:String = readLibraryCurrentVersion(libraryName);
|
||||
|
||||
var libraryCurrentCommitHash:String = readLibraryGitCommitHash(libraryName);
|
||||
|
||||
if (libraryDev != "" && !isLibraryLocalGitDev(libraryName))
|
||||
{
|
||||
switch (library.type)
|
||||
{
|
||||
case 'haxelib':
|
||||
if (libraryDev != "")
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [libraryDev, library.version]);
|
||||
}
|
||||
else if (library.version != libraryCurrentVersion)
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [libraryCurrentVersion, library.version]);
|
||||
}
|
||||
diffrentLibraries.set(libraryName, [libraryDev, libraryCurrentCommitHash, library.version, 'haxelib']);
|
||||
case 'git':
|
||||
if (libraryDev != "" && !isLibraryGitDev(libraryName))
|
||||
if (libraryCurrentCommitHash != library.ref)
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [libraryDev, library.ref]);
|
||||
diffrentLibraries.set(libraryName, [libraryDev, libraryCurrentCommitHash, library.ref, 'git']);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var commitHash:String = readLibraryGitCommitHash(libraryName);
|
||||
|
||||
if (commitHash != library.ref && commitHash != "")
|
||||
switch (library.type)
|
||||
{
|
||||
outdatedLibraries.set(libraryName, [commitHash, library.ref]);
|
||||
case 'haxelib':
|
||||
if (library.version != libraryCurrentVersion)
|
||||
{
|
||||
diffrentLibraries.set(libraryName, ['', libraryCurrentVersion, library.version, 'haxelib']);
|
||||
}
|
||||
case 'git':
|
||||
if (libraryCurrentCommitHash != library.ref)
|
||||
{
|
||||
diffrentLibraries.set(libraryName, ['', libraryCurrentCommitHash, library.ref, 'git']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Lambda.count(outdatedLibraries) > 0)
|
||||
if (Lambda.count(diffrentLibraries) > 0)
|
||||
{
|
||||
warn("The following haxelibs diverge from the versions set in hmm.json.".bold().yellow());
|
||||
warn("They may be outdated, so it is recommended to abort compilation and run `hmm reinstall [library]` to update each library.".bold().yellow());
|
||||
warn("You may ignore this warning if your libraries are newer than the versions in hmm.json, or if you know what you're doing.".bold().yellow());
|
||||
warn("Some libraries differ from the versions defined in hmm.json.".bold().yellow());
|
||||
warn("To ensure consistency, it's recommended to stop the build and run `hmm reinstall [library]`.".bold().yellow());
|
||||
warn("If you're intentionally using development builds or newer versions, you can ignore this warning.".bold().yellow());
|
||||
|
||||
Sys.println('');
|
||||
|
||||
for (libraryName in outdatedLibraries.keys())
|
||||
for (libraryName in diffrentLibraries.keys())
|
||||
{
|
||||
var versions:Null<Array<String>> = outdatedLibraries.get(libraryName);
|
||||
if (versions == null) continue;
|
||||
var infos:Null<Array<String>> = diffrentLibraries.get(libraryName);
|
||||
|
||||
var outdatedVersion:String = versions[0];
|
||||
var expectedVersion:String = versions[1];
|
||||
if (infos == null) continue;
|
||||
|
||||
Sys.println("- " + libraryName.replace(",", ".") + (haxe.io.Path.isAbsolute(outdatedVersion) ? " (Development Build)".blue().bold() : ""));
|
||||
Sys.println((" Current version: " + outdatedVersion).red());
|
||||
Sys.println((" Expected version: " + expectedVersion).green());
|
||||
var devPath:String = infos[0];
|
||||
var currentVersion:String = infos[1];
|
||||
var expectedVersion:String = infos[2];
|
||||
var libType:String = infos[3];
|
||||
|
||||
if (haxe.io.Path.isAbsolute(devPath))
|
||||
{
|
||||
Sys.println("- " + libraryName.replace(",", ".") + " (Development Build)".blue().bold());
|
||||
Sys.println((" Path: " + haxe.io.Path.removeTrailingSlashes(devPath)).blue());
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
Sys.println("- " + libraryName.replace(",", "."));
|
||||
}
|
||||
|
||||
Sys.println((" Current: " + currentVersion).red());
|
||||
Sys.println((" Expected: " + expectedVersion).green());
|
||||
}
|
||||
|
||||
Sys.println('');
|
||||
|
|
@ -2136,11 +2154,20 @@ class Project extends HXProject
|
|||
|
||||
static function readLibraryGitCommitHash(libraryName:String):String
|
||||
{
|
||||
var commit:String = '';
|
||||
|
||||
var gitProccess = new sys.io.Process("git", ["-C", getLibraryGitPath(libraryName), "rev-parse", "HEAD"]);
|
||||
|
||||
gitProccess.exitCode(true);
|
||||
commit = gitProccess.stdout.readAll().toString().trim();
|
||||
|
||||
return gitProccess.stdout.readAll().toString().trim();
|
||||
if (commit.length <= 0)
|
||||
{
|
||||
gitProccess = new sys.io.Process("git", ["-C", readLibraryDev(libraryName), "rev-parse", "HEAD"]);
|
||||
gitProccess.exitCode(true);
|
||||
commit = gitProccess.stdout.readAll().toString().trim();
|
||||
}
|
||||
|
||||
return commit;
|
||||
}
|
||||
|
||||
static function getLibraryCurrentFile(libraryName:String):String
|
||||
|
|
@ -2158,7 +2185,7 @@ class Project extends HXProject
|
|||
return haxe.io.Path.join([haxe.io.Path.addTrailingSlash(Sys.getCwd()), '.haxelib', libraryName, 'git']);
|
||||
}
|
||||
|
||||
static function isLibraryGitDev(libraryName:String):Bool
|
||||
static function isLibraryLocalGitDev(libraryName:String):Bool
|
||||
{
|
||||
final gitPath:String = getLibraryGitPath(libraryName);
|
||||
final devFile:String = getLibraryDevFile(libraryName);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,10 @@ package funkin.audio.waveform;
|
|||
import funkin.graphics.rendering.MeshRender;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
/**
|
||||
* A sprite which displays the waveform of audio data.
|
||||
* Generate a WaveformData and provide it to this sprite.
|
||||
*/
|
||||
class WaveformSprite extends MeshRender
|
||||
{
|
||||
static final DEFAULT_COLOR:FlxColor = FlxColor.WHITE;
|
||||
|
|
@ -18,14 +22,17 @@ class WaveformSprite extends MeshRender
|
|||
* Do this any time the data or drawable area of the waveform changes.
|
||||
* This often (but not always) needs to be done every frame.
|
||||
*/
|
||||
var isWaveformDirty:Bool = true;
|
||||
var isWaveformDirty:Bool;
|
||||
|
||||
/**
|
||||
* If true, force the waveform to redraw every frame.
|
||||
* Useful if the waveform's clipRect is constantly changing.
|
||||
*/
|
||||
public var forceUpdate:Bool = false;
|
||||
public var forceUpdate:Bool;
|
||||
|
||||
/**
|
||||
* The data to render the waveform with.
|
||||
*/
|
||||
public var waveformData(default, set):Null<WaveformData>;
|
||||
|
||||
function set_waveformData(value:Null<WaveformData>):Null<WaveformData>
|
||||
|
|
@ -52,6 +59,9 @@ class WaveformSprite extends MeshRender
|
|||
return waveformColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the Waveform is horizontal or vertical.
|
||||
*/
|
||||
public var orientation(default, set):WaveformOrientation;
|
||||
|
||||
function set_orientation(value:WaveformOrientation):WaveformOrientation
|
||||
|
|
@ -68,7 +78,7 @@ class WaveformSprite extends MeshRender
|
|||
*/
|
||||
public var time(default, set):Float;
|
||||
|
||||
function set_time(value:Float)
|
||||
function set_time(value:Float):Float
|
||||
{
|
||||
if (time == value) return value;
|
||||
|
||||
|
|
@ -77,13 +87,22 @@ class WaveformSprite extends MeshRender
|
|||
return time;
|
||||
}
|
||||
|
||||
override function set_visible(value:Bool):Bool
|
||||
{
|
||||
if (visible == value) return value;
|
||||
|
||||
visible = value;
|
||||
isWaveformDirty = true;
|
||||
return visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* The duration, in seconds, that the waveform represents.
|
||||
* The section of waveform from `time` to `time + duration` and `width` are used to determine how many samples each pixel represents.
|
||||
*/
|
||||
public var duration(default, set):Float;
|
||||
|
||||
function set_duration(value:Float)
|
||||
function set_duration(value:Float):Float
|
||||
{
|
||||
if (duration == value) return value;
|
||||
|
||||
|
|
@ -120,13 +139,13 @@ class WaveformSprite extends MeshRender
|
|||
*
|
||||
* NOTE: This is technically doubled since it's applied above and below the center of the waveform.
|
||||
*/
|
||||
public var minWaveformSize:Int = 1;
|
||||
public var minWaveformSize:Int;
|
||||
|
||||
/**
|
||||
* A multiplier on the size of the waveform.
|
||||
* Still capped at the width and height set for the sprite.
|
||||
*/
|
||||
public var amplitude:Float = 1.0;
|
||||
public var amplitude:Float;
|
||||
|
||||
public function new(?waveformData:WaveformData, ?orientation:WaveformOrientation, ?color:FlxColor, ?duration:Float)
|
||||
{
|
||||
|
|
@ -135,6 +154,11 @@ class WaveformSprite extends MeshRender
|
|||
this.width = DEFAULT_WIDTH;
|
||||
this.height = DEFAULT_HEIGHT;
|
||||
|
||||
this.minWaveformSize = 1;
|
||||
this.amplitude = 1.0;
|
||||
this.isWaveformDirty = true;
|
||||
this.forceUpdate = false;
|
||||
|
||||
this.waveformData = waveformData;
|
||||
this.orientation = orientation ?? DEFAULT_ORIENTATION;
|
||||
this.time = 0.0;
|
||||
|
|
@ -151,7 +175,7 @@ class WaveformSprite extends MeshRender
|
|||
isWaveformDirty = true;
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float)
|
||||
public override function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
|
|
@ -170,6 +194,11 @@ class WaveformSprite extends MeshRender
|
|||
makeGraphic(1, 1, this.waveformColor);
|
||||
}
|
||||
|
||||
public override function draw():Void
|
||||
{
|
||||
super.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param offsetX Horizontal offset to draw the waveform at, in samples.
|
||||
*/
|
||||
|
|
@ -183,12 +212,12 @@ class WaveformSprite extends MeshRender
|
|||
|
||||
this.clear();
|
||||
|
||||
if (waveformData == null) return;
|
||||
if (waveformData == null || !this.visible) return;
|
||||
|
||||
// Center point of the waveform. When horizontal this is half the height, when vertical this is half the width.
|
||||
var waveformCenterPos:Int = orientation == HORIZONTAL ? Std.int(this.height / 2) : Std.int(this.width / 2);
|
||||
|
||||
var oneSecondInIndices:Int = waveformData.secondsToIndex(1);
|
||||
// var oneSecondInIndices:Int = waveformData.secondsToIndex(1)
|
||||
|
||||
var startTime:Float = time;
|
||||
var endTime:Float = time + duration;
|
||||
|
|
@ -223,7 +252,10 @@ class WaveformSprite extends MeshRender
|
|||
|
||||
var isBeforeClipRect:Bool = (clipRect != null) && ((orientation == HORIZONTAL) ? pixelPos < clipRect.x : pixelPos < clipRect.y);
|
||||
|
||||
if (isBeforeClipRect) continue;
|
||||
if (isBeforeClipRect)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var isAfterClipRect:Bool = (clipRect != null)
|
||||
&& ((orientation == HORIZONTAL) ? pixelPos > (clipRect.x + clipRect.width) : pixelPos > (clipRect.y + clipRect.height));
|
||||
|
|
@ -426,20 +458,40 @@ class WaveformSprite extends MeshRender
|
|||
}
|
||||
}
|
||||
|
||||
public static function buildFromWaveformData(data:WaveformData, ?orientation:WaveformOrientation, ?color:FlxColor, ?duration:Float)
|
||||
/**
|
||||
* Build a WaveformSprite from waveform data.
|
||||
* @param data The data for the waveform to use.
|
||||
* @param orientation Whether the waveform should be horizontal or vertical.
|
||||
* @param color The color of the waveform.
|
||||
* @param duration The width of the waveform, in seconds.
|
||||
*
|
||||
* @return The resulting WaveformSprite.
|
||||
*/
|
||||
public static function buildFromWaveformData(data:WaveformData, ?orientation:WaveformOrientation, ?color:FlxColor, ?duration:Float):WaveformSprite
|
||||
{
|
||||
return new WaveformSprite(data, orientation, color, duration);
|
||||
}
|
||||
|
||||
public static function buildFromFunkinSound(sound:FunkinSound, ?orientation:WaveformOrientation, ?color:FlxColor, ?duration:Float)
|
||||
/**
|
||||
* Build a WaveformSprite from a FunkinSound's waveform data.
|
||||
* @param sound The audio for the waveform to use.
|
||||
* @param orientation Whether the waveform should be horizontal or vertical.
|
||||
* @param color The color of the waveform.
|
||||
* @param duration The width of the waveform, in seconds.
|
||||
*
|
||||
* @return The resulting WaveformSprite.
|
||||
*/
|
||||
public static function buildFromFunkinSound(sound:FunkinSound, ?orientation:WaveformOrientation, ?color:FlxColor, ?duration:Float):WaveformSprite
|
||||
{
|
||||
// TODO: Build waveform data from FunkinSound.
|
||||
var data = null;
|
||||
var data = WaveformDataParser.interpretFlxSound(sound);
|
||||
|
||||
return buildFromWaveformData(data, orientation, color, duration);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The possible orientations of a waveform.
|
||||
*/
|
||||
enum WaveformOrientation
|
||||
{
|
||||
HORIZONTAL;
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
|||
* @param id The ID of the entry.
|
||||
* @return `true` if the entry has an attached script, `false` otherwise.
|
||||
*/
|
||||
public function isScriptedEntry(id:String):Bool
|
||||
public function isScriptedEntry(id:String, ?params:Null<P>):Bool
|
||||
{
|
||||
return scriptedEntryIds.exists(id);
|
||||
}
|
||||
|
|
@ -167,7 +167,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
|
|||
* @param id The ID of the entry.
|
||||
* @return The class name, or `null` if it does not exist.
|
||||
*/
|
||||
public function getScriptedEntryClassName(id:String):Null<String>
|
||||
public function getScriptedEntryClassName(id:String, ?params:Null<P>):Null<String>
|
||||
{
|
||||
return scriptedEntryIds.get(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,6 +129,29 @@ using funkin.data.song.migrator.SongDataMigrator;
|
|||
return parseEntryMetadataRaw(contents);
|
||||
}
|
||||
|
||||
public override function isScriptedEntry(id:String, ?params:Null<SongEntryParams>)
|
||||
{
|
||||
var variation:String = params?.variation ?? Constants.DEFAULT_VARIATION;
|
||||
if (variation != Constants.DEFAULT_VARIATION)
|
||||
{
|
||||
return scriptedSongVariations.exists('${id}:${variation}');
|
||||
}
|
||||
return super.isScriptedEntry(id, params);
|
||||
}
|
||||
|
||||
public override function getScriptedEntryClassName(id:String, ?params:Null<SongEntryParams>):Null<String>
|
||||
{
|
||||
var variation:String = params?.variation ?? Constants.DEFAULT_VARIATION;
|
||||
if (variation != Constants.DEFAULT_VARIATION)
|
||||
{
|
||||
final variationSongId:ScriptedSong = cast scriptedSongVariations.get('${id}:${variation}');
|
||||
@:privateAccess
|
||||
var path:String = variationSongId._asc._c.name;
|
||||
return path;
|
||||
}
|
||||
return super.getScriptedEntryClassName(id, params);
|
||||
}
|
||||
|
||||
/**
|
||||
* We override `fetchEntry` to handle song variations!
|
||||
*/
|
||||
|
|
|
|||
26
source/funkin/external/android/KeyboardUtil.hx
vendored
Normal file
26
source/funkin/external/android/KeyboardUtil.hx
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package funkin.external.android;
|
||||
|
||||
#if android
|
||||
import lime.system.JNI;
|
||||
|
||||
/**
|
||||
* Utility class for keyboard detection.
|
||||
*/
|
||||
class KeyboardUtil
|
||||
{
|
||||
/**
|
||||
* Returns `true` if a keyboard is currently connected to the device.
|
||||
*/
|
||||
public static var keyboardConnected(get, never):Bool;
|
||||
|
||||
@:noCompletion
|
||||
static function get_keyboardConnected():Bool
|
||||
{
|
||||
final method:Null<Dynamic> = JNIUtil.createStaticMethod('funkin/util/KeyboardUtil', 'isKeyboardConnected', '()Z');
|
||||
|
||||
if (method == null) return false;
|
||||
|
||||
return inline JNI.callStatic(method, []);
|
||||
}
|
||||
}
|
||||
#end
|
||||
14
source/funkin/external/android/java/funkin/util/KeyboardUtil.java
vendored
Normal file
14
source/funkin/external/android/java/funkin/util/KeyboardUtil.java
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
package funkin.util;
|
||||
|
||||
import org.haxe.extension.Extension;
|
||||
|
||||
public class KeyboardUtil
|
||||
{
|
||||
public static boolean isKeyboardConnected()
|
||||
{
|
||||
if (Extension.mainContext == null) return false;
|
||||
|
||||
// KEYBOARD_UNDEFINED = 0, KEYBOARD_NOKEYS = 1
|
||||
return Extension.mainContext.getResources().getConfiguration().keyboard > 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -21,6 +21,7 @@ import animate.internal.elements.AtlasInstance;
|
|||
import animate.internal.elements.SymbolInstance;
|
||||
import animate.FlxAnimate;
|
||||
import animate.FlxAnimateFrames;
|
||||
import haxe.io.Path;
|
||||
|
||||
using StringTools;
|
||||
|
||||
|
|
@ -104,10 +105,41 @@ class FunkinSprite extends FlxAnimate
|
|||
/**
|
||||
* @param x Starting X position
|
||||
* @param y Starting Y position
|
||||
* @param path The asset path for the graphic
|
||||
* @param atlasSettings The optional settings for the texture atlas
|
||||
*/
|
||||
public function new(?x:Float = 0, ?y:Float = 0)
|
||||
public function new(?x:Float = 0, ?y:Float = 0, ?path:String, ?atlasSettings:AtlasSpriteSettings)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
if (path != null)
|
||||
{
|
||||
var ext:String = Path.extension(path);
|
||||
|
||||
switch (ext)
|
||||
{
|
||||
case 'png':
|
||||
this.loadGraphic(path);
|
||||
|
||||
case '':
|
||||
// Do the opposite of Paths.animateAtlas since that function is called in loadTextureAtlas.
|
||||
var lib:String = Paths.getLibrary(path);
|
||||
|
||||
if (lib == 'preload')
|
||||
{
|
||||
path = path.replace('assets/images/', '');
|
||||
}
|
||||
else
|
||||
{
|
||||
path = path.replace('$lib:assets/$lib/images/', '');
|
||||
}
|
||||
|
||||
this.loadTextureAtlas(path, lib, atlasSettings);
|
||||
|
||||
default:
|
||||
FlxG.log.warn('Texture path $path is not a valid path. Make sure the path points to either an image or a folder with the texture atlas files!');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override function initVars():Void
|
||||
|
|
@ -419,6 +451,46 @@ class FunkinSprite extends FlxAnimate
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets every frame on every symbol that starts with the given keyword.
|
||||
* @param keyword The keyword to search for.
|
||||
* @return An array of frames.
|
||||
*/
|
||||
public function getFramesWithKeyword(keyword:String):Array<animate.internal.Frame>
|
||||
{
|
||||
if (!this.isAnimate)
|
||||
{
|
||||
trace('WARNING: getFramesWithKeyword() only works texture atlases!');
|
||||
return [];
|
||||
}
|
||||
|
||||
var symbolItems:Array<animate.internal.SymbolItem> = [];
|
||||
var frames:Array<animate.internal.Frame> = [];
|
||||
|
||||
@:privateAccess
|
||||
for (symbol in this.library.dictionary.keys())
|
||||
{
|
||||
var symbolItem:Null<animate.internal.SymbolItem> = this.library.getSymbol(symbol);
|
||||
if (symbolItem == null) continue;
|
||||
|
||||
if (symbolItem.name.contains(keyword))
|
||||
{
|
||||
symbolItems.push(symbolItem);
|
||||
}
|
||||
}
|
||||
|
||||
for (symbolItem in symbolItems)
|
||||
{
|
||||
symbolItem.timeline.forEachLayer((layer) -> {
|
||||
layer.forEachFrame((frame) -> {
|
||||
frames.push(frame);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return frames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current animation ID.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -70,6 +70,11 @@ class MeshRender extends FlxStrip
|
|||
add_tri(a, c, d);
|
||||
}
|
||||
|
||||
public override function draw():Void
|
||||
{
|
||||
super.draw();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a quad from four points.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -19,19 +19,20 @@ import flixel.math.FlxPoint;
|
|||
*/
|
||||
class Controls extends FlxActionSet
|
||||
{
|
||||
/**
|
||||
/*
|
||||
* A list of actions that a player would invoke via some input device.
|
||||
* Uses FlxActions to funnel various inputs to a single action.
|
||||
*/
|
||||
var _ui_up = new FunkinAction(Action.UI_UP);
|
||||
|
||||
var _ui_left = new FunkinAction(Action.UI_LEFT);
|
||||
var _ui_right = new FunkinAction(Action.UI_RIGHT);
|
||||
var _ui_down = new FunkinAction(Action.UI_DOWN);
|
||||
|
||||
var _note_up = new FunkinAction(Action.NOTE_UP);
|
||||
var _note_left = new FunkinAction(Action.NOTE_LEFT);
|
||||
var _note_right = new FunkinAction(Action.NOTE_RIGHT);
|
||||
var _note_down = new FunkinAction(Action.NOTE_DOWN);
|
||||
|
||||
var _accept = new FunkinAction(Action.ACCEPT);
|
||||
var _back = new FunkinAction(Action.BACK);
|
||||
var _pause = new FunkinAction(Action.PAUSE);
|
||||
|
|
@ -126,26 +127,6 @@ class Controls extends FlxActionSet
|
|||
inline function get_UI_DOWN_R()
|
||||
return _ui_down.checkJustReleased();
|
||||
|
||||
public var UI_UP_GAMEPAD(get, never):Bool;
|
||||
|
||||
inline function get_UI_UP_GAMEPAD()
|
||||
return _ui_up.checkPressedGamepad();
|
||||
|
||||
public var UI_LEFT_GAMEPAD(get, never):Bool;
|
||||
|
||||
inline function get_UI_LEFT_GAMEPAD()
|
||||
return _ui_left.checkPressedGamepad();
|
||||
|
||||
public var UI_RIGHT_GAMEPAD(get, never):Bool;
|
||||
|
||||
inline function get_UI_RIGHT_GAMEPAD()
|
||||
return _ui_right.checkPressedGamepad();
|
||||
|
||||
public var UI_DOWN_GAMEPAD(get, never):Bool;
|
||||
|
||||
inline function get_UI_DOWN_GAMEPAD()
|
||||
return _ui_down.checkPressedGamepad();
|
||||
|
||||
public var NOTE_UP(get, never):Bool;
|
||||
|
||||
inline function get_NOTE_UP()
|
||||
|
|
@ -527,9 +508,8 @@ class Controls extends FlxActionSet
|
|||
* Calls a function passing each action bound by the specified control
|
||||
* @param control
|
||||
* @param func
|
||||
* @return ->Void)
|
||||
*/
|
||||
function forEachBound(control:Control, func:FunkinAction->FlxInputState->Void)
|
||||
function forEachBound(control:Control, func:FunkinAction->FlxInputState->Void):Void
|
||||
{
|
||||
switch (control)
|
||||
{
|
||||
|
|
@ -624,7 +604,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int)
|
||||
public function replaceBinding(control:Control, device:Device, toAdd:Int, toRemove:Int):Void
|
||||
{
|
||||
if (toAdd == toRemove) return;
|
||||
|
||||
|
|
@ -638,7 +618,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState)
|
||||
function replaceKey(action:FlxActionDigital, toAdd:FlxKey, toRemove:FlxKey, state:FlxInputState):Void
|
||||
{
|
||||
if (action.inputs.length == 0)
|
||||
{
|
||||
|
|
@ -689,7 +669,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState)
|
||||
function replaceButton(action:FlxActionDigital, deviceID:Int, toAdd:FlxGamepadInputID, toRemove:FlxGamepadInputID, state:FlxInputState):Void
|
||||
{
|
||||
if (action.inputs.length == 0)
|
||||
{
|
||||
|
|
@ -717,7 +697,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
public function copyFrom(controls:Controls, ?device:Device)
|
||||
public function copyFrom(controls:Controls, ?device:Device):Void
|
||||
{
|
||||
for (name in controls.byName.keys())
|
||||
{
|
||||
|
|
@ -744,7 +724,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
inline public function copyTo(controls:Controls, ?device:Device)
|
||||
inline public function copyTo(controls:Controls, ?device:Device):Void
|
||||
{
|
||||
controls.copyFrom(this, device);
|
||||
}
|
||||
|
|
@ -767,7 +747,7 @@ class Controls extends FlxActionSet
|
|||
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
|
||||
* If binder is a literal you can inline this
|
||||
*/
|
||||
public function bindKeys(control:Control, keys:Array<FlxKey>)
|
||||
public function bindKeys(control:Control, keys:Array<FlxKey>):Void
|
||||
{
|
||||
forEachBound(control, function(action, state) addKeys(action, keys, state));
|
||||
}
|
||||
|
|
@ -776,12 +756,12 @@ class Controls extends FlxActionSet
|
|||
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
|
||||
* If binder is a literal you can inline this
|
||||
*/
|
||||
public function unbindKeys(control:Control, keys:Array<FlxKey>)
|
||||
public function unbindKeys(control:Control, keys:Array<FlxKey>):Void
|
||||
{
|
||||
forEachBound(control, function(action, _) removeKeys(action, keys));
|
||||
}
|
||||
|
||||
static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState)
|
||||
static function addKeys(action:FlxActionDigital, keys:Array<FlxKey>, state:FlxInputState):Void
|
||||
{
|
||||
for (key in keys)
|
||||
{
|
||||
|
|
@ -790,7 +770,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>)
|
||||
static function removeKeys(action:FlxActionDigital, keys:Array<FlxKey>):Void
|
||||
{
|
||||
var i = action.inputs.length;
|
||||
while (i-- > 0)
|
||||
|
|
@ -951,7 +931,7 @@ class Controls extends FlxActionSet
|
|||
return [];
|
||||
}
|
||||
|
||||
function removeKeyboard()
|
||||
function removeKeyboard():Void
|
||||
{
|
||||
for (action in this.digitalActions)
|
||||
{
|
||||
|
|
@ -971,16 +951,6 @@ class Controls extends FlxActionSet
|
|||
fromSaveData(padData, Gamepad(id));
|
||||
}
|
||||
|
||||
public function getGamepadIds():Array<Int>
|
||||
{
|
||||
return gamepadsAdded;
|
||||
}
|
||||
|
||||
public function getGamepads():Array<FlxGamepad>
|
||||
{
|
||||
return [for (id in gamepadsAdded) FlxG.gamepads.getByID(id)];
|
||||
}
|
||||
|
||||
inline function addGamepadLiteral(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
|
||||
{
|
||||
gamepadsAdded.push(id);
|
||||
|
|
@ -1004,7 +974,7 @@ class Controls extends FlxActionSet
|
|||
gamepadsAdded.remove(deviceID);
|
||||
}
|
||||
|
||||
public function addDefaultGamepad(id):Void
|
||||
public function addDefaultGamepad(id:Int):Void
|
||||
{
|
||||
addGamepadLiteral(id, [
|
||||
Control.ACCEPT => getDefaultGamepadBinds(Control.ACCEPT),
|
||||
|
|
@ -1048,32 +1018,32 @@ class Controls extends FlxActionSet
|
|||
|
||||
function getDefaultGamepadBinds(control:Control):Array<FlxGamepadInputID>
|
||||
{
|
||||
switch (control)
|
||||
return switch (control)
|
||||
{
|
||||
case Control.ACCEPT:
|
||||
return [#if switch B #else A #end];
|
||||
[A];
|
||||
case Control.BACK:
|
||||
return [#if switch A #else B #end];
|
||||
[B];
|
||||
case Control.UI_UP:
|
||||
return [DPAD_UP, LEFT_STICK_DIGITAL_UP];
|
||||
[DPAD_UP, LEFT_STICK_DIGITAL_UP];
|
||||
case Control.UI_DOWN:
|
||||
return [DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN];
|
||||
[DPAD_DOWN, LEFT_STICK_DIGITAL_DOWN];
|
||||
case Control.UI_LEFT:
|
||||
return [DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT];
|
||||
[DPAD_LEFT, LEFT_STICK_DIGITAL_LEFT];
|
||||
case Control.UI_RIGHT:
|
||||
return [DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT];
|
||||
[DPAD_RIGHT, LEFT_STICK_DIGITAL_RIGHT];
|
||||
case Control.NOTE_UP:
|
||||
return [DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP];
|
||||
[DPAD_UP, Y, LEFT_STICK_DIGITAL_UP, RIGHT_STICK_DIGITAL_UP];
|
||||
case Control.NOTE_DOWN:
|
||||
return [DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN];
|
||||
[DPAD_DOWN, A, LEFT_STICK_DIGITAL_DOWN, RIGHT_STICK_DIGITAL_DOWN];
|
||||
case Control.NOTE_LEFT:
|
||||
return [DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT];
|
||||
[DPAD_LEFT, X, LEFT_STICK_DIGITAL_LEFT, RIGHT_STICK_DIGITAL_LEFT];
|
||||
case Control.NOTE_RIGHT:
|
||||
return [DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT];
|
||||
[DPAD_RIGHT, B, LEFT_STICK_DIGITAL_RIGHT, RIGHT_STICK_DIGITAL_RIGHT];
|
||||
case Control.PAUSE:
|
||||
return [START];
|
||||
[START];
|
||||
case Control.RESET:
|
||||
return [FlxGamepadInputID.BACK]; // Back (i.e. Select)
|
||||
[FlxGamepadInputID.BACK]; // Back (i.e. Select)
|
||||
case Control.WINDOW_FULLSCREEN:
|
||||
[];
|
||||
#if FEATURE_SCREENSHOTS
|
||||
|
|
@ -1081,19 +1051,19 @@ class Controls extends FlxActionSet
|
|||
[];
|
||||
#end
|
||||
case Control.CUTSCENE_ADVANCE:
|
||||
return [A];
|
||||
[A];
|
||||
case Control.FREEPLAY_FAVORITE:
|
||||
return [Y]; // Back (i.e. Select)
|
||||
[Y]; // Back (i.e. Select)
|
||||
case Control.FREEPLAY_LEFT:
|
||||
return [LEFT_SHOULDER];
|
||||
[LEFT_SHOULDER];
|
||||
case Control.FREEPLAY_RIGHT:
|
||||
return [RIGHT_SHOULDER];
|
||||
[RIGHT_SHOULDER];
|
||||
case Control.FREEPLAY_CHAR_SELECT:
|
||||
return [X];
|
||||
[X];
|
||||
case Control.FREEPLAY_JUMP_TO_TOP:
|
||||
return [RIGHT_STICK_DIGITAL_UP];
|
||||
[RIGHT_STICK_DIGITAL_UP];
|
||||
case Control.FREEPLAY_JUMP_TO_BOTTOM:
|
||||
return [RIGHT_STICK_DIGITAL_DOWN];
|
||||
[RIGHT_STICK_DIGITAL_DOWN];
|
||||
case Control.VOLUME_UP:
|
||||
[];
|
||||
case Control.VOLUME_DOWN:
|
||||
|
|
@ -1115,16 +1085,15 @@ class Controls extends FlxActionSet
|
|||
case Control.DEBUG_DISPLAY:
|
||||
[];
|
||||
default:
|
||||
// Fallthrough.
|
||||
[];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
|
||||
* If binder is a literal you can inline this
|
||||
*/
|
||||
public function bindButtons(control:Control, id, buttons)
|
||||
public function bindButtons(control:Control, id:Int, buttons):Void
|
||||
{
|
||||
forEachBound(control, function(action, state) addButtons(action, buttons, state, id));
|
||||
}
|
||||
|
|
@ -1133,12 +1102,12 @@ class Controls extends FlxActionSet
|
|||
* Sets all actions that pertain to the binder to trigger when the supplied keys are used.
|
||||
* If binder is a literal you can inline this
|
||||
*/
|
||||
public function unbindButtons(control:Control, gamepadID:Int, buttons)
|
||||
public function unbindButtons(control:Control, gamepadID:Int, buttons):Void
|
||||
{
|
||||
forEachBound(control, function(action, _) removeButtons(action, gamepadID, buttons));
|
||||
}
|
||||
|
||||
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id)
|
||||
inline static function addButtons(action:FlxActionDigital, buttons:Array<FlxGamepadInputID>, state, id:Int):Void
|
||||
{
|
||||
for (button in buttons)
|
||||
{
|
||||
|
|
@ -1147,7 +1116,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>)
|
||||
static function removeButtons(action:FlxActionDigital, gamepadID:Int, buttons:Array<FlxGamepadInputID>):Void
|
||||
{
|
||||
var i = action.inputs.length;
|
||||
while (i-- > 0)
|
||||
|
|
@ -1177,17 +1146,6 @@ class Controls extends FlxActionSet
|
|||
return list;
|
||||
}
|
||||
|
||||
public function removeDevice(device:Device)
|
||||
{
|
||||
switch (device)
|
||||
{
|
||||
case Keys:
|
||||
setKeyboardScheme(None);
|
||||
case Gamepad(id):
|
||||
removeGamepad(id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* NOTE: When loading controls:
|
||||
* An EMPTY array means the control is uninitialized and needs to be reset to default.
|
||||
|
|
@ -1270,7 +1228,7 @@ class Controls extends FlxActionSet
|
|||
return isEmpty ? null : data;
|
||||
}
|
||||
|
||||
static function isDevice(input:FlxActionInput, device:Device)
|
||||
static function isDevice(input:FlxActionInput, device:Device):Bool
|
||||
{
|
||||
return switch (device)
|
||||
{
|
||||
|
|
@ -1279,7 +1237,7 @@ class Controls extends FlxActionSet
|
|||
}
|
||||
}
|
||||
|
||||
inline static function isGamepad(input:FlxActionInput, deviceID:Int)
|
||||
inline static function isGamepad(input:FlxActionInput, deviceID:Int):Bool
|
||||
{
|
||||
return input.device == GAMEPAD && (deviceID == FlxInputDeviceID.ALL || input.deviceID == deviceID);
|
||||
}
|
||||
|
|
@ -1393,14 +1351,8 @@ class FunkinAction extends FlxActionDigital
|
|||
|
||||
public function checkMultiFiltered(?filterTriggers:Array<FlxInputState>, ?filterDevices:Array<FlxInputDevice>):Bool
|
||||
{
|
||||
if (filterTriggers == null)
|
||||
{
|
||||
filterTriggers = [PRESSED, JUST_PRESSED];
|
||||
}
|
||||
if (filterDevices == null)
|
||||
{
|
||||
filterDevices = [];
|
||||
}
|
||||
filterTriggers ??= [PRESSED, JUST_PRESSED];
|
||||
filterDevices ??= [];
|
||||
|
||||
// Perform checkFiltered for each combination.
|
||||
for (i in filterTriggers)
|
||||
|
|
@ -1431,6 +1383,7 @@ class FunkinAction extends FlxActionDigital
|
|||
* @param action The action to check for.
|
||||
* @param filterTrigger Optionally filter by trigger condition (`JUST_PRESSED`, `PRESSED`, `JUST_RELEASED`, `RELEASED`).
|
||||
* @param filterDevice Optionally filter by device (`KEYBOARD`, `MOUSE`, `GAMEPAD`, `OTHER`).
|
||||
* @return bool if our input has been triggered
|
||||
*/
|
||||
public function checkFiltered(?filterTrigger:FlxInputState, ?filterDevice:FlxInputDevice):Bool
|
||||
{
|
||||
|
|
@ -1442,20 +1395,22 @@ class FunkinAction extends FlxActionDigital
|
|||
{
|
||||
return cacheEntry.value;
|
||||
}
|
||||
// Use a for loop instead so we can remove inputs while iterating.
|
||||
|
||||
// We don't return early because we need to call check() on ALL inputs.
|
||||
var result = false;
|
||||
var len = inputs != null ? inputs.length : 0;
|
||||
for (i in 0...len)
|
||||
_x = null;
|
||||
_y = null;
|
||||
|
||||
_timestamp = FlxG.game.ticks;
|
||||
triggered = false;
|
||||
|
||||
var i = inputs?.length ?? 0;
|
||||
while (i-- > 0) // Iterate backwards, since we may remove items
|
||||
{
|
||||
var j = len - i - 1;
|
||||
var input = inputs[j];
|
||||
var input = inputs[i];
|
||||
|
||||
// Filter out dead inputs.
|
||||
if (input.destroyed)
|
||||
{
|
||||
inputs.splice(j, 1);
|
||||
inputs.remove(input);
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -1477,14 +1432,13 @@ class FunkinAction extends FlxActionDigital
|
|||
// Check whether the input has triggered.
|
||||
if (input.check(this))
|
||||
{
|
||||
result = true;
|
||||
triggered = true;
|
||||
}
|
||||
}
|
||||
|
||||
// We need to cache this result.
|
||||
cache.set(key, {timestamp: FlxG.game.ticks, value: result});
|
||||
cache.set(key, {timestamp: FlxG.game.ticks, value: triggered});
|
||||
|
||||
return result;
|
||||
return triggered;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1505,10 +1459,10 @@ enum Control
|
|||
UI_DOWN;
|
||||
UI_UP;
|
||||
UI_RIGHT;
|
||||
RESET;
|
||||
ACCEPT;
|
||||
BACK;
|
||||
PAUSE;
|
||||
RESET;
|
||||
// CUTSCENE
|
||||
CUTSCENE_ADVANCE;
|
||||
// FREEPLAY
|
||||
|
|
@ -1539,27 +1493,11 @@ enum abstract Action(String) to String from String
|
|||
var NOTE_LEFT = "note_left";
|
||||
var NOTE_RIGHT = "note_right";
|
||||
var NOTE_DOWN = "note_down";
|
||||
var NOTE_UP_P = "note_up-press";
|
||||
var NOTE_LEFT_P = "note_left-press";
|
||||
var NOTE_RIGHT_P = "note_right-press";
|
||||
var NOTE_DOWN_P = "note_down-press";
|
||||
var NOTE_UP_R = "note_up-release";
|
||||
var NOTE_LEFT_R = "note_left-release";
|
||||
var NOTE_RIGHT_R = "note_right-release";
|
||||
var NOTE_DOWN_R = "note_down-release";
|
||||
// UI
|
||||
var UI_UP = "ui_up";
|
||||
var UI_LEFT = "ui_left";
|
||||
var UI_RIGHT = "ui_right";
|
||||
var UI_DOWN = "ui_down";
|
||||
var UI_UP_P = "ui_up-press";
|
||||
var UI_LEFT_P = "ui_left-press";
|
||||
var UI_RIGHT_P = "ui_right-press";
|
||||
var UI_DOWN_P = "ui_down-press";
|
||||
var UI_UP_R = "ui_up-release";
|
||||
var UI_LEFT_R = "ui_left-release";
|
||||
var UI_RIGHT_R = "ui_right-release";
|
||||
var UI_DOWN_R = "ui_down-release";
|
||||
var ACCEPT = "accept";
|
||||
var BACK = "back";
|
||||
var PAUSE = "pause";
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ import funkin.mobile.ui.FunkinHitbox;
|
|||
import funkin.play.notes.NoteDirection;
|
||||
import openfl.events.KeyboardEvent;
|
||||
import openfl.events.TouchEvent;
|
||||
#if android
|
||||
import funkin.external.android.KeyboardUtil;
|
||||
#end
|
||||
|
||||
/**
|
||||
* Handles setting up and managing input controls for the game.
|
||||
|
|
@ -125,7 +128,7 @@ class ControlsHandler
|
|||
@:noCompletion
|
||||
private static function get_hasExternalInputDevice():Bool
|
||||
{
|
||||
return FlxG.gamepads.numActiveGamepads > 0 #if android || extension.androidtools.Tools.isChromebook() #end;
|
||||
return FlxG.gamepads.numActiveGamepads > 0 #if android || KeyboardUtil.keyboardConnected || extension.androidtools.Tools.isChromebook() #end;
|
||||
}
|
||||
|
||||
@:noCompletion
|
||||
|
|
|
|||
|
|
@ -811,6 +811,10 @@ class PlayState extends MusicBeatSubState
|
|||
// This state receives draw calls even when a substate is active.
|
||||
this.persistentDraw = true;
|
||||
|
||||
// Make the player unable to pause if they're moving from the chart editor while the focus is still on since the input persists.
|
||||
@:privateAccess
|
||||
justUnpaused = isChartingMode && !FlxG.game._lostFocus;
|
||||
|
||||
// Stop any pre-existing music.
|
||||
if (!overrideMusic)
|
||||
{
|
||||
|
|
@ -1185,14 +1189,17 @@ class PlayState extends MusicBeatSubState
|
|||
if (health > Constants.HEALTH_MAX) health = Constants.HEALTH_MAX;
|
||||
if (health < Constants.HEALTH_MIN) health = Constants.HEALTH_MIN;
|
||||
|
||||
// Apply camera zoom + multipliers.
|
||||
if (subState == null && cameraZoomRate > 0.0) // && !isInCutscene)
|
||||
{
|
||||
cameraBopMultiplier = FlxMath.lerp(1.0, cameraBopMultiplier, 0.95); // Lerp bop multiplier back to 1.0x
|
||||
var zoomPlusBop = currentCameraZoom * cameraBopMultiplier; // Apply camera bop multiplier.
|
||||
if (!debugUnbindCameraZoom) FlxG.camera.zoom = zoomPlusBop; // Actually apply the zoom to the camera.
|
||||
var decayRate:Float = 0.95;
|
||||
var dt:Float = elapsed * 60; //
|
||||
|
||||
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, 0.95);
|
||||
if (subState == null && cameraZoomRate > 0.0)
|
||||
{
|
||||
cameraBopMultiplier = FlxMath.lerp(1.0, cameraBopMultiplier, Math.pow(decayRate, dt));
|
||||
|
||||
var zoomPlusBop = currentCameraZoom * cameraBopMultiplier;
|
||||
if (!debugUnbindCameraZoom) FlxG.camera.zoom = zoomPlusBop;
|
||||
|
||||
camHUD.zoom = FlxMath.lerp(defaultHUDCameraZoom, camHUD.zoom, Math.pow(decayRate, dt));
|
||||
}
|
||||
|
||||
if (currentStage != null && currentStage.getBoyfriend() != null)
|
||||
|
|
@ -2679,8 +2686,6 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function onKeyRelease(event:PreciseInputEvent):Void
|
||||
{
|
||||
if (isGamePaused) return;
|
||||
|
||||
// Do the minimal possible work here.
|
||||
inputReleaseQueue.push(event);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,13 +47,13 @@ class AnimateAtlasCharacter extends BaseCharacter
|
|||
originalSizes.set(this.width, this.height);
|
||||
}
|
||||
|
||||
function loadAtlas()
|
||||
function loadAtlas():Void
|
||||
{
|
||||
trace('[ATLASCHAR] Loading sprite atlas for ${characterId}.');
|
||||
var assetLibrary:String = Paths.getLibrary(_data.assetPath);
|
||||
var assetPath:String = Paths.stripLibrary(_data.assetPath);
|
||||
|
||||
loadTextureAtlas(assetPath, assetLibrary, cast _data.atlasSettings);
|
||||
loadTextureAtlas(assetPath, assetLibrary, getAtlasSettings());
|
||||
|
||||
if (_data.isPixel)
|
||||
{
|
||||
|
|
@ -69,7 +69,7 @@ class AnimateAtlasCharacter extends BaseCharacter
|
|||
this.setScale(_data.scale);
|
||||
}
|
||||
|
||||
function loadAnimations()
|
||||
function loadAnimations():Void
|
||||
{
|
||||
trace('[ATLASCHAR] Loading ${_data.animations.length} animations for ${characterId}');
|
||||
|
||||
|
|
@ -100,4 +100,13 @@ class AnimateAtlasCharacter extends BaseCharacter
|
|||
{
|
||||
return originalSizes.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration for the texture atlas.
|
||||
* @return The configuration for the texture atlas.
|
||||
*/
|
||||
public function getAtlasSettings():AtlasSpriteSettings
|
||||
{
|
||||
return cast _data.atlasSettings;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ class MultiAnimateAtlasCharacter extends BaseCharacter
|
|||
originalSizes.set(this.width, this.height);
|
||||
}
|
||||
|
||||
function loadAtlases()
|
||||
function loadAtlases():Void
|
||||
{
|
||||
trace('[MULTIATLASCHAR] Loading sprite atlases for ${characterId}.');
|
||||
|
||||
|
|
@ -65,7 +65,7 @@ class MultiAnimateAtlasCharacter extends BaseCharacter
|
|||
var baseAssetLibrary:String = Paths.getLibrary(_data.assetPath);
|
||||
var baseAssetPath:String = Paths.stripLibrary(_data.assetPath);
|
||||
|
||||
loadTextureAtlas(baseAssetPath, baseAssetLibrary, cast _data.atlasSettings);
|
||||
loadTextureAtlas(baseAssetPath, baseAssetLibrary, getAtlasSettings());
|
||||
|
||||
for (asset in assetList)
|
||||
{
|
||||
|
|
@ -95,7 +95,7 @@ class MultiAnimateAtlasCharacter extends BaseCharacter
|
|||
this.setScale(_data.scale);
|
||||
}
|
||||
|
||||
function loadAnimations()
|
||||
function loadAnimations():Void
|
||||
{
|
||||
trace('[MULTIATLASCHAR] Loading ${_data.animations.length} animations for ${characterId}');
|
||||
|
||||
|
|
@ -126,4 +126,13 @@ class MultiAnimateAtlasCharacter extends BaseCharacter
|
|||
{
|
||||
return originalSizes.y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the configuration for the texture atlas.
|
||||
* @return The configuration for the texture atlas.
|
||||
*/
|
||||
public function getAtlasSettings():AtlasSpriteSettings
|
||||
{
|
||||
return cast _data.atlasSettings;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,12 @@ class HealthIcon extends FunkinSprite
|
|||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (bopEvery != 0) this.angle = MathUtil.smoothLerpPrecision(this.angle, 0, elapsed, 0.512);
|
||||
if (bopEvery != 0)
|
||||
{
|
||||
var dt:Float = elapsed * 60;
|
||||
|
||||
this.angle = MathUtil.smoothLerpPrecision(this.angle, 0, dt, 0.512);
|
||||
}
|
||||
|
||||
this.updatePosition();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -193,15 +193,15 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
* @param validScore Whether the song is elegible for highscores.
|
||||
* @return The constructed song object.
|
||||
*/
|
||||
public static function buildRaw(songId:String, metadata:Array<SongMetadata>, variations:Array<String>, charts:Map<String, SongChartData>,
|
||||
includeScript:Bool = true, validScore:Bool = false):Song
|
||||
public static function buildRaw(songId:String, metadata:Array<SongMetadata>, variation:String, charts:Map<String, SongChartData>, includeScript:Bool = true,
|
||||
validScore:Bool = false):Song
|
||||
{
|
||||
@:privateAccess
|
||||
var result:Null<Song> = null;
|
||||
|
||||
if (includeScript && SongRegistry.instance.isScriptedEntry(songId))
|
||||
if (includeScript && SongRegistry.instance.isScriptedEntry(songId, {variation: variation}))
|
||||
{
|
||||
var songClassName:Null<String> = SongRegistry.instance.getScriptedEntryClassName(songId);
|
||||
var songClassName:Null<String> = SongRegistry.instance.getScriptedEntryClassName(songId, {variation: variation});
|
||||
@:privateAccess
|
||||
if (songClassName != null) result = SongRegistry.instance.createScriptedEntry(songClassName);
|
||||
}
|
||||
|
|
|
|||
136
source/funkin/ui/charSelect/CharSelectCursors.hx
Normal file
136
source/funkin/ui/charSelect/CharSelectCursors.hx
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package funkin.ui.charSelect;
|
||||
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.math.FlxPoint;
|
||||
import openfl.display.BlendMode;
|
||||
import flixel.group.FlxSpriteContainer.FlxTypedSpriteContainer;
|
||||
import funkin.util.MathUtil;
|
||||
|
||||
class CharSelectCursors extends FlxTypedSpriteContainer<FunkinSprite>
|
||||
{
|
||||
/**
|
||||
* The main cursor sprite for this class.
|
||||
*/
|
||||
public var main:FunkinSprite;
|
||||
|
||||
var lightBlue:FunkinSprite;
|
||||
var darkBlue:FunkinSprite;
|
||||
|
||||
var cursorConfirmed:FunkinSprite;
|
||||
var cursorDenied:FunkinSprite;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
darkBlue = new FunkinSprite(0, 0);
|
||||
lightBlue = new FunkinSprite(0, 0);
|
||||
main = new FunkinSprite(0, 0);
|
||||
|
||||
cursorConfirmed = new FunkinSprite(0, 0);
|
||||
cursorDenied = new FunkinSprite(0, 0);
|
||||
|
||||
darkBlue.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
lightBlue.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
main.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
|
||||
darkBlue.color = 0xFF3C74F7;
|
||||
lightBlue.color = 0xFF3EBBFF;
|
||||
main.color = 0xFFFFFF00;
|
||||
FlxTween.color(main, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: PINGPONG});
|
||||
|
||||
darkBlue.blend = BlendMode.SCREEN;
|
||||
lightBlue.blend = BlendMode.SCREEN;
|
||||
|
||||
add(darkBlue);
|
||||
add(lightBlue);
|
||||
add(main);
|
||||
|
||||
cursorConfirmed.frames = Paths.getSparrowAtlas("charSelect/charSelectorConfirm");
|
||||
cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED instance 1", 24, true);
|
||||
cursorConfirmed.visible = false;
|
||||
add(cursorConfirmed);
|
||||
|
||||
cursorDenied.frames = Paths.getSparrowAtlas("charSelect/charSelectorDenied");
|
||||
cursorDenied.animation.addByPrefix("idle", "cursor DENIED instance 1", 24, false);
|
||||
cursorDenied.visible = false;
|
||||
add(cursorDenied);
|
||||
|
||||
scrollFactor.set();
|
||||
directAlpha = true;
|
||||
}
|
||||
|
||||
public function confirm():Void
|
||||
{
|
||||
cursorConfirmed.visible = true;
|
||||
cursorConfirmed.animation.play("idle", true);
|
||||
|
||||
main.visible = lightBlue.visible = darkBlue.visible = false;
|
||||
}
|
||||
|
||||
public function resetDeny():Void
|
||||
{
|
||||
cursorDenied.visible = false;
|
||||
}
|
||||
|
||||
public function deny():Void
|
||||
{
|
||||
cursorDenied.visible = true;
|
||||
cursorDenied.animation.play('idle', true);
|
||||
cursorDenied.animation.onFinish.add((_) -> {
|
||||
cursorDenied.visible = false;
|
||||
});
|
||||
}
|
||||
|
||||
public function unconfirm():Void
|
||||
{
|
||||
cursorConfirmed.visible = false;
|
||||
main.visible = lightBlue.visible = darkBlue.visible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Snaps the cursors to the given position.
|
||||
* @param intendedPosition The position to snap to as a `FlxPoint`.
|
||||
*/
|
||||
public function snapToLocation(intendedPosition:FlxPoint):Void
|
||||
{
|
||||
main.x = intendedPosition.x;
|
||||
main.y = intendedPosition.y;
|
||||
|
||||
lightBlue.x = main.x;
|
||||
lightBlue.y = main.y;
|
||||
|
||||
darkBlue.x = intendedPosition.x;
|
||||
darkBlue.y = intendedPosition.y;
|
||||
|
||||
cursorConfirmed.x = main.x - 2;
|
||||
cursorConfirmed.y = main.y - 4;
|
||||
|
||||
cursorDenied.x = main.x - 2;
|
||||
cursorDenied.y = main.y - 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lerps the cursors to the given position.
|
||||
* @param intendedPosition The position to lerp to as a `FlxPoint`.
|
||||
*/
|
||||
public function lerpToLocation(intendedPosition:FlxPoint):Void
|
||||
{
|
||||
main.x = MathUtil.snap(MathUtil.smoothLerpPrecision(main.x, intendedPosition.x, FlxG.elapsed, 0.1), intendedPosition.x, 1);
|
||||
main.y = MathUtil.snap(MathUtil.smoothLerpPrecision(main.y, intendedPosition.y, FlxG.elapsed, 0.1), intendedPosition.y, 1);
|
||||
|
||||
lightBlue.x = MathUtil.smoothLerpPrecision(lightBlue.x, main.x, FlxG.elapsed, 0.202);
|
||||
lightBlue.y = MathUtil.smoothLerpPrecision(lightBlue.y, main.y, FlxG.elapsed, 0.202);
|
||||
|
||||
darkBlue.x = MathUtil.smoothLerpPrecision(darkBlue.x, intendedPosition.x, FlxG.elapsed, 0.404);
|
||||
darkBlue.y = MathUtil.smoothLerpPrecision(darkBlue.y, intendedPosition.y, FlxG.elapsed, 0.404);
|
||||
|
||||
cursorConfirmed.x = main.x - 2;
|
||||
cursorConfirmed.y = main.y - 4;
|
||||
|
||||
cursorDenied.x = main.x - 2;
|
||||
cursorDenied.y = main.y - 4;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package funkin.ui.charSelect;
|
||||
|
||||
import flixel.util.FlxDirectionFlags;
|
||||
import flixel.FlxObject;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.math.FlxMath;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.sound.FlxSound;
|
||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||
|
|
@ -13,7 +15,6 @@ import flixel.util.FlxColor;
|
|||
import funkin.audio.FunkinSound;
|
||||
import funkin.data.freeplay.player.PlayerData.PlayerCharSelectData;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import funkin.graphics.shaders.BlueFade;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
|
@ -42,22 +43,20 @@ import funkin.util.TouchUtil;
|
|||
@:nullSafety
|
||||
class CharSelectSubState extends MusicBeatSubState
|
||||
{
|
||||
// what the actual hell
|
||||
// having a hard time trying to make my changes work so i chose to be less stubborn and just remove them for now. - Zack
|
||||
// Left this here so somebody can remind me
|
||||
var cursor:FunkinSprite;
|
||||
/**
|
||||
* The default index for the cursor.
|
||||
*/
|
||||
final DEFAULT_CURSOR_INDEX:Int = 4;
|
||||
|
||||
var cursors:CharSelectCursors;
|
||||
|
||||
var cursorBlue:FunkinSprite;
|
||||
var cursorDarkBlue:FunkinSprite;
|
||||
var grpCursors:FlxTypedGroup<FunkinSprite>;
|
||||
var cursorConfirmed:FunkinSprite;
|
||||
var cursorDenied:FunkinSprite;
|
||||
var cursorX:Int = 0;
|
||||
var cursorY:Int = 0;
|
||||
var cursorFactor:Float = 110;
|
||||
var cursorOffsetX:Float = -16;
|
||||
var cursorOffsetY:Float = -48;
|
||||
var cursorLocIntended:FlxPoint = new FlxPoint(0, 0);
|
||||
|
||||
var playerChill:CharSelectPlayer;
|
||||
var playerChillOut:CharSelectPlayer;
|
||||
var gfChill:CharSelectGF;
|
||||
|
|
@ -82,7 +81,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
var introSound:FunkinSound = new FunkinSound();
|
||||
var staticSound:FunkinSound = new FunkinSound();
|
||||
|
||||
// var charSelectCam:FunkinCamera;
|
||||
var selectedBizz:Array<BitmapFilter> = [
|
||||
new DropShadowFilter(0, 0, 0xFFFFFF, 1, 2, 2, 19, 1, false, false, false),
|
||||
new DropShadowFilter(5, 45, 0x000000, 1, 2, 2, 1, 1, false, false, false)
|
||||
|
|
@ -104,7 +102,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
cutoutSize = FullScreenScaleMode.gameCutoutSize.x / 2;
|
||||
|
||||
grpCursors = new FlxTypedGroup<FunkinSprite>();
|
||||
cursors = new CharSelectCursors();
|
||||
grpHitboxes = new FlxTypedGroup<FlxObject>();
|
||||
|
||||
gfChill = new CharSelectGF(cutoutSize, 0);
|
||||
|
|
@ -115,13 +113,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
dipshitBacking = new FunkinSprite(cutoutSize + 423, -17);
|
||||
chooseDipshit = new FunkinSprite(cutoutSize + 426, -13);
|
||||
|
||||
nametag = new Nametag(curChar);
|
||||
|
||||
cursor = new FunkinSprite(0, 0);
|
||||
cursorBlue = new FunkinSprite(0, 0);
|
||||
cursorDarkBlue = new FunkinSprite(0, 0);
|
||||
cursorConfirmed = new FunkinSprite(0, 0);
|
||||
cursorDenied = new FunkinSprite(0, 0);
|
||||
nametag = new Nametag(rememberedChar);
|
||||
|
||||
charHitbox = new FlxObject(FlxG.width * 0.65, FlxG.height * 0.2, 300, 500);
|
||||
|
||||
|
|
@ -248,14 +240,17 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
{
|
||||
if (charId == rememberedChar)
|
||||
{
|
||||
setCursorPosition(pos);
|
||||
setCursorPosition(pos, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@:bypassAccessor curChar = rememberedChar;
|
||||
}
|
||||
else
|
||||
{
|
||||
setupPlayerChill(Constants.DEFAULT_CHARACTER);
|
||||
setCursorPosition(DEFAULT_CURSOR_INDEX, true);
|
||||
}
|
||||
|
||||
var speakers:FunkinSprite = FunkinSprite.createTextureAtlas(cutoutSize - 10, 0, "charSelect/charSelectSpeakers",
|
||||
{
|
||||
|
|
@ -314,49 +309,18 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FunkinSprite, ["x", "y", "alpha", "scale", "blend"]));
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSound, ["pitch", "volume"]));
|
||||
|
||||
add(grpCursors);
|
||||
|
||||
cursor.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
cursor.color = 0xFFFFFF00;
|
||||
|
||||
cursorBlue.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
cursorBlue.color = 0xFF3EBBFF;
|
||||
|
||||
cursorDarkBlue.loadGraphic(Paths.image('charSelect/charSelector'));
|
||||
cursorDarkBlue.color = 0xFF3C74F7;
|
||||
|
||||
cursorBlue.blend = BlendMode.SCREEN;
|
||||
cursorDarkBlue.blend = BlendMode.SCREEN;
|
||||
|
||||
cursorConfirmed.scrollFactor.set();
|
||||
cursorConfirmed.frames = Paths.getSparrowAtlas("charSelect/charSelectorConfirm");
|
||||
cursorConfirmed.animation.addByPrefix("idle", "cursor ACCEPTED instance 1", 24, true);
|
||||
cursorConfirmed.visible = false;
|
||||
add(cursorConfirmed);
|
||||
|
||||
cursorDenied.scrollFactor.set();
|
||||
cursorDenied.frames = Paths.getSparrowAtlas("charSelect/charSelectorDenied");
|
||||
cursorDenied.animation.addByPrefix("idle", "cursor DENIED instance 1", 24, false);
|
||||
cursorDenied.visible = false;
|
||||
add(cursorDenied);
|
||||
|
||||
grpCursors.add(cursorDarkBlue);
|
||||
grpCursors.add(cursorBlue);
|
||||
grpCursors.add(cursor);
|
||||
add(cursors);
|
||||
|
||||
charHitbox.active = false;
|
||||
charHitbox.scrollFactor.set();
|
||||
|
||||
selectSound.loadEmbedded(Paths.sound('CS_select'));
|
||||
selectSound.pitch = 1;
|
||||
selectSound.volume = 0.7;
|
||||
|
||||
FlxG.sound.defaultSoundGroup.add(selectSound);
|
||||
FlxG.sound.list.add(selectSound);
|
||||
|
||||
unlockSound.loadEmbedded(Paths.sound('CS_unlock'));
|
||||
unlockSound.pitch = 1;
|
||||
|
||||
unlockSound.volume = 0;
|
||||
unlockSound.play(true);
|
||||
|
||||
|
|
@ -364,18 +328,13 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
FlxG.sound.list.add(unlockSound);
|
||||
|
||||
lockedSound.loadEmbedded(Paths.sound('CS_locked'));
|
||||
lockedSound.pitch = 1;
|
||||
|
||||
lockedSound.volume = 1.;
|
||||
|
||||
FlxG.sound.defaultSoundGroup.add(lockedSound);
|
||||
FlxG.sound.list.add(lockedSound);
|
||||
|
||||
staticSound.loadEmbedded(Paths.sound('static loop'));
|
||||
staticSound.pitch = 1;
|
||||
|
||||
staticSound.looped = true;
|
||||
|
||||
staticSound.volume = 0.6;
|
||||
|
||||
FlxG.sound.defaultSoundGroup.add(staticSound);
|
||||
|
|
@ -398,12 +357,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
FlxTween.tween(member, {y: member.y - 300}, 1, {ease: FlxEase.expoOut});
|
||||
}
|
||||
|
||||
cursor.scrollFactor.set();
|
||||
cursorBlue.scrollFactor.set();
|
||||
cursorDarkBlue.scrollFactor.set();
|
||||
|
||||
FlxTween.color(cursor, 0.2, 0xFFFFFF00, 0xFFFFCC00, {type: PINGPONG});
|
||||
|
||||
FlxG.debugger.addTrackerProfile(new TrackerProfile(CharSelectSubState, ["curChar", "grpXSpread", "grpYSpread"]));
|
||||
FlxG.debugger.track(this);
|
||||
|
||||
|
|
@ -462,7 +415,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
introSound = new FunkinSound();
|
||||
introSound.loadEmbedded(Paths.sound('CS_Lights'));
|
||||
introSound.pitch = 1;
|
||||
introSound.volume = 0;
|
||||
|
||||
FlxG.sound.defaultSoundGroup.add(introSound);
|
||||
|
|
@ -594,9 +546,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
var xThing = (copy - index - 2) * -1;
|
||||
// Look, I'd write better code but I had better aneurysms, my bad - Cheems
|
||||
// felt - Zack
|
||||
// Krue - Abnormal
|
||||
cursorY = yThing;
|
||||
cursorX = xThing;
|
||||
|
||||
|
|
@ -733,10 +682,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
#end
|
||||
|
||||
FlxTween.tween(cursor, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursorBlue, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursorDarkBlue, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursorConfirmed, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
FlxTween.tween(cursors, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
|
||||
|
||||
FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn});
|
||||
FlxTween.tween(nametag, {y: nametag.y + 80}, 0.8, {ease: FlxEase.backIn});
|
||||
|
|
@ -774,10 +720,8 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
var holdTmrDown:Float = 0;
|
||||
var holdTmrLeft:Float = 0;
|
||||
var holdTmrRight:Float = 0;
|
||||
var spamUp:Bool = false;
|
||||
var spamDown:Bool = false;
|
||||
var spamLeft:Bool = false;
|
||||
var spamRight:Bool = false;
|
||||
var spamDirections:FlxDirectionFlags = NONE;
|
||||
var initSpam = 0.5;
|
||||
|
||||
var mobileDeny:Bool = false;
|
||||
var mobileAccept:Bool = false;
|
||||
|
|
@ -792,9 +736,6 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
mobileAccept = false;
|
||||
|
||||
if (controls.UI_UP_R || controls.UI_DOWN_R || controls.UI_LEFT_R || controls.UI_RIGHT_R #if FEATURE_TOUCH_CONTROLS || TouchUtil.justReleased #end)
|
||||
selectSound.pitch = 1;
|
||||
|
||||
if (allowInput && !pressedSelect)
|
||||
{
|
||||
#if FEATURE_TOUCH_CONTROLS
|
||||
|
|
@ -811,7 +752,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
{
|
||||
cursorX = indexCX;
|
||||
cursorY = indexCY;
|
||||
cursorDenied.visible = false;
|
||||
cursors.resetDeny();
|
||||
selectSound.play(true);
|
||||
}
|
||||
else if (TouchUtil.justPressed)
|
||||
|
|
@ -830,45 +771,10 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
}
|
||||
#end
|
||||
|
||||
if (controls.UI_UP) holdTmrUp += elapsed;
|
||||
if (controls.UI_UP_R)
|
||||
{
|
||||
holdTmrUp = 0;
|
||||
spamUp = false;
|
||||
}
|
||||
|
||||
if (controls.UI_DOWN) holdTmrDown += elapsed;
|
||||
if (controls.UI_DOWN_R)
|
||||
{
|
||||
holdTmrDown = 0;
|
||||
spamDown = false;
|
||||
}
|
||||
|
||||
if (controls.UI_LEFT) holdTmrLeft += elapsed;
|
||||
if (controls.UI_LEFT_R)
|
||||
{
|
||||
holdTmrLeft = 0;
|
||||
spamLeft = false;
|
||||
}
|
||||
|
||||
if (controls.UI_RIGHT) holdTmrRight += elapsed;
|
||||
if (controls.UI_RIGHT_R)
|
||||
{
|
||||
holdTmrRight = 0;
|
||||
spamRight = false;
|
||||
}
|
||||
|
||||
var initSpam = 0.5;
|
||||
|
||||
if (holdTmrUp >= initSpam) spamUp = true;
|
||||
if (holdTmrDown >= initSpam) spamDown = true;
|
||||
if (holdTmrLeft >= initSpam) spamLeft = true;
|
||||
if (holdTmrRight >= initSpam) spamRight = true;
|
||||
|
||||
if (controls.UI_UP_P)
|
||||
{
|
||||
cursorY -= 1;
|
||||
cursorDenied.visible = false;
|
||||
cursors.resetDeny();
|
||||
|
||||
holdTmrUp = 0;
|
||||
|
||||
|
|
@ -877,14 +783,14 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
if (controls.UI_DOWN_P)
|
||||
{
|
||||
cursorY += 1;
|
||||
cursorDenied.visible = false;
|
||||
cursors.resetDeny();
|
||||
holdTmrDown = 0;
|
||||
selectSound.play(true);
|
||||
}
|
||||
if (controls.UI_LEFT_P)
|
||||
{
|
||||
cursorX -= 1;
|
||||
cursorDenied.visible = false;
|
||||
cursors.resetDeny();
|
||||
|
||||
holdTmrLeft = 0;
|
||||
selectSound.play(true);
|
||||
|
|
@ -892,30 +798,49 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
if (controls.UI_RIGHT_P)
|
||||
{
|
||||
cursorX += 1;
|
||||
cursorDenied.visible = false;
|
||||
cursors.resetDeny();
|
||||
holdTmrRight = 0;
|
||||
selectSound.play(true);
|
||||
}
|
||||
|
||||
if (controls.UI_UP) holdTmrUp += elapsed;
|
||||
if (controls.UI_UP_R || !controls.UI_UP)
|
||||
{
|
||||
holdTmrUp = 0;
|
||||
spamDirections = spamDirections.without(UP);
|
||||
}
|
||||
|
||||
if (controls.UI_DOWN) holdTmrDown += elapsed;
|
||||
if (controls.UI_DOWN_R || !controls.UI_DOWN)
|
||||
{
|
||||
holdTmrDown = 0;
|
||||
spamDirections = spamDirections.without(DOWN);
|
||||
}
|
||||
|
||||
if (controls.UI_LEFT) holdTmrLeft += elapsed;
|
||||
if (controls.UI_LEFT_R || !controls.UI_LEFT)
|
||||
{
|
||||
holdTmrLeft = 0;
|
||||
spamDirections = spamDirections.without(LEFT);
|
||||
}
|
||||
|
||||
if (controls.UI_RIGHT) holdTmrRight += elapsed;
|
||||
if (controls.UI_RIGHT_R || !controls.UI_RIGHT)
|
||||
{
|
||||
holdTmrRight = 0;
|
||||
spamDirections = spamDirections.without(RIGHT);
|
||||
}
|
||||
|
||||
if (holdTmrUp >= initSpam) spamDirections = spamDirections.with(UP);
|
||||
if (holdTmrDown >= initSpam) spamDirections = spamDirections.with(DOWN);
|
||||
if (holdTmrLeft >= initSpam) spamDirections = spamDirections.with(LEFT);
|
||||
if (holdTmrRight >= initSpam) spamDirections = spamDirections.with(RIGHT);
|
||||
|
||||
if (controls.BACK_P) goBack();
|
||||
}
|
||||
|
||||
if (cursorX < -1)
|
||||
{
|
||||
cursorX = 1;
|
||||
}
|
||||
if (cursorX > 1)
|
||||
{
|
||||
cursorX = -1;
|
||||
}
|
||||
if (cursorY < -1)
|
||||
{
|
||||
cursorY = 1;
|
||||
}
|
||||
if (cursorY > 1)
|
||||
{
|
||||
cursorY = -1;
|
||||
}
|
||||
cursorX = FlxMath.wrap(cursorX, -1, 1);
|
||||
cursorY = FlxMath.wrap(cursorY, -1, 1);
|
||||
|
||||
var currentCharacter:String = availableChars[getCurrentSelected()] ?? Constants.DEFAULT_CHARACTER;
|
||||
if (availableChars.exists(getCurrentSelected()) && PlayerRegistry.instance.isCharacterSeen(currentCharacter))
|
||||
|
|
@ -926,8 +851,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
if (allowInput && pressedSelect && (controls.BACK_P #if FEATURE_TOUCH_CONTROLS || (mobileDeny && TouchUtil.justReleased) #end))
|
||||
{
|
||||
mobileDeny = false;
|
||||
cursorConfirmed.visible = false;
|
||||
grpCursors.visible = true;
|
||||
cursors.unconfirm();
|
||||
|
||||
dispatchEvent(new CharacterSelectScriptEvent(CHARACTER_DESELECTED, curChar));
|
||||
|
||||
|
|
@ -962,17 +886,11 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
if (allowInput && !pressedSelect && (controls.ACCEPT_P || mobileAccept))
|
||||
{
|
||||
mobileDeny = false;
|
||||
spamUp = false;
|
||||
spamDown = false;
|
||||
spamLeft = false;
|
||||
spamRight = false;
|
||||
spamDirections = NONE;
|
||||
|
||||
cursorConfirmed.visible = true;
|
||||
cursorConfirmed.animation.play("idle", true);
|
||||
cursors.confirm();
|
||||
|
||||
grpCursors.visible = false;
|
||||
|
||||
FlxG.sound.play(Paths.sound('CS_confirm'));
|
||||
FunkinSound.playOnce(Paths.sound('CS_confirm'));
|
||||
|
||||
dispatchEvent(new CharacterSelectScriptEvent(CHARACTER_CONFIRMED, curChar));
|
||||
|
||||
|
|
@ -985,9 +903,11 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 0.1}, 1, {ease: FlxEase.quadInOut});
|
||||
FlxTween.tween(FlxG.sound.music, {volume: 0.0}, 1.5, {ease: FlxEase.quadInOut});
|
||||
|
||||
playerChill.anim.play("select");
|
||||
gfChill.anim.play("confirm", true);
|
||||
gfChill.anim.curAnim.looped = true;
|
||||
|
||||
pressedSelect = true;
|
||||
selectTimer.start(1.5, (_) -> {
|
||||
goToFreeplay();
|
||||
|
|
@ -1007,50 +927,30 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
if (allowInput && (controls.ACCEPT_P || mobileAccept))
|
||||
{
|
||||
cursorDenied.visible = true;
|
||||
|
||||
playerChill.anim.play("cannot select Label", true);
|
||||
|
||||
lockedSound.play(true);
|
||||
|
||||
HapticUtil.vibrate(0, 0.2);
|
||||
|
||||
cursorDenied.animation.play('idle', true);
|
||||
cursorDenied.animation.onFinish.add((_) -> {
|
||||
cursorDenied.visible = false;
|
||||
});
|
||||
cursors.deny();
|
||||
}
|
||||
}
|
||||
|
||||
updateLockAnims();
|
||||
|
||||
if (autoFollow == true)
|
||||
if (autoFollow)
|
||||
{
|
||||
camFollow.screenCenter();
|
||||
camFollow.x += cursorX * 10;
|
||||
camFollow.y += cursorY * 10;
|
||||
}
|
||||
|
||||
cursorLocIntended.x = (cursorFactor * cursorX) + (FlxG.width / 2) - cursor.width / 2;
|
||||
cursorLocIntended.y = (cursorFactor * cursorY) + (FlxG.height / 2) - cursor.height / 2;
|
||||
cursorLocIntended.x = (cursorFactor * cursorX) + (FlxG.width / 2) - cursors.main.width / 2;
|
||||
cursorLocIntended.y = (cursorFactor * cursorY) + (FlxG.height / 2) - cursors.main.height / 2;
|
||||
|
||||
cursorLocIntended.x += cursorOffsetX;
|
||||
cursorLocIntended.y += cursorOffsetY;
|
||||
|
||||
cursor.x = MathUtil.snap(MathUtil.smoothLerpPrecision(cursor.x, cursorLocIntended.x, elapsed, 0.1), cursorLocIntended.x, 1);
|
||||
cursor.y = MathUtil.snap(MathUtil.smoothLerpPrecision(cursor.y, cursorLocIntended.y, elapsed, 0.1), cursorLocIntended.y, 1);
|
||||
|
||||
cursorBlue.x = MathUtil.smoothLerpPrecision(cursorBlue.x, cursor.x, elapsed, 0.202);
|
||||
cursorBlue.y = MathUtil.smoothLerpPrecision(cursorBlue.y, cursor.y, elapsed, 0.202);
|
||||
|
||||
cursorDarkBlue.x = MathUtil.smoothLerpPrecision(cursorDarkBlue.x, cursorLocIntended.x, elapsed, 0.404);
|
||||
cursorDarkBlue.y = MathUtil.smoothLerpPrecision(cursorDarkBlue.y, cursorLocIntended.y, elapsed, 0.404);
|
||||
|
||||
cursorConfirmed.x = cursor.x - 2;
|
||||
cursorConfirmed.y = cursor.y - 4;
|
||||
|
||||
cursorDenied.x = cursor.x - 2;
|
||||
cursorDenied.y = cursor.y - 4;
|
||||
cursors.lerpToLocation(cursorLocIntended);
|
||||
}
|
||||
|
||||
function goBack():Void
|
||||
|
|
@ -1120,29 +1020,29 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
|
||||
function spamOnStep():Void
|
||||
{
|
||||
if (spamUp || spamDown || spamLeft || spamRight)
|
||||
if (spamDirections.hasAny(ANY))
|
||||
{
|
||||
if (selectSound.pitch > 5) selectSound.pitch = 5;
|
||||
selectSound.play(true);
|
||||
|
||||
cursorDenied.visible = false;
|
||||
cursors.resetDeny();
|
||||
|
||||
if (spamUp)
|
||||
if (spamDirections.has(UP))
|
||||
{
|
||||
cursorY -= 1;
|
||||
holdTmrUp = 0;
|
||||
}
|
||||
if (spamDown)
|
||||
if (spamDirections.has(DOWN))
|
||||
{
|
||||
cursorY += 1;
|
||||
holdTmrDown = 0;
|
||||
}
|
||||
if (spamLeft)
|
||||
if (spamDirections.has(LEFT))
|
||||
{
|
||||
cursorX -= 1;
|
||||
holdTmrLeft = 0;
|
||||
}
|
||||
if (spamRight)
|
||||
if (spamDirections.has(RIGHT))
|
||||
{
|
||||
cursorX += 1;
|
||||
holdTmrRight = 0;
|
||||
|
|
@ -1219,8 +1119,7 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
return gridPosition;
|
||||
}
|
||||
|
||||
// Moved this code into a function because is now used twice
|
||||
function setCursorPosition(index:Int)
|
||||
function setCursorPosition(index:Int, instant:Bool = false):Void
|
||||
{
|
||||
var copy = 3;
|
||||
var yThing = -1;
|
||||
|
|
@ -1236,6 +1135,17 @@ class CharSelectSubState extends MusicBeatSubState
|
|||
// Look, I'd write better code but I had better aneurysms, my bad - Cheems
|
||||
cursorY = yThing;
|
||||
cursorX = xThing;
|
||||
|
||||
if (instant)
|
||||
{
|
||||
cursorLocIntended.x = (cursorFactor * cursorX) + (FlxG.width / 2) - cursors.main.width / 2;
|
||||
cursorLocIntended.y = (cursorFactor * cursorY) + (FlxG.height / 2) - cursors.main.height / 2;
|
||||
|
||||
cursorLocIntended.x += cursorOffsetX;
|
||||
cursorLocIntended.y += cursorOffsetY;
|
||||
|
||||
cursors.snapToLocation(cursorLocIntended);
|
||||
}
|
||||
}
|
||||
|
||||
function set_curChar(value:String):String
|
||||
|
|
|
|||
|
|
@ -175,27 +175,26 @@ class DebugBoundingState extends FlxState
|
|||
function updateOnionSkin():Void
|
||||
{
|
||||
if (swagChar == null) return;
|
||||
if (swagChar.hasAnimation("idle")) swagChar.playAnimation("idle", true);
|
||||
|
||||
onionSkinChar.alpha = 0.6;
|
||||
onionSkinChar.flipX = swagChar.flipX;
|
||||
|
||||
if (onionSkinChar.hasAnimation("idle"))
|
||||
{
|
||||
onionSkinChar.playAnimation("idle", true);
|
||||
}
|
||||
else if (onionSkinChar.hasAnimation("danceLeft"))
|
||||
{
|
||||
onionSkinChar.playAnimation("danceLeft", true);
|
||||
}
|
||||
else if (onionSkinChar.hasAnimation("danceRight"))
|
||||
{
|
||||
onionSkinChar.playAnimation("danceRight", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
onionSkinChar.playAnimation(currentAnimationName, true);
|
||||
}
|
||||
|
||||
onionSkinChar.animation.pause();
|
||||
onionSkinChar.flipX = swagChar.flipX;
|
||||
onionSkinChar.scale.set(swagChar.scale.x, swagChar.scale.y);
|
||||
onionSkinChar.updateHitbox();
|
||||
onionSkinChar.offset.x = swagChar.offset.x + (swagChar.animOffsets[0] - swagChar.globalOffsets[0]) * swagChar.scale.x;
|
||||
onionSkinChar.offset.y = swagChar.offset.y + (swagChar.animOffsets[1] - swagChar.globalOffsets[1]) * swagChar.scale.y;
|
||||
|
||||
swagChar.playAnimation(currentAnimationName, true);
|
||||
}
|
||||
|
||||
function initOffsetView():Void
|
||||
|
|
|
|||
|
|
@ -73,6 +73,7 @@ import funkin.ui.debug.charting.commands.RemoveStackedNotesCommand;
|
|||
import funkin.ui.debug.charting.commands.SelectAllItemsCommand;
|
||||
import funkin.ui.debug.charting.commands.SelectItemsCommand;
|
||||
import funkin.ui.debug.charting.commands.SetItemSelectionCommand;
|
||||
import funkin.ui.debug.charting.commands.SwitchDifficultyCommand;
|
||||
import funkin.ui.debug.charting.components.ChartEditorEventSprite;
|
||||
import funkin.ui.debug.charting.components.ChartEditorHoldNoteSprite;
|
||||
import funkin.ui.debug.charting.components.ChartEditorMeasureTicks;
|
||||
|
|
@ -385,12 +386,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
if (isViewDownscroll)
|
||||
{
|
||||
gridTiledSprite.y = -scrollPositionInPixels + (GRID_INITIAL_Y_POS);
|
||||
measureTicks.y = gridTiledSprite.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridTiledSprite.y = -scrollPositionInPixels + (GRID_INITIAL_Y_POS);
|
||||
measureTicks.y = gridTiledSprite.y;
|
||||
|
||||
for (member in audioWaveforms.members)
|
||||
{
|
||||
|
|
@ -1335,12 +1334,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
function get_availableDifficulties():Array<String>
|
||||
{
|
||||
var m:Null<SongMetadata> = songMetadata.get(selectedVariation);
|
||||
return getAvailableDifficulties(selectedVariation);
|
||||
}
|
||||
|
||||
function getAvailableDifficulties(variation:String):Array<String>
|
||||
{
|
||||
var m:Null<SongMetadata> = songMetadata.get(variation);
|
||||
return m?.playData?.difficulties ?? [Constants.DEFAULT_DIFFICULTY];
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of difficulties for ALL variations of the current song.
|
||||
* Retrieves the list of (suffixed) difficulties for ALL variations of the current song.
|
||||
*/
|
||||
var allDifficulties(get, never):Array<String>;
|
||||
|
||||
|
|
@ -1738,6 +1742,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
noteTooltipsDirty = true;
|
||||
notePreviewViewportBoundsDirty = true;
|
||||
|
||||
// Switching difficulties should automatically clear the selection.
|
||||
currentNoteSelection = [];
|
||||
currentEventSelection = [];
|
||||
|
||||
switchToCurrentInstrumental();
|
||||
postLoadInstrumental();
|
||||
|
||||
|
|
@ -1761,6 +1769,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
noteTooltipsDirty = true;
|
||||
notePreviewViewportBoundsDirty = true;
|
||||
|
||||
// Switching difficulties should automatically clear the selection.
|
||||
currentNoteSelection = [];
|
||||
currentEventSelection = [];
|
||||
|
||||
return selectedDifficulty;
|
||||
}
|
||||
|
||||
|
|
@ -1939,6 +1951,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
*/
|
||||
var menubarItemViewSubtitles:MenuCheckBox;
|
||||
|
||||
/**
|
||||
* The `View -> Waveforms` menu item.
|
||||
*/
|
||||
var menubarItemViewWaveforms:MenuCheckBox;
|
||||
|
||||
/**
|
||||
* The `View -> Increase Difficulty` menu item.
|
||||
*/
|
||||
|
|
@ -2154,11 +2171,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
*/
|
||||
var notePreviewViewportBitmap:Null<BitmapData> = null;
|
||||
|
||||
/**
|
||||
* The IMAGE used for the measure ticks. Updated by ChartEditorThemeHandler.
|
||||
*/
|
||||
var measureTickBitmap:Null<BitmapData> = null;
|
||||
|
||||
/**
|
||||
* The IMAGE used for the offset ticks. Updated by ChartEditorThemeHandler.
|
||||
*/
|
||||
|
|
@ -2389,7 +2401,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
// Setup the onClick listeners for the UI after it's been created.
|
||||
setupUIListeners();
|
||||
setupContextMenu();
|
||||
setupTurboKeyHandlers();
|
||||
|
||||
setupAutoSave();
|
||||
|
|
@ -2717,10 +2728,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
measureTicks = new ChartEditorMeasureTicks(this);
|
||||
var measureTicksWidth = (GRID_SIZE);
|
||||
measureTicks.x = gridTiledSprite.x - measureTicksWidth;
|
||||
measureTicks.y = MENU_BAR_HEIGHT + GRID_TOP_PAD;
|
||||
measureTicks.zIndex = 20;
|
||||
|
||||
add(measureTicks);
|
||||
|
||||
handleMeasureTickPosition();
|
||||
}
|
||||
|
||||
function buildNotePreview():Void
|
||||
|
|
@ -3240,6 +3251,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
menubarItemViewSubtitles.onClick = event -> showSubtitles = menubarItemViewSubtitles.selected;
|
||||
menubarItemViewSubtitles.selected = showSubtitles;
|
||||
|
||||
menubarItemViewWaveforms.onClick = event -> audioWaveforms.visible = menubarItemViewWaveforms.selected;
|
||||
menubarItemViewWaveforms.selected = audioWaveforms.visible;
|
||||
|
||||
menubarItemDifficultyUp.onClick = _ -> incrementDifficulty(1);
|
||||
menubarItemDifficultyDown.onClick = _ -> incrementDifficulty(-1);
|
||||
|
||||
|
|
@ -3354,21 +3368,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// registerContextMenu(null, Paths.ui('chart-editor/context/test'));
|
||||
}
|
||||
|
||||
function setupContextMenu():Void
|
||||
{
|
||||
Screen.instance.registerEvent(MouseEvent.RIGHT_MOUSE_UP, function(e:MouseEvent) {
|
||||
var xPos = e.screenX;
|
||||
var yPos = e.screenY;
|
||||
onContextMenu(xPos, yPos);
|
||||
});
|
||||
}
|
||||
|
||||
function onContextMenu(xPos:Float, yPos:Float)
|
||||
{
|
||||
trace('User right clicked to open menu at (${xPos}, ${yPos})');
|
||||
// this.openDefaultContextMenu(xPos, yPos);
|
||||
}
|
||||
|
||||
function copySelection():Void
|
||||
{
|
||||
// Doesn't use a command because it's not undoable.
|
||||
|
|
@ -3586,6 +3585,20 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
handlePostUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Function called when the game window loses focus.
|
||||
*/
|
||||
public override function onFocusLost():Void
|
||||
{
|
||||
super.onFocusLost();
|
||||
|
||||
// Stop the song upon tabbing out.
|
||||
if (Preferences.autoPause)
|
||||
{
|
||||
stopAudioPlayback();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Beat hit while the song is playing.
|
||||
*/
|
||||
|
|
@ -4431,8 +4444,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
|
||||
// TODO: TBH some of this should be using FlxMouseEventManager...
|
||||
|
||||
if (shouldHandleCursor)
|
||||
// early return if we shouldn't handle the cursor at all
|
||||
if (!shouldHandleCursor)
|
||||
{
|
||||
if (gridGhostNote != null) gridGhostNote.visible = false;
|
||||
if (gridGhostHoldNote != null) gridGhostHoldNote.visible = false;
|
||||
if (gridGhostEvent != null) gridGhostEvent.visible = false;
|
||||
|
||||
// Do not set Cursor.cursorMode here, because it will be set by the HaxeUI.
|
||||
return;
|
||||
}
|
||||
|
||||
// Over the course of this big conditional block,
|
||||
// we determine what the cursor should look like,
|
||||
// and fall back to the default cursor if none of the conditions are met.
|
||||
|
|
@ -4932,7 +4954,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
}
|
||||
var dragDistanceColumns:Int = cursorGridPos - noteGridPos;
|
||||
|
||||
if (dragTargetCurrentStep != dragDistanceSteps || dragTargetCurrentColumn != dragDistanceColumns)
|
||||
if ((dragTargetCurrentColumn != dragDistanceColumns && overlapsGrid) || dragTargetCurrentStep != dragDistanceSteps)
|
||||
{
|
||||
// Play a sound as we drag.
|
||||
this.playSound(Paths.sound('chartingSounds/noteLay'));
|
||||
|
|
@ -5381,15 +5403,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Actually set the cursor mode to the one we specified earlier.
|
||||
Cursor.cursorMode = targetCursorMode ?? Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gridGhostNote != null) gridGhostNote.visible = false;
|
||||
if (gridGhostHoldNote != null) gridGhostHoldNote.visible = false;
|
||||
if (gridGhostEvent != null) gridGhostEvent.visible = false;
|
||||
|
||||
// Do not set Cursor.cursorMode here, because it will be set by the HaxeUI.
|
||||
}
|
||||
}
|
||||
|
||||
function handleToolboxes():Void
|
||||
{
|
||||
|
|
@ -6202,7 +6215,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
var targetSong:Song;
|
||||
try
|
||||
{
|
||||
targetSong = Song.buildRaw(currentSongId, songMetadata.values(), availableVariations, songChartData, playtestSongScripts, false);
|
||||
targetSong = Song.buildRaw(currentSongId, songMetadata.values(), selectedVariation, songChartData, playtestSongScripts, false);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
|
|
@ -6411,6 +6424,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
if (gridTiledSprite != null)
|
||||
{
|
||||
gridTiledSprite.height = songLengthInPixels;
|
||||
measureTicks.setHeight(gridTiledSprite.height);
|
||||
}
|
||||
|
||||
// Remove any notes past the end of the song.
|
||||
|
|
@ -6472,6 +6486,46 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
||||
}
|
||||
|
||||
function cloneDifficulty(variation:String, difficulty:String, newVariation:String, newDifficulty:String, scrollSpeed:Float = 1.0):Void
|
||||
{
|
||||
var newVariationMetadata:Null<SongMetadata> = songMetadata.get(newVariation);
|
||||
if (newVariationMetadata == null) return;
|
||||
|
||||
var oldChartData:Null<SongChartData> = songChartData.get(variation);
|
||||
if (oldChartData == null)
|
||||
{
|
||||
// If we can't access the difficulty to copy, just create a blank one.
|
||||
createDifficulty(newVariation, newDifficulty, scrollSpeed);
|
||||
return;
|
||||
};
|
||||
|
||||
var newNoteData:Null<Array<SongNoteData>> = oldChartData.notes.get(difficulty)?.clone();
|
||||
if (newNoteData == null || newNoteData.length == 0)
|
||||
{
|
||||
// If we can't access the difficulty to copy, just create a blank one.
|
||||
createDifficulty(newVariation, newDifficulty, scrollSpeed);
|
||||
return;
|
||||
};
|
||||
|
||||
// Actually add the new difficulty.
|
||||
|
||||
newVariationMetadata.playData.difficulties.push(newDifficulty);
|
||||
|
||||
var newChartData = songChartData.get(newVariation);
|
||||
if (newChartData == null)
|
||||
{
|
||||
newChartData = new SongChartData([newDifficulty => scrollSpeed], [], [newDifficulty => newNoteData]);
|
||||
songChartData.set(newVariation, newChartData);
|
||||
}
|
||||
else
|
||||
{
|
||||
newChartData.scrollSpeed.set(newDifficulty, scrollSpeed);
|
||||
newChartData.notes.set(newDifficulty, newNoteData);
|
||||
}
|
||||
|
||||
difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
||||
}
|
||||
|
||||
function removeDifficulty(variation:String, difficulty:String):Void
|
||||
{
|
||||
var variationMetadata:Null<SongMetadata> = songMetadata.get(variation);
|
||||
|
|
@ -6486,6 +6540,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
resultChartData.notes.remove(difficulty);
|
||||
}
|
||||
|
||||
// Remove the variation if it is empty.
|
||||
if (songMetadata.size() > 1)
|
||||
{
|
||||
if (variation != Constants.DEFAULT_VARIATION && variationMetadata.playData.difficulties.length == 0)
|
||||
|
|
@ -6497,11 +6552,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
if (variation == selectedVariation)
|
||||
{
|
||||
var firstVariation = songMetadata.keyValues()[0];
|
||||
// Directly set the variation, since you can't undo removing the difficulty.
|
||||
if (firstVariation != null) selectedVariation = firstVariation;
|
||||
variationMetadata = songMetadata.get(selectedVariation);
|
||||
}
|
||||
}
|
||||
|
||||
// Directly set the difficulty rather than using a command, since you can't undo removing the difficulty.
|
||||
if (selectedDifficulty == difficulty
|
||||
|| !variationMetadata.playData.difficulties.contains(selectedDifficulty)) selectedDifficulty = variationMetadata.playData.difficulties[0];
|
||||
|
||||
|
|
@ -6550,10 +6607,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Go to the previous variation, then last difficulty in that variation.
|
||||
var currentVariationIndex:Int = availableVariations.indexOf(selectedVariation);
|
||||
var prevVariation = availableVariations[currentVariationIndex - 1];
|
||||
selectedVariation = prevVariation;
|
||||
var prevVariationDifficulties:Array<String> = getAvailableDifficulties(prevVariation);
|
||||
var prevDifficulty = prevVariationDifficulties[prevVariationDifficulties.length - 1];
|
||||
|
||||
var prevDifficulty = availableDifficulties[availableDifficulties.length - 1];
|
||||
selectedDifficulty = prevDifficulty;
|
||||
trace('${selectedVariation}:${selectedDifficulty} -> ${prevVariation}:${prevDifficulty}');
|
||||
performCommand(new SwitchDifficultyCommand(selectedDifficulty, prevDifficulty, selectedVariation, prevVariation));
|
||||
|
||||
Conductor.instance.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
||||
updateTimeSignature();
|
||||
|
|
@ -6565,7 +6623,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
// Go to previous difficulty in this variation.
|
||||
var prevDifficulty = availableDifficulties[currentDifficultyIndex - 1];
|
||||
selectedDifficulty = prevDifficulty;
|
||||
trace('${selectedVariation}:${selectedDifficulty} -> ${selectedVariation}:${prevDifficulty}');
|
||||
performCommand(new SwitchDifficultyCommand(selectedDifficulty, prevDifficulty, selectedVariation, selectedVariation));
|
||||
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
|
|
@ -6581,10 +6640,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
// Go to next variation, then first difficulty in that variation.
|
||||
var currentVariationIndex:Int = availableVariations.indexOf(selectedVariation);
|
||||
var nextVariation = availableVariations[currentVariationIndex + 1];
|
||||
selectedVariation = nextVariation;
|
||||
var nextVariationDifficulties:Array<String> = getAvailableDifficulties(nextVariation);
|
||||
var nextDifficulty = nextVariationDifficulties[0];
|
||||
|
||||
var nextDifficulty = availableDifficulties[0];
|
||||
selectedDifficulty = nextDifficulty;
|
||||
trace('${selectedVariation}:${selectedDifficulty} -> ${nextVariation}:${nextDifficulty}');
|
||||
performCommand(new SwitchDifficultyCommand(selectedDifficulty, nextDifficulty, selectedVariation, nextVariation));
|
||||
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
|
|
@ -6593,7 +6653,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
{
|
||||
// Go to next difficulty in this variation.
|
||||
var nextDifficulty = availableDifficulties[currentDifficultyIndex + 1];
|
||||
selectedDifficulty = nextDifficulty;
|
||||
trace('${selectedVariation}:${selectedDifficulty} -> ${selectedVariation}:${nextDifficulty}');
|
||||
performCommand(new SwitchDifficultyCommand(selectedDifficulty, nextDifficulty, selectedVariation, selectedVariation));
|
||||
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
|
|
@ -6719,32 +6780,16 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
/**
|
||||
* Updates the measure tick bitmap forcibly to make sure it's correct.
|
||||
*/
|
||||
function updateTimeSignature():Void
|
||||
{
|
||||
this.updateMeasureTicks(true);
|
||||
gridTiledSprite.loadGraphic(gridBitmap);
|
||||
}
|
||||
function updateTimeSignature():Void {}
|
||||
|
||||
/**
|
||||
* Handle positioning the measure ticks sprite.
|
||||
*/
|
||||
function handleMeasureTickPosition():Void
|
||||
{
|
||||
this.updateMeasureTicks();
|
||||
var currentMeasureTime = Conductor.instance.getMeasureTimeInMs(Math.floor(Conductor.instance.getTimeInMeasures(scrollPositionInMs)));
|
||||
var currentMeasurePos = currentMeasureTime < 0 ? 0 : Conductor.instance.getTimeInSteps(currentMeasureTime) * GRID_SIZE;
|
||||
measureTicks.y = gridTiledSprite?.y + currentMeasurePos;
|
||||
// Make sure the measure tick bitmap does not past the grid itself.
|
||||
var totalMeasureTicksHeight:Float = currentMeasurePos + measureTickBitmap.height;
|
||||
if (totalMeasureTicksHeight >= songLengthInPixels)
|
||||
{
|
||||
var spillOver:Float = totalMeasureTicksHeight - songLengthInPixels;
|
||||
measureTicks.setClipRect(new FlxRect(0, 0, measureTickBitmap.width, measureTickBitmap.height - spillOver));
|
||||
}
|
||||
else
|
||||
{
|
||||
measureTicks.setClipRect(null);
|
||||
}
|
||||
// var currentMeasureTime = Conductor.instance.getMeasureTimeInMs(Math.floor(Conductor.instance.getTimeInMeasures(scrollPositionInMs)));
|
||||
// var currentMeasurePos = currentMeasureTime < 0 ? 0 : Conductor.instance.getTimeInSteps(currentMeasureTime) * GRID_SIZE;
|
||||
measureTicks.y = gridTiledSprite?.y;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -21,24 +21,42 @@ class SwitchDifficultyCommand implements ChartEditorCommand
|
|||
this.newVariation = newVariation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the difficulty switch.
|
||||
* @param state The ChartEditorState to perform the action on.
|
||||
*/
|
||||
public function execute(state:ChartEditorState):Void
|
||||
{
|
||||
state.selectedVariation = newVariation != null ? newVariation : prevVariation;
|
||||
state.selectedDifficulty = newDifficulty != null ? newDifficulty : prevDifficulty;
|
||||
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
markDirty(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the difficulty switch.
|
||||
* @param state The ChartEditorState to perform the action on.
|
||||
*/
|
||||
public function undo(state:ChartEditorState):Void
|
||||
{
|
||||
state.selectedVariation = prevVariation != null ? prevVariation : newVariation;
|
||||
state.selectedDifficulty = prevDifficulty != null ? prevDifficulty : newDifficulty;
|
||||
|
||||
markDirty(state);
|
||||
}
|
||||
|
||||
function markDirty(state:ChartEditorState):Void
|
||||
{
|
||||
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
state.noteDisplayDirty = true;
|
||||
state.notePreviewDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param state The ChartEditorState to perform the action on.
|
||||
* @return Whether or not this instance of the command should be added to the history.
|
||||
* If the command didn't actually change anything, return `false` to prevent polluting the history.
|
||||
*/
|
||||
public function shouldAddToHistory(state:ChartEditorState):Bool
|
||||
{
|
||||
// Add to the history if we actually performed an action.
|
||||
|
|
@ -47,7 +65,7 @@ class SwitchDifficultyCommand implements ChartEditorCommand
|
|||
|
||||
public function toString():String
|
||||
{
|
||||
return 'Switch Difficulty';
|
||||
return 'Switch Difficulty to $newDifficulty ($newVariation)';
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
|
|
|||
|
|
@ -1,29 +1,63 @@
|
|||
package funkin.ui.debug.charting.components;
|
||||
|
||||
#if FEATURE_CHART_EDITOR
|
||||
import flixel.addons.display.FlxTiledSprite;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.math.FlxRect;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import openfl.display.BitmapData;
|
||||
import openfl.geom.Rectangle;
|
||||
|
||||
/**
|
||||
* Handles the display of the measure ticks and numbers on the left side.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChartEditorMeasureTicks extends FlxTypedSpriteGroup<FlxSprite>
|
||||
{
|
||||
/**
|
||||
* The owning ChartEditorState.
|
||||
*/
|
||||
var chartEditorState:ChartEditorState;
|
||||
|
||||
var measureTicksSprite:FlxSprite;
|
||||
var measureNumbers:Array<FlxText> = [];
|
||||
/**
|
||||
* The measure ticks underneath the numbers.
|
||||
*/
|
||||
var measureTicksSprite:FlxTiledSprite = new FlxTiledSprite(ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE * 16);
|
||||
|
||||
public var measureLengthsInPixels:Array<Int> = [];
|
||||
/**
|
||||
* The numbers that display the current measure number.
|
||||
* This is a group so we can kill and recycle its members.
|
||||
*/
|
||||
var measureNumbers:FlxTypedSpriteGroup<FlxText> = new FlxTypedSpriteGroup<FlxText>();
|
||||
|
||||
/**
|
||||
* The horizontal bars over the grid at each measure tick.
|
||||
*/
|
||||
var measureDividers:FlxTypedSpriteGroup<FlxSprite> = new FlxTypedSpriteGroup<FlxSprite>();
|
||||
|
||||
/**
|
||||
* The positions of each measure tick, in pixels, relative to the start of the song.
|
||||
*/
|
||||
var measurePositions:Array<Float> = [];
|
||||
|
||||
/**
|
||||
* A map of the
|
||||
* @param value
|
||||
* @return Float
|
||||
*/
|
||||
override function set_y(value:Float):Float
|
||||
{
|
||||
var result = super.set_y(value);
|
||||
// Don't update if the value hasn't changed.
|
||||
if (this.y == value) return value;
|
||||
|
||||
updateMeasureNumber();
|
||||
super.set_y(value);
|
||||
|
||||
return result;
|
||||
updateMeasureNumbers();
|
||||
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public function new(chartEditorState:ChartEditorState)
|
||||
|
|
@ -32,59 +66,182 @@ class ChartEditorMeasureTicks extends FlxTypedSpriteGroup<FlxSprite>
|
|||
|
||||
this.chartEditorState = chartEditorState;
|
||||
|
||||
measureTicksSprite = new FlxSprite(0, 0);
|
||||
add(measureTicksSprite);
|
||||
add(measureNumbers);
|
||||
add(measureDividers);
|
||||
|
||||
for (i in 0...5)
|
||||
buildMeasureTicksSprite();
|
||||
updateMeasureNumbers(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the overall height of the measure ticks.
|
||||
* @param height The desired height in pixels.
|
||||
*/
|
||||
public function setHeight(height:Float):Void
|
||||
{
|
||||
measureTicksSprite.height = height;
|
||||
}
|
||||
|
||||
public function updateTheme():Void
|
||||
{
|
||||
buildMeasureTicksSprite();
|
||||
updateMeasureNumbers(true);
|
||||
}
|
||||
|
||||
function buildMeasureTicksSprite():Void
|
||||
{
|
||||
var backingColor:FlxColor = switch (chartEditorState.currentTheme)
|
||||
{
|
||||
case Light: ChartEditorThemeHandler.MEASTURE_TICKS_BACKING_COLOR_LIGHT;
|
||||
case Dark: ChartEditorThemeHandler.MEASTURE_TICKS_BACKING_COLOR_DARK;
|
||||
default: ChartEditorThemeHandler.MEASTURE_TICKS_BACKING_COLOR_LIGHT;
|
||||
};
|
||||
var dividerColor:FlxColor = switch (chartEditorState.currentTheme)
|
||||
{
|
||||
case Light: ChartEditorThemeHandler.GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: ChartEditorThemeHandler.GRID_MEASURE_DIVIDER_COLOR_DARK;
|
||||
default: ChartEditorThemeHandler.GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||
};
|
||||
|
||||
// TODO: This does NOT account for time signature, and always assumes 4/4!
|
||||
// Better to have the little lines not line up than be required to redraw the image every frame,
|
||||
// but we need to fix this eventually.
|
||||
var stepsPerMeasure:Int = Constants.STEPS_PER_BEAT * 4;
|
||||
|
||||
// Start the bitmap with the basic gray color.
|
||||
var measureTickBitmap = new BitmapData(ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE * 16, true, backingColor);
|
||||
|
||||
// Draw the measure ticks at the top and bottom.
|
||||
measureTickBitmap.fillRect(new Rectangle(0, 0, ChartEditorState.GRID_SIZE, ChartEditorThemeHandler.MEASURE_TICKS_MEASURE_WIDTH / 2), dividerColor);
|
||||
var bottomTickY:Float = measureTickBitmap.height - (ChartEditorThemeHandler.MEASURE_TICKS_MEASURE_WIDTH / 2);
|
||||
measureTickBitmap.fillRect(new Rectangle(0, bottomTickY, ChartEditorState.GRID_SIZE, ChartEditorThemeHandler.MEASURE_TICKS_MEASURE_WIDTH / 2),
|
||||
dividerColor);
|
||||
|
||||
// Draw the beat ticks and dividers, and step ticks. No need for two seperate loops thankfully.
|
||||
for (i in 1...stepsPerMeasure)
|
||||
{
|
||||
if ((i % Constants.STEPS_PER_BEAT) == 0) // If we're on a beat, draw a beat tick and divider.
|
||||
{
|
||||
var beatTickY:Float = ChartEditorState.GRID_SIZE * i - (ChartEditorThemeHandler.MEASURE_TICKS_BEAT_WIDTH / 2);
|
||||
var beatTickLength:Float = ChartEditorState.GRID_SIZE * 2 / 3;
|
||||
measureTickBitmap.fillRect(new Rectangle(0, beatTickY, beatTickLength, ChartEditorThemeHandler.MEASURE_TICKS_BEAT_WIDTH), dividerColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Draw a step tick.
|
||||
var stepTickY:Float = ChartEditorState.GRID_SIZE * i - (ChartEditorThemeHandler.MEASURE_TICKS_STEP_WIDTH / 2);
|
||||
var stepTickLength:Float = ChartEditorState.GRID_SIZE * 1 / 3;
|
||||
measureTickBitmap.fillRect(new Rectangle(0, stepTickY, stepTickLength, ChartEditorThemeHandler.MEASURE_TICKS_STEP_WIDTH), dividerColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, set the sprite to use the image.
|
||||
measureTicksSprite.loadGraphic(measureTickBitmap);
|
||||
|
||||
// Destroy these so they get rebuilt with the right theme later.
|
||||
measureNumbers.forEach(function(measureNumber:FlxText) {
|
||||
measureNumber.destroy();
|
||||
});
|
||||
measureNumbers.clear();
|
||||
// Destroy these so they get rebuilt with the right theme later.
|
||||
measureDividers.forEach(function(measureDivider:FlxSprite) {
|
||||
measureDivider.destroy();
|
||||
});
|
||||
measureDividers.clear();
|
||||
}
|
||||
|
||||
// The last measure number we updated the ticks on.
|
||||
var previousMeasure:Int = 0;
|
||||
|
||||
function updateMeasureNumbers(force:Bool = false):Void
|
||||
{
|
||||
if (chartEditorState == null || Conductor.instance == null) return;
|
||||
|
||||
// Get the time at the top of the screen, in measures, rounded down.
|
||||
// This is the earliest measure we'll need to display a tick for.
|
||||
var currentMeasure:Int = Math.floor(Conductor.instance.getTimeInMeasures(chartEditorState.scrollPositionInMs));
|
||||
if (previousMeasure == currentMeasure && !force) return;
|
||||
if (currentMeasure < 0) currentMeasure = previousMeasure = 0;
|
||||
|
||||
// Remove existing measure numbers.
|
||||
measureNumbers.forEachAlive(function(measureNumber:FlxText) {
|
||||
measureNumber.kill();
|
||||
});
|
||||
measureDividers.forEachAlive(function(measureDivider:FlxSprite) {
|
||||
measureDivider.kill();
|
||||
});
|
||||
|
||||
final ARBITRARY_LIMIT = 5;
|
||||
|
||||
for (i in 0...ARBITRARY_LIMIT)
|
||||
{
|
||||
var targetMeasure:Int = currentMeasure + i - 1;
|
||||
if (targetMeasure < 0) continue;
|
||||
|
||||
// TODO: This math is kinda awkward but DOES account for time signatures,
|
||||
// might want some cleanup though? Maybe add a `getMeasureTimeInSteps` method?
|
||||
var measureTimeInMs:Float = Conductor.instance.getMeasureTimeInMs(targetMeasure);
|
||||
var measureTimeInSteps:Float = Conductor.instance.getTimeInSteps(measureTimeInMs);
|
||||
var measureTimeInPixels:Float = measureTimeInSteps * ChartEditorState.GRID_SIZE;
|
||||
|
||||
var relativeMeasureTimeInPixels:Float = measureTimeInPixels + this.y;
|
||||
|
||||
final SCREEN_PADDING:Float = ChartEditorState.GRID_SIZE / 2;
|
||||
|
||||
// Above the visible area, keep going.
|
||||
if (relativeMeasureTimeInPixels < 0 - SCREEN_PADDING)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Below the visible area, quit it.
|
||||
if (relativeMeasureTimeInPixels > FlxG.height + SCREEN_PADDING)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Else, display a number.
|
||||
|
||||
// Reuse an existing number. If we need a new number, create one with makeMeasureNumber().
|
||||
final REVIVE:Bool = true;
|
||||
var measureNumber = measureNumbers.recycle(makeMeasureNumber, false, REVIVE);
|
||||
|
||||
trace('Placing measure number. $relativeMeasureTimeInPixels -> $targetMeasure');
|
||||
|
||||
// Measures are base ZERO gah!
|
||||
final OFFSET = 2;
|
||||
measureNumber.text = '${targetMeasure + 1}';
|
||||
measureNumber.y = relativeMeasureTimeInPixels + OFFSET;
|
||||
measureNumber.x = this.x;
|
||||
|
||||
// Place a measure divider too.
|
||||
var measureDivider = measureDividers.recycle(makeMeasureDivider, false, REVIVE);
|
||||
measureDivider.y = relativeMeasureTimeInPixels - (ChartEditorThemeHandler.MEASURE_TICKS_MEASURE_WIDTH / 2);
|
||||
measureDivider.x = this.x + (measureTicksSprite.width);
|
||||
}
|
||||
}
|
||||
|
||||
function makeMeasureNumber():FlxText
|
||||
{
|
||||
var measureNumber = new FlxText(0, 0, ChartEditorState.GRID_SIZE, "1");
|
||||
measureNumber.setFormat(Paths.font('vcr.ttf'), 20, FlxColor.WHITE);
|
||||
measureNumber.borderStyle = FlxTextBorderStyle.OUTLINE;
|
||||
measureNumber.borderColor = FlxColor.BLACK;
|
||||
add(measureNumber);
|
||||
measureNumbers.push(measureNumber);
|
||||
}
|
||||
// Need these two lines or the ticks don't render before loading a chart!
|
||||
chartEditorState.updateMeasureTicks(true);
|
||||
reloadTickBitmap();
|
||||
return measureNumber;
|
||||
}
|
||||
|
||||
public function reloadTickBitmap():Void
|
||||
function makeMeasureDivider():FlxSprite
|
||||
{
|
||||
measureTicksSprite.loadGraphic(chartEditorState.measureTickBitmap);
|
||||
}
|
||||
|
||||
public function setClipRect(rect:Null<FlxRect>):Void
|
||||
var dividerColor:FlxColor = switch (chartEditorState.currentTheme)
|
||||
{
|
||||
measureTicksSprite.clipRect = rect;
|
||||
}
|
||||
case Light: ChartEditorThemeHandler.GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: ChartEditorThemeHandler.GRID_MEASURE_DIVIDER_COLOR_DARK;
|
||||
default: ChartEditorThemeHandler.GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Update all 5 measure numbers, since that's the most we can really see at a time, even if barely.
|
||||
* Please excuse the horror you're about to witness.
|
||||
* Welp, you gotta go back in commits to see it now.
|
||||
*/
|
||||
function updateMeasureNumber()
|
||||
{
|
||||
if (measureLengthsInPixels.length == 0 || measureLengthsInPixels == null) return;
|
||||
|
||||
var currentMeasure:Int = Math.floor(Conductor.instance?.getTimeInMeasures(chartEditorState.scrollPositionInMs));
|
||||
for (i in 0...measureNumbers.length)
|
||||
{
|
||||
var measureNumber:FlxText = measureNumbers[i];
|
||||
if (measureNumber == null) continue;
|
||||
|
||||
var measureNumberPosition = measureLengthsInPixels[i];
|
||||
measureNumber.y = this.y + measureNumberPosition;
|
||||
|
||||
// Show the measure number only if it isn't beneath the end of the note grid.
|
||||
// Using measureNumber + 1 because the cut-off bar at the bottom is technically a bar, but it looks bad if a measure number shows up there.
|
||||
var fixedMeasureNumberValue = currentMeasure + i + 1;
|
||||
if (fixedMeasureNumberValue < Math.ceil(Conductor.instance?.getTimeInMeasures(chartEditorState.songLengthInMs)))
|
||||
measureNumber.text = '${fixedMeasureNumberValue}';
|
||||
else
|
||||
measureNumber.text = '';
|
||||
}
|
||||
var measureDivider = new FunkinSprite().makeSolidColor(ChartEditorState.GRID_SIZE * ChartEditorThemeHandler.TOTAL_COLUMN_COUNT,
|
||||
ChartEditorThemeHandler.MEASURE_TICKS_MEASURE_WIDTH, dividerColor);
|
||||
return measureDivider;
|
||||
}
|
||||
}
|
||||
#end
|
||||
|
|
|
|||
|
|
@ -63,6 +63,8 @@ class ChartEditorDialogHandler
|
|||
static final CHART_EDITOR_DIALOG_USER_GUIDE_LAYOUT:String = Paths.ui('chart-editor/dialogs/user-guide');
|
||||
static final CHART_EDITOR_DIALOG_ADD_VARIATION_LAYOUT:String = Paths.ui('chart-editor/dialogs/add-variation');
|
||||
static final CHART_EDITOR_DIALOG_ADD_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/dialogs/add-difficulty');
|
||||
static final CHART_EDITOR_DIALOG_CLONE_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/dialogs/clone-difficulty');
|
||||
static final CHART_EDITOR_DIALOG_MOVE_DIFFICULTY_LAYOUT:String = Paths.ui('chart-editor/dialogs/move-difficulty');
|
||||
static final CHART_EDITOR_DIALOG_BACKUP_AVAILABLE_LAYOUT:String = Paths.ui('chart-editor/dialogs/backup-available');
|
||||
|
||||
/**
|
||||
|
|
@ -1386,6 +1388,86 @@ class ChartEditorDialogHandler
|
|||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and opens a dialog where the user can copy an existing difficulty for a song.
|
||||
* @param state The current chart editor state.
|
||||
* @param deleteOriginal Whether to delete the original difficulty after copying.
|
||||
* This essentially turns the copy into a move.
|
||||
* @param closable Whether the dialog can be closed by the user.
|
||||
* @return The dialog that was opened.
|
||||
*/
|
||||
public static function openCloneDifficultyDialog(state:ChartEditorState, deleteOriginal:Bool, closable:Bool = true):Dialog
|
||||
{
|
||||
var layout = deleteOriginal ? CHART_EDITOR_DIALOG_MOVE_DIFFICULTY_LAYOUT : CHART_EDITOR_DIALOG_CLONE_DIFFICULTY_LAYOUT;
|
||||
var dialog:Null<Dialog> = openDialog(state, layout, true, false);
|
||||
if (dialog == null) throw 'Could not locate Clone/Move Difficulty dialog';
|
||||
|
||||
var difficultyForm:Null<Form> = dialog.findComponent('difficultyForm', Form);
|
||||
if (difficultyForm == null) throw 'Could not locate difficultyForm Form in Clone Difficulty dialog';
|
||||
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Clone Difficulty dialog';
|
||||
buttonCancel.onClick = function(_) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var dialogClone:Null<Button> = dialog.findComponent('dialogClone', Button);
|
||||
if (dialogClone == null) throw 'Could not locate dialogClone button in Clone Difficulty dialog';
|
||||
dialogClone.onClick = function(_) {
|
||||
// This performs validation before the onSubmit callback is called.
|
||||
difficultyForm.submit();
|
||||
}
|
||||
|
||||
var dialogVariation:Null<DropDown> = dialog.findComponent('dialogVariation', DropDown);
|
||||
if (dialogVariation == null) throw 'Could not locate dialogVariation DropDown in Clone Variation dialog';
|
||||
dialogVariation.value = ChartEditorDropdowns.populateDropdownWithVariations(dialogVariation, state, true);
|
||||
|
||||
var labelScrollSpeed:Null<Label> = dialog.findComponent('labelScrollSpeed', Label);
|
||||
if (labelScrollSpeed == null) throw 'Could not find labelScrollSpeed component.';
|
||||
|
||||
var inputScrollSpeed:Null<Slider> = dialog.findComponent('inputScrollSpeed', Slider);
|
||||
if (inputScrollSpeed == null) throw 'Could not find inputScrollSpeed component.';
|
||||
inputScrollSpeed.onChange = function(event:UIEvent) {
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${inputScrollSpeed.value}x';
|
||||
};
|
||||
inputScrollSpeed.value = state.currentSongChartScrollSpeed;
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${inputScrollSpeed.value}x';
|
||||
|
||||
difficultyForm.onSubmit = function(_) {
|
||||
trace('Clone Difficulty dialog submitted, validation succeeded!');
|
||||
|
||||
var dialogDifficultyName:Null<TextField> = dialog.findComponent('dialogDifficultyName', TextField);
|
||||
if (dialogDifficultyName == null) throw 'Could not locate dialogDifficultyName TextField in Add Difficulty dialog';
|
||||
|
||||
var variationToClone:String = state.selectedVariation;
|
||||
var difficultyToClone:String = state.selectedDifficulty;
|
||||
var targetVariation:String = dialogVariation.value.id;
|
||||
var targetDifficulty:String = dialogDifficultyName.text.toLowerCase();
|
||||
|
||||
state.cloneDifficulty(variationToClone, difficultyToClone, targetVariation, targetDifficulty, inputScrollSpeed.value ?? 1.0);
|
||||
|
||||
if (deleteOriginal)
|
||||
{
|
||||
state.removeDifficulty(variationToClone, difficultyToClone);
|
||||
state.selectedDifficulty = targetDifficulty;
|
||||
state.selectedVariation = targetVariation;
|
||||
|
||||
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
state.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
|
||||
state.success('Move Difficulty', 'Moved difficulty "$difficultyToClone" to "${dialogDifficultyName.text.toLowerCase()}"');
|
||||
}
|
||||
else
|
||||
{
|
||||
state.success('Clone Difficulty', 'Cloned difficulty "$difficultyToClone" to "${dialogDifficultyName.text.toLowerCase()}"');
|
||||
}
|
||||
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
}
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds and opens a dialog where the user can confirm to leave the chart editor if they have unsaved changes.
|
||||
* @param state The current chart editor state.
|
||||
|
|
|
|||
|
|
@ -25,35 +25,37 @@ class ChartEditorThemeHandler
|
|||
static final BACKGROUND_COLOR_DARK:FlxColor = 0xFF361E60;
|
||||
|
||||
// Color 1 of the grid pattern. Alternates with Color 2.
|
||||
static final GRID_COLOR_1_LIGHT:FlxColor = 0xFFE7E6E6;
|
||||
static final GRID_COLOR_1_DARK:FlxColor = 0xFF181919;
|
||||
public static final GRID_COLOR_1_LIGHT:FlxColor = 0xFFE7E6E6;
|
||||
public static final GRID_COLOR_1_DARK:FlxColor = 0xFF181919;
|
||||
|
||||
// Color 2 of the grid pattern. Alternates with Color 1.
|
||||
static final GRID_COLOR_2_LIGHT:FlxColor = 0xFFF8F8F8;
|
||||
static final GRID_COLOR_2_DARK:FlxColor = 0xFF202020;
|
||||
public static final GRID_COLOR_2_LIGHT:FlxColor = 0xFFF8F8F8;
|
||||
public static final GRID_COLOR_2_DARK:FlxColor = 0xFF202020;
|
||||
|
||||
// Color 3 of the grid pattern. Borders the other colors.
|
||||
static final GRID_COLOR_3_LIGHT:FlxColor = 0xFFD9D5D5;
|
||||
static final GRID_COLOR_3_DARK:FlxColor = 0xFF262A2A;
|
||||
public static final GRID_COLOR_3_LIGHT:FlxColor = 0xFFD9D5D5;
|
||||
public static final GRID_COLOR_3_DARK:FlxColor = 0xFF262A2A;
|
||||
|
||||
// Vertical divider between characters.
|
||||
static final GRID_STRUMLINE_DIVIDER_COLOR_LIGHT:FlxColor = 0xFF111111;
|
||||
static final GRID_STRUMLINE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||
public static final GRID_STRUMLINE_DIVIDER_COLOR_LIGHT:FlxColor = 0xFF111111;
|
||||
public static final GRID_STRUMLINE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||
static final GRID_STRUMLINE_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH;
|
||||
|
||||
// Horizontal divider between measures.
|
||||
static final GRID_MEASURE_DIVIDER_COLOR_LIGHT:FlxColor = 0xFF111111;
|
||||
static final GRID_MEASURE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||
public static final GRID_MEASURE_DIVIDER_COLOR_LIGHT:FlxColor = 0xFF111111;
|
||||
public static final GRID_MEASURE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||
static final GRID_MEASURE_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH;
|
||||
|
||||
// Horizontal divider between beats.
|
||||
static final GRID_BEAT_DIVIDER_COLOR_LIGHT:FlxColor = 0xFFC1C1C1;
|
||||
static final GRID_BEAT_DIVIDER_COLOR_DARK:FlxColor = 0xFF848484;
|
||||
static final GRID_BEAT_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH;
|
||||
public static final MEASTURE_TICKS_BACKING_COLOR_LIGHT:FlxColor = 0xFFC1C1C1;
|
||||
public static final MEASTURE_TICKS_BACKING_COLOR_DARK:FlxColor = 0xFF484848;
|
||||
|
||||
// Border on the square highlighting selected notes.
|
||||
static final SELECTION_SQUARE_BORDER_COLOR_LIGHT:FlxColor = 0xFF339933;
|
||||
static final SELECTION_SQUARE_BORDER_COLOR_DARK:FlxColor = 0xFF339933;
|
||||
|
||||
/**
|
||||
* The width of the opaque border around the square highlighting selected notes.
|
||||
*/
|
||||
public static final SELECTION_SQUARE_BORDER_WIDTH:Int = 1;
|
||||
|
||||
// Fill on the square highlighting selected notes.
|
||||
|
|
@ -65,6 +67,11 @@ class ChartEditorThemeHandler
|
|||
static final PLAYHEAD_BLOCK_BORDER_COLOR:FlxColor = 0xFF9D0011;
|
||||
static final PLAYHEAD_BLOCK_FILL_COLOR:FlxColor = 0xFFBD0231;
|
||||
|
||||
// Lines on the measure ticks.
|
||||
public static final MEASURE_TICKS_MEASURE_WIDTH:Int = 6;
|
||||
public static final MEASURE_TICKS_BEAT_WIDTH:Int = 4;
|
||||
public static final MEASURE_TICKS_STEP_WIDTH:Int = 2;
|
||||
|
||||
// Border on the square over the note preview.
|
||||
static final NOTE_PREVIEW_VIEWPORT_BORDER_COLOR_LIGHT = 0xFFF8A657;
|
||||
static final NOTE_PREVIEW_VIEWPORT_BORDER_COLOR_DARK = 0xFFF8A657;
|
||||
|
|
@ -73,10 +80,7 @@ class ChartEditorThemeHandler
|
|||
static final NOTE_PREVIEW_VIEWPORT_FILL_COLOR_LIGHT = 0x80F8A657;
|
||||
static final NOTE_PREVIEW_VIEWPORT_FILL_COLOR_DARK = 0x80F8A657;
|
||||
|
||||
static final TOTAL_COLUMN_COUNT:Int = ChartEditorState.STRUMLINE_SIZE * 2 + 1;
|
||||
|
||||
// Used for checking in updateMeasureTicks() so we don't have to redraw it everytime if the current measure is the same as before.
|
||||
static var previousMeasure:Int = -69;
|
||||
public static final TOTAL_COLUMN_COUNT:Int = ChartEditorState.STRUMLINE_SIZE * 2 + 1;
|
||||
|
||||
/**
|
||||
* When the theme is changed, this function updates all of the UI elements to match the new theme.
|
||||
|
|
@ -86,10 +90,10 @@ class ChartEditorThemeHandler
|
|||
{
|
||||
updateBackground(state);
|
||||
updateGridBitmap(state);
|
||||
updateMeasureTicks(state);
|
||||
updateOffsetTicks(state);
|
||||
updateSelectionSquare(state);
|
||||
updateNotePreview(state);
|
||||
updateMeasureTicks(state);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -127,6 +131,13 @@ class ChartEditorThemeHandler
|
|||
default: GRID_COLOR_2_LIGHT;
|
||||
};
|
||||
|
||||
var dividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
case Light: GRID_STRUMLINE_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: GRID_STRUMLINE_DIVIDER_COLOR_DARK;
|
||||
default: GRID_STRUMLINE_DIVIDER_COLOR_LIGHT;
|
||||
}
|
||||
|
||||
// Draw the base grid.
|
||||
|
||||
// 2 * (Strumline Size) + 1 grid squares wide, by (4 * quarter notes per measure) grid squares tall.
|
||||
|
|
@ -161,7 +172,7 @@ class ChartEditorThemeHandler
|
|||
ChartEditorState.GRID_SELECTION_BORDER_WIDTH),
|
||||
selectionBorderColor);
|
||||
|
||||
// Selection border at left.
|
||||
// Selection border at far left.
|
||||
state.gridBitmap.fillRect(new Rectangle(-(ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2), 0, ChartEditorState.GRID_SELECTION_BORDER_WIDTH,
|
||||
state.gridBitmap.height),
|
||||
selectionBorderColor);
|
||||
|
|
@ -169,12 +180,14 @@ class ChartEditorThemeHandler
|
|||
// Selection borders vertically along the middle.
|
||||
for (i in 1...TOTAL_COLUMN_COUNT)
|
||||
{
|
||||
var isStrumlineColumn:Bool = (i % ChartEditorState.STRUMLINE_SIZE == 0) && (i > 0);
|
||||
|
||||
state.gridBitmap.fillRect(new Rectangle((ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2), 0,
|
||||
ChartEditorState.GRID_SELECTION_BORDER_WIDTH, state.gridBitmap.height),
|
||||
selectionBorderColor);
|
||||
isStrumlineColumn ? dividerColor : selectionBorderColor);
|
||||
}
|
||||
|
||||
// Selection border at right.
|
||||
// Selection border at far right.
|
||||
state.gridBitmap.fillRect(new Rectangle(state.gridBitmap.width - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2), 0,
|
||||
ChartEditorState.GRID_SELECTION_BORDER_WIDTH, state.gridBitmap.height),
|
||||
selectionBorderColor);
|
||||
|
|
@ -186,115 +199,6 @@ class ChartEditorThemeHandler
|
|||
// Else, gridTiledSprite will be built later.
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw the measure ticks left of the grid. This also includes the horizontal beat and measure dividers as well as the vertical strumline dividers.
|
||||
*/
|
||||
public static function updateMeasureTicks(state:ChartEditorState, force:Bool = false):Void
|
||||
{
|
||||
var measureTickWidth:Int = 6;
|
||||
var beatTickWidth:Int = 4;
|
||||
var stepTickWidth:Int = 2;
|
||||
var measuresToCheck:Int = 5;
|
||||
|
||||
var currentMeasure:Int = Math.floor(Conductor.instance.getTimeInMeasures(state.scrollPositionInMs));
|
||||
if (previousMeasure == currentMeasure && !force) return;
|
||||
if (currentMeasure < 0) currentMeasure = previousMeasure = 0;
|
||||
|
||||
// Make the bitmap.
|
||||
var ticksWidth:Int = Std.int(ChartEditorState.GRID_SIZE);
|
||||
var linesWidth:Int = Std.int(ChartEditorState.GRID_SIZE * TOTAL_COLUMN_COUNT);
|
||||
state.measureTickBitmap = new BitmapData(ticksWidth + linesWidth, 6144, true, 0x00FFFFFF);
|
||||
|
||||
// Draw the vertical grey sidebar.
|
||||
state.measureTickBitmap.fillRect(new Rectangle(0, 0, ticksWidth, 6144), GRID_BEAT_DIVIDER_COLOR_DARK);
|
||||
|
||||
// Then draw the ticks and dividers themselves.
|
||||
var totalTicksHeight:Int = 0;
|
||||
var measureLengthsInPixels:Array<Int> = [];
|
||||
for (measure in 0...measuresToCheck)
|
||||
{
|
||||
var currentTimeInMs:Float = Conductor.instance.getMeasureTimeInMs(currentMeasure + measure)
|
||||
+ 10; // Manual adjustment as it can get the wrong time change without this.
|
||||
var currentTimeChange:SongTimeChange = Conductor.instance.getTimeChange(currentTimeInMs);
|
||||
var stepsPerMeasure:Int = Constants.STEPS_PER_BEAT * currentTimeChange.timeSignatureNum;
|
||||
var ticksHeight:Int = Std.int(ChartEditorState.GRID_SIZE * stepsPerMeasure);
|
||||
|
||||
// Draw horizontal dividers between the measures.
|
||||
var gridMeasureDividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
case Light: GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: GRID_MEASURE_DIVIDER_COLOR_DARK;
|
||||
default: GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||
};
|
||||
|
||||
// Divider at top
|
||||
state.measureTickBitmap.fillRect(new Rectangle(ticksWidth, totalTicksHeight, linesWidth, GRID_MEASURE_DIVIDER_WIDTH / 2), gridMeasureDividerColor);
|
||||
// Divider at bottom
|
||||
var dividerLineBY:Float = ticksHeight - (GRID_MEASURE_DIVIDER_WIDTH / 2);
|
||||
state.measureTickBitmap.fillRect(new Rectangle(ticksWidth, totalTicksHeight + dividerLineBY, linesWidth, GRID_MEASURE_DIVIDER_WIDTH / 2),
|
||||
gridMeasureDividerColor);
|
||||
|
||||
// Measure ticks.
|
||||
state.measureTickBitmap.fillRect(new Rectangle(0, totalTicksHeight, ticksWidth, measureTickWidth / 2), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||
var bottomTickY:Float = ticksHeight - (measureTickWidth / 2);
|
||||
state.measureTickBitmap.fillRect(new Rectangle(0, totalTicksHeight + bottomTickY, ticksWidth, measureTickWidth / 2), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||
|
||||
// Draw horizontal dividers between the beats, this is done inside the following loop.
|
||||
var gridBeatDividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
case Light: GRID_BEAT_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: GRID_BEAT_DIVIDER_COLOR_DARK;
|
||||
default: GRID_BEAT_DIVIDER_COLOR_LIGHT;
|
||||
};
|
||||
|
||||
// Draw the beat ticks and dividers, and step ticks. No need for two seperate loops thankfully.
|
||||
for (i in 1...stepsPerMeasure)
|
||||
{
|
||||
if ((i % Constants.STEPS_PER_BEAT) == 0) // If we're on a beat, draw a beat tick and divider.
|
||||
{
|
||||
var beatTickY:Float = totalTicksHeight + ChartEditorState.GRID_SIZE * i - (beatTickWidth / 2);
|
||||
var beatTickLength:Float = ticksWidth * 2 / 3;
|
||||
state.measureTickBitmap.fillRect(new Rectangle(0, beatTickY, beatTickLength, beatTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||
|
||||
// Horizontal divider.
|
||||
state.measureTickBitmap.fillRect(new Rectangle(ticksWidth, totalTicksHeight + (ChartEditorState.GRID_SIZE * i) - (GRID_BEAT_DIVIDER_WIDTH / 2),
|
||||
linesWidth, GRID_BEAT_DIVIDER_WIDTH),
|
||||
gridBeatDividerColor);
|
||||
}
|
||||
else // Else, draw a step tick.
|
||||
{
|
||||
var stepTickY:Float = totalTicksHeight + ChartEditorState.GRID_SIZE * i - (stepTickWidth / 2);
|
||||
var stepTickLength:Float = ticksWidth * 1 / 3;
|
||||
state.measureTickBitmap.fillRect(new Rectangle(0, stepTickY, stepTickLength, stepTickWidth), GRID_MEASURE_DIVIDER_COLOR_LIGHT);
|
||||
}
|
||||
}
|
||||
measureLengthsInPixels.push(totalTicksHeight);
|
||||
totalTicksHeight += ticksHeight;
|
||||
}
|
||||
previousMeasure = currentMeasure;
|
||||
|
||||
// Finally, draw vertical dividers between the strumlines.
|
||||
var gridStrumlineDividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
case Light: GRID_STRUMLINE_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: GRID_STRUMLINE_DIVIDER_COLOR_DARK;
|
||||
default: GRID_STRUMLINE_DIVIDER_COLOR_LIGHT;
|
||||
};
|
||||
|
||||
// Divider at 1 * (Strumline Size)
|
||||
var dividerLineAX:Float = ticksWidth + ChartEditorState.GRID_SIZE * (ChartEditorState.STRUMLINE_SIZE) - (GRID_STRUMLINE_DIVIDER_WIDTH / 2);
|
||||
state.measureTickBitmap.fillRect(new Rectangle(dividerLineAX, 0, GRID_STRUMLINE_DIVIDER_WIDTH, state.measureTickBitmap.height), gridStrumlineDividerColor);
|
||||
// Divider at 2 * (Strumline Size)
|
||||
var dividerLineBX:Float = ticksWidth + ChartEditorState.GRID_SIZE * (ChartEditorState.STRUMLINE_SIZE * 2) - (GRID_STRUMLINE_DIVIDER_WIDTH / 2);
|
||||
state.measureTickBitmap.fillRect(new Rectangle(dividerLineBX, 0, GRID_STRUMLINE_DIVIDER_WIDTH, state.measureTickBitmap.height), gridStrumlineDividerColor);
|
||||
|
||||
if (state.measureTicks != null)
|
||||
{
|
||||
state.measureTicks.measureLengthsInPixels = measureLengthsInPixels;
|
||||
state.measureTicks.reloadTickBitmap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Horizontal offset ticks.
|
||||
*/
|
||||
|
|
@ -306,7 +210,7 @@ class ChartEditorThemeHandler
|
|||
var ticksWidth:Int = Std.int(ChartEditorState.GRID_SIZE * Conductor.instance.stepsPerMeasure); // 10 minor ticks wide.
|
||||
var ticksHeight:Int = Std.int(ChartEditorState.GRID_SIZE); // 1 grid squares tall.
|
||||
state.offsetTickBitmap = new BitmapData(ticksWidth, ticksHeight, true);
|
||||
state.offsetTickBitmap.fillRect(new Rectangle(0, 0, ticksWidth, ticksHeight), GRID_BEAT_DIVIDER_COLOR_DARK);
|
||||
state.offsetTickBitmap.fillRect(new Rectangle(0, 0, ticksWidth, ticksHeight), 0xFFC4C4C4);
|
||||
|
||||
// Draw the major ticks.
|
||||
var leftTickX:Float = 0;
|
||||
|
|
@ -420,6 +324,11 @@ class ChartEditorThemeHandler
|
|||
}
|
||||
}
|
||||
|
||||
static function updateMeasureTicks(state:ChartEditorState):Void
|
||||
{
|
||||
state.measureTicks?.updateTheme();
|
||||
}
|
||||
|
||||
public static function buildPlayheadBlock():FlxSprite
|
||||
{
|
||||
var playheadBlock:FlxSprite = new FlxSprite();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package funkin.ui.debug.charting.toolboxes;
|
||||
|
||||
import funkin.ui.debug.charting.commands.SwitchDifficultyCommand;
|
||||
#if FEATURE_CHART_EDITOR
|
||||
import funkin.data.song.SongData.SongChartData;
|
||||
import funkin.data.song.SongData.SongMetadata;
|
||||
|
|
@ -71,6 +72,14 @@ class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox
|
|||
chartEditorState.openAddDifficultyDialog(true);
|
||||
};
|
||||
|
||||
difficultyToolboxCloneDifficulty.onClick = function(_:UIEvent) {
|
||||
chartEditorState.openCloneDifficultyDialog(false, true);
|
||||
};
|
||||
|
||||
difficultyToolboxMoveDifficulty.onClick = function(_:UIEvent) {
|
||||
chartEditorState.openCloneDifficultyDialog(true, true);
|
||||
};
|
||||
|
||||
difficultyToolboxRemoveDifficulty.onClick = function(_:UIEvent) {
|
||||
var currentVariation:String = chartEditorState.selectedVariation;
|
||||
var currentDifficulty:String = chartEditorState.selectedDifficulty;
|
||||
|
|
@ -277,9 +286,10 @@ class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox
|
|||
if (variation != null && difficulty != null)
|
||||
{
|
||||
trace('Changing difficulty to "$variation:$difficulty"');
|
||||
chartEditorState.selectedVariation = variation;
|
||||
chartEditorState.selectedDifficulty = difficulty;
|
||||
chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
|
||||
chartEditorState.performCommand(new SwitchDifficultyCommand(chartEditorState.selectedDifficulty, difficulty, chartEditorState.selectedVariation,
|
||||
variation));
|
||||
|
||||
refreshTreeSelection();
|
||||
}
|
||||
// case 'song':
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import funkin.ui.MenuList.MenuTypedList;
|
|||
import funkin.ui.MenuList.MenuTypedItem;
|
||||
import flixel.text.FlxText;
|
||||
import funkin.data.freeplay.player.PlayerRegistry;
|
||||
import funkin.ui.freeplay.charselect.PlayableCharacter;
|
||||
import funkin.ui.options.items.CheckboxPreferenceItem;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.tweens.FlxTween;
|
||||
|
|
@ -55,7 +56,8 @@ class ResultsDebugSubState extends MusicBeatSubState
|
|||
});
|
||||
});
|
||||
createToggleListItem("Character", PlayerRegistry.instance.listEntryIds(), function(result:String) {
|
||||
resultsParams.characterId = result;
|
||||
var playableCharacter:PlayableCharacter = PlayerRegistry.instance.fetchEntry(result);
|
||||
resultsParams.characterId = playableCharacter.getOwnedCharacterIds()[0];
|
||||
});
|
||||
createToggleListItem("Results Mode", ["Debug", "Story", "Freeplay"], function(result:String) {
|
||||
returnToDebugScreen = result == "Debug"; // We will create the ResultsState as a Substate, that we will just close and return back to here
|
||||
|
|
|
|||
|
|
@ -1,125 +1,95 @@
|
|||
package funkin.ui.freeplay;
|
||||
|
||||
import flixel.FlxObject;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.math.FlxPoint;
|
||||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxSort;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.util.FlxDestroyUtil;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.util.FlxSort;
|
||||
|
||||
// its kinda like marqeee html lol!
|
||||
@:nullSafety
|
||||
class BGScrollingText extends FlxSpriteGroup
|
||||
class BGScrollingText extends FlxText
|
||||
{
|
||||
var grpTexts:FlxTypedSpriteGroup<FlxSprite>;
|
||||
var sourceText:FlxText;
|
||||
var _textPositions:Array<FlxPoint> = [];
|
||||
var _positionCache:FlxPoint = FlxPoint.get();
|
||||
|
||||
public var widthShit:Float = FlxG.width;
|
||||
public var placementOffset:Float = 20;
|
||||
public var speed:Float = 1;
|
||||
public var size(default, set):Int = 48;
|
||||
|
||||
public var funnyColor(default, set):FlxColor = 0xFFFFFFFF;
|
||||
@:deprecated("Use color instead")
|
||||
public var funnyColor(get, set):FlxColor;
|
||||
|
||||
function get_funnyColor():FlxColor
|
||||
return color;
|
||||
|
||||
function set_funnyColor(c:FlxColor):FlxColor
|
||||
{
|
||||
return color = c;
|
||||
}
|
||||
|
||||
public function new(x:Float, y:Float, text:String, widthShit:Float = 100, ?bold:Bool = false, ?size:Int = 48)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
grpTexts = new FlxTypedSpriteGroup<FlxSprite>();
|
||||
super(x, y, 0, text, size);
|
||||
_positionCache = FlxPoint.get(x, y);
|
||||
font = "5by7";
|
||||
this.bold = bold ?? false;
|
||||
|
||||
this.widthShit = widthShit;
|
||||
|
||||
// Only keep one FlxText graphic at a time for batching
|
||||
sourceText = new FlxText(0, 0, 0, text, size ?? this.size);
|
||||
sourceText.font = "5by7";
|
||||
sourceText.bold = bold ?? false;
|
||||
|
||||
@:privateAccess
|
||||
sourceText.regenGraphic();
|
||||
regenGraphic();
|
||||
|
||||
var needed:Int = Math.ceil(widthShit / sourceText.frameWidth) + 1;
|
||||
var needed:Int = Math.ceil(widthShit / frameWidth) + 1;
|
||||
|
||||
for (i in 0...needed)
|
||||
{
|
||||
var coolText = new FlxSprite((i * sourceText.frameWidth) + (i * 20), 0);
|
||||
grpTexts.add(coolText);
|
||||
}
|
||||
|
||||
if (size != null) this.size = size;
|
||||
|
||||
add(grpTexts);
|
||||
}
|
||||
|
||||
function reloadGraphics()
|
||||
{
|
||||
if (grpTexts != null)
|
||||
{
|
||||
@:privateAccess
|
||||
sourceText.regenGraphic();
|
||||
grpTexts.forEach(function(txt:FlxSprite) {
|
||||
txt.loadGraphic(sourceText.graphic);
|
||||
txt.updateHitbox();
|
||||
});
|
||||
_textPositions.push(FlxPoint.get((i * frameWidth) + (i * 20), 0));
|
||||
}
|
||||
}
|
||||
|
||||
function set_size(value:Int):Int
|
||||
override public function update(elapsed:Float):Void
|
||||
{
|
||||
sourceText.size = value;
|
||||
reloadGraphics();
|
||||
this.size = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
function set_funnyColor(value:FlxColor):FlxColor
|
||||
{
|
||||
sourceText.color = value;
|
||||
reloadGraphics();
|
||||
this.funnyColor = value;
|
||||
return value;
|
||||
}
|
||||
|
||||
override public function update(elapsed:Float)
|
||||
{
|
||||
for (txt in grpTexts.group)
|
||||
{
|
||||
if (txt == null) continue;
|
||||
txt.x -= 1 * (speed * (elapsed / (1 / 60)));
|
||||
|
||||
if (speed > 0)
|
||||
{
|
||||
if (txt.x < -txt.frameWidth)
|
||||
{
|
||||
txt.x = grpTexts.group.members[grpTexts.length - 1].x + grpTexts.group.members[grpTexts.length - 1].frameWidth + placementOffset;
|
||||
|
||||
sortTextShit();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (txt.x > txt.frameWidth * 2)
|
||||
{
|
||||
txt.x = grpTexts.group.members[0].x - grpTexts.group.members[0].frameWidth - placementOffset;
|
||||
|
||||
sortTextShit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.update(elapsed);
|
||||
|
||||
for (txtPosition in _textPositions)
|
||||
{
|
||||
if (txtPosition == null) continue;
|
||||
txtPosition.x -= 1 * (speed * (elapsed / (1 / 60)));
|
||||
|
||||
if (speed > 0) // Going left
|
||||
{
|
||||
if (txtPosition.x < -frameWidth)
|
||||
{
|
||||
txtPosition.x = _textPositions[_textPositions.length - 1].x + frameWidth + placementOffset;
|
||||
sortTextShit();
|
||||
}
|
||||
}
|
||||
else // Going right
|
||||
{
|
||||
if (txtPosition.x > frameWidth * 2)
|
||||
{
|
||||
txtPosition.x = _textPositions[0].x - frameWidth - placementOffset;
|
||||
sortTextShit();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override public function draw():Void
|
||||
{
|
||||
_positionCache.set(x, y);
|
||||
for (position in _textPositions)
|
||||
{
|
||||
setPosition(_positionCache.x + position.x, _positionCache.y + position.y);
|
||||
super.draw();
|
||||
}
|
||||
setPosition(_positionCache.x, _positionCache.y);
|
||||
}
|
||||
|
||||
function sortTextShit():Void
|
||||
{
|
||||
grpTexts.sort(function(Order:Int, Obj1:FlxObject, Obj2:FlxObject) {
|
||||
return FlxSort.byValues(Order, Obj1.x, Obj2.x);
|
||||
_textPositions.sort(function(Obj1:FlxPoint, Obj2:FlxPoint) {
|
||||
return FlxSort.byValues(FlxSort.ASCENDING, Obj1.x, Obj2.x);
|
||||
});
|
||||
}
|
||||
|
||||
override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
sourceText = FlxDestroyUtil.destroy(sourceText);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2441,7 +2441,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
if (!capsuleAnim) generateSongList(currentFilter, false, true, true);
|
||||
}
|
||||
|
||||
if (intendedCompletion == Math.POSITIVE_INFINITY || intendedCompletion == Math.NEGATIVE_INFINITY || Math.isNaN(intendedCompletion))
|
||||
if (!Math.isFinite(intendedCompletion) || Math.isNaN(intendedCompletion))
|
||||
{
|
||||
intendedCompletion = 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,27 +131,27 @@ class NewCharacterCard extends BackingCard
|
|||
darkBg = new FlxSprite(0, 0).loadGraphic(bitmap);
|
||||
add(darkBg);
|
||||
|
||||
friendFoe.funnyColor = 0xFF139376;
|
||||
friendFoe.color = 0xFF139376;
|
||||
friendFoe.speed = -4;
|
||||
add(friendFoe);
|
||||
|
||||
newUnlock1.funnyColor = 0xFF99BDF2;
|
||||
newUnlock1.color = 0xFF99BDF2;
|
||||
newUnlock1.speed = 2;
|
||||
add(newUnlock1);
|
||||
|
||||
waiting.funnyColor = 0xFF40EA84;
|
||||
waiting.color = 0xFF40EA84;
|
||||
waiting.speed = -2;
|
||||
add(waiting);
|
||||
|
||||
newUnlock2.funnyColor = 0xFF99BDF2;
|
||||
newUnlock2.color = 0xFF99BDF2;
|
||||
newUnlock2.speed = 2;
|
||||
add(newUnlock2);
|
||||
|
||||
friendFoe2.funnyColor = 0xFF139376;
|
||||
friendFoe2.color = 0xFF139376;
|
||||
friendFoe2.speed = -4;
|
||||
add(friendFoe2);
|
||||
|
||||
newUnlock3.funnyColor = 0xFF99BDF2;
|
||||
newUnlock3.color = 0xFF99BDF2;
|
||||
newUnlock3.speed = 2;
|
||||
add(newUnlock3);
|
||||
|
||||
|
|
|
|||
|
|
@ -315,7 +315,7 @@ class StickerTransitionSprite extends openfl.display.Sprite
|
|||
scaleY = 1;
|
||||
|
||||
// Adjusting camera and container cropping to the game resolution
|
||||
__scrollRect.setTo(0, 0, FlxG.scaleMode.gameSize.x, FlxG.scaleMode.gameSize.y);
|
||||
__scrollRect.setTo(0, 0, FlxG.camera._scrollRect.scrollRect.width, FlxG.camera._scrollRect.scrollRect.height);
|
||||
|
||||
stickersCamera.onResize();
|
||||
stickersCamera._scrollRect.scrollRect = scrollRect;
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ class MathUtil
|
|||
if (x <= 0.0) return 0.0;
|
||||
if (x >= 1.0) return 1.0;
|
||||
var result:Float = (x < 0.5) ? (1 - Math.sqrt(1 - 4 * x * x)) / 2 : (Math.sqrt(1 - 4 * (1 - x) * (1 - x)) + 1) / 2;
|
||||
return (result == Math.NaN) ? 1.0 : result;
|
||||
return Math.isNaN(result) ? 1.0 : result;
|
||||
}
|
||||
|
||||
public static function easeInOutBack(x:Float, c:Float = 1.70158):Float
|
||||
|
|
@ -36,7 +36,7 @@ class MathUtil
|
|||
if (x <= 0.0) return 0.0;
|
||||
if (x >= 1.0) return 1.0;
|
||||
var result:Float = (x < 0.5) ? (2 * x * x * ((c + 1) * 2 * x - c)) / 2 : (1 - 2 * (1 - x) * (1 - x) * ((c + 1) * 2 * (1 - x) - c)) / 2;
|
||||
return (result == Math.NaN) ? 1.0 : result;
|
||||
return Math.isNaN(result) ? 1.0 : result;
|
||||
}
|
||||
|
||||
public static function easeInBack(x:Float, c:Float = 1.70158):Float
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ class MemoryUtil
|
|||
|
||||
final kb:Float = Std.parseFloat(regex.matched(1));
|
||||
|
||||
if (kb != Math.NaN)
|
||||
if (!Math.isNaN(kb))
|
||||
{
|
||||
return kb * 1024.0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ class FNFCUtil
|
|||
}
|
||||
|
||||
// Combine into a Song object that can be played in PlayState.
|
||||
var song = Song.buildRaw(songId, songMetadatas.values(), variationList, songChartDatas, false, false);
|
||||
var song = Song.buildRaw(songId, songMetadatas.values(), Constants.DEFAULT_VARIATION, songChartDatas, false, false);
|
||||
|
||||
return song;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue