mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-07-05 10:16:39 +00:00
Merge branch 'rewrite/master' into bugfix/freeplay-fixins
This commit is contained in:
commit
33644d0c7f
2
.github/actions/setup-haxeshit/action.yml
vendored
2
.github/actions/setup-haxeshit/action.yml
vendored
|
@ -23,8 +23,6 @@ runs:
|
||||||
with:
|
with:
|
||||||
path: .haxelib
|
path: .haxelib
|
||||||
key: ${{ runner.os }}-hmm-${{ hashFiles('**/hmm.json') }}
|
key: ${{ runner.os }}-hmm-${{ hashFiles('**/hmm.json') }}
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-hmm-
|
|
||||||
- if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }}
|
- if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }}
|
||||||
name: hmm install
|
name: hmm install
|
||||||
run: |
|
run: |
|
||||||
|
|
9
.github/workflows/build-shit.yml
vendored
9
.github/workflows/build-shit.yml
vendored
|
@ -53,9 +53,8 @@ jobs:
|
||||||
token: ${{ secrets.GH_RO_PAT }}
|
token: ${{ secrets.GH_RO_PAT }}
|
||||||
- uses: ./.github/actions/setup-haxeshit
|
- uses: ./.github/actions/setup-haxeshit
|
||||||
- name: Make HXCPP cache dir
|
- name: Make HXCPP cache dir
|
||||||
shell: bash
|
|
||||||
run: |
|
run: |
|
||||||
mkdir -p ${{ runner.temp }}\\hxcpp_cache
|
mkdir -p ${{ runner.temp }}\hxcpp_cache
|
||||||
- name: Restore build cache
|
- name: Restore build cache
|
||||||
id: cache-build-win
|
id: cache-build-win
|
||||||
uses: actions/cache@v3
|
uses: actions/cache@v3
|
||||||
|
@ -63,10 +62,8 @@ jobs:
|
||||||
path: |
|
path: |
|
||||||
.haxelib
|
.haxelib
|
||||||
export
|
export
|
||||||
${{ runner.temp }}\\hxcpp_cache
|
${{ runner.temp }}\hxcpp_cache
|
||||||
key: ${{ runner.os }}-build-win-${{ github.ref_name }}
|
key: ${{ runner.os }}-build-win-${{ github.ref_name }}-${{ hashFiles('**/hmm.json') }}
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-build-win-
|
|
||||||
- name: Build game
|
- name: Build game
|
||||||
run: |
|
run: |
|
||||||
haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER
|
haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER
|
||||||
|
|
1
.gitmodules
vendored
1
.gitmodules
vendored
|
@ -1,6 +1,7 @@
|
||||||
[submodule "assets"]
|
[submodule "assets"]
|
||||||
path = assets
|
path = assets
|
||||||
url = https://github.com/FunkinCrew/Funkin-history-rewrite-assets
|
url = https://github.com/FunkinCrew/Funkin-history-rewrite-assets
|
||||||
|
branch = master
|
||||||
[submodule "art"]
|
[submodule "art"]
|
||||||
path = art
|
path = art
|
||||||
url = https://github.com/FunkinCrew/Funkin-history-rewrite-art
|
url = https://github.com/FunkinCrew/Funkin-history-rewrite-art
|
||||||
|
|
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
|
@ -23,6 +23,12 @@
|
||||||
"name": "Haxe Eval",
|
"name": "Haxe Eval",
|
||||||
"type": "haxe-eval",
|
"type": "haxe-eval",
|
||||||
"request": "launch"
|
"request": "launch"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Attaches the debugger to an already running game
|
||||||
|
"name": "HXCPP - Attach",
|
||||||
|
"type": "hxcpp",
|
||||||
|
"request": "attach"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 8104d43e584a1f25e574438d7b21a7e671358969
|
Subproject commit ef79a6cf1ae3dcbd86a5b798f8117a6c692c0156
|
4
hmm.json
4
hmm.json
|
@ -104,7 +104,7 @@
|
||||||
"name": "lime",
|
"name": "lime",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "f195121ebec688b417e38ab115185c8d93c349d3",
|
"ref": "737b86f121cdc90358d59e2e527934f267c94a2c",
|
||||||
"url": "https://github.com/EliteMasterEric/lime"
|
"url": "https://github.com/EliteMasterEric/lime"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -139,7 +139,7 @@
|
||||||
"name": "openfl",
|
"name": "openfl",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "de9395d2f367a80f93f082e1b639b9cde2258bf1",
|
"ref": "f229d76361c7e31025a048fe7909847f75bb5d5e",
|
||||||
"url": "https://github.com/EliteMasterEric/openfl"
|
"url": "https://github.com/EliteMasterEric/openfl"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -85,6 +85,13 @@ class Main extends Sprite
|
||||||
|
|
||||||
initHaxeUI();
|
initHaxeUI();
|
||||||
|
|
||||||
|
fpsCounter = new FPS(10, 3, 0xFFFFFF);
|
||||||
|
// addChild(fpsCounter); // Handled by Preferences.init
|
||||||
|
#if !html5
|
||||||
|
memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF);
|
||||||
|
// addChild(memoryCounter);
|
||||||
|
#end
|
||||||
|
|
||||||
// George recommends binding the save before FlxGame is created.
|
// George recommends binding the save before FlxGame is created.
|
||||||
Save.load();
|
Save.load();
|
||||||
|
|
||||||
|
@ -93,15 +100,6 @@ class Main extends Sprite
|
||||||
#if hxcpp_debug_server
|
#if hxcpp_debug_server
|
||||||
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
|
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
|
||||||
#end
|
#end
|
||||||
|
|
||||||
#if debug
|
|
||||||
fpsCounter = new FPS(10, 3, 0xFFFFFF);
|
|
||||||
addChild(fpsCounter);
|
|
||||||
#if !html5
|
|
||||||
memoryCounter = new MemoryCounter(10, 13, 0xFFFFFF);
|
|
||||||
addChild(memoryCounter);
|
|
||||||
#end
|
|
||||||
#end
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function initHaxeUI():Void
|
function initHaxeUI():Void
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
|
import flixel.input.gamepad.FlxGamepad;
|
||||||
import flixel.util.FlxDirectionFlags;
|
import flixel.util.FlxDirectionFlags;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.input.FlxInput;
|
import flixel.input.FlxInput;
|
||||||
|
@ -832,6 +834,14 @@ class Controls extends FlxActionSet
|
||||||
fromSaveData(padData, Gamepad(id));
|
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
|
inline function addGamepadLiteral(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
|
||||||
{
|
{
|
||||||
gamepadsAdded.push(id);
|
gamepadsAdded.push(id);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin;
|
package funkin;
|
||||||
|
|
||||||
|
import funkin.play.song.Song;
|
||||||
import flash.text.TextField;
|
import flash.text.TextField;
|
||||||
import flixel.addons.display.FlxGridOverlay;
|
import flixel.addons.display.FlxGridOverlay;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
|
@ -48,10 +49,13 @@ import lime.utils.Assets;
|
||||||
|
|
||||||
class FreeplayState extends MusicBeatSubState
|
class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
var songs:Array<FreeplaySongData> = [];
|
var songs:Array<Null<FreeplaySongData>> = [];
|
||||||
|
|
||||||
|
var diffIdsCurrent:Array<String> = [];
|
||||||
|
var diffIdsTotal:Array<String> = [];
|
||||||
|
|
||||||
var curSelected:Int = 0;
|
var curSelected:Int = 0;
|
||||||
var curDifficulty:Int = 1;
|
var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY;
|
||||||
|
|
||||||
var fp:FreeplayScore;
|
var fp:FreeplayScore;
|
||||||
var txtCompletion:FlxText;
|
var txtCompletion:FlxText;
|
||||||
|
@ -60,7 +64,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var lerpScore:Float = 0;
|
var lerpScore:Float = 0;
|
||||||
var intendedScore:Int = 0;
|
var intendedScore:Int = 0;
|
||||||
|
|
||||||
var grpDifficulties:FlxSpriteGroup;
|
var grpDifficulties:FlxTypedSpriteGroup<DifficultySprite>;
|
||||||
|
|
||||||
var coolColors:Array<Int> = [
|
var coolColors:Array<Int> = [
|
||||||
0xff9271fd,
|
0xff9271fd,
|
||||||
|
@ -85,6 +89,10 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
var stickerSubState:StickerSubState;
|
var stickerSubState:StickerSubState;
|
||||||
|
|
||||||
|
//
|
||||||
|
static var rememberedDifficulty:Null<String> = "normal";
|
||||||
|
static var rememberedSongId:Null<String> = null;
|
||||||
|
|
||||||
public function new(?stickers:StickerSubState = null)
|
public function new(?stickers:StickerSubState = null)
|
||||||
{
|
{
|
||||||
if (stickers != null)
|
if (stickers != null)
|
||||||
|
@ -130,14 +138,23 @@ class FreeplayState extends MusicBeatSubState
|
||||||
songs.push(null);
|
songs.push(null);
|
||||||
|
|
||||||
// programmatically adds the songs via LevelRegistry and SongRegistry
|
// programmatically adds the songs via LevelRegistry and SongRegistry
|
||||||
for (coolWeek in LevelRegistry.instance.listBaseGameLevelIds())
|
for (levelId in LevelRegistry.instance.listBaseGameLevelIds())
|
||||||
{
|
{
|
||||||
for (songId in LevelRegistry.instance.parseEntryData(coolWeek).songs)
|
for (songId in LevelRegistry.instance.parseEntryData(levelId).songs)
|
||||||
{
|
{
|
||||||
var metadata = SongRegistry.instance.parseEntryMetadata(songId);
|
var song:Song = SongRegistry.instance.fetchEntry(songId);
|
||||||
var char = metadata.playData.characters.opponent;
|
var songBaseDifficulty:SongDifficulty = song.getDifficulty(Constants.DEFAULT_DIFFICULTY);
|
||||||
var songName = metadata.songName;
|
|
||||||
addSong(songId, songName, coolWeek, char);
|
var songName = songBaseDifficulty.songName;
|
||||||
|
var songOpponent = songBaseDifficulty.characters.opponent;
|
||||||
|
var songDifficulties = song.listDifficulties();
|
||||||
|
|
||||||
|
songs.push(new FreeplaySongData(songId, songName, levelId, songOpponent, songDifficulties));
|
||||||
|
|
||||||
|
for (difficulty in songDifficulties)
|
||||||
|
{
|
||||||
|
diffIdsTotal.pushUnique(difficulty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +300,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
grpCapsules = new FlxTypedGroup<SongMenuItem>();
|
grpCapsules = new FlxTypedGroup<SongMenuItem>();
|
||||||
add(grpCapsules);
|
add(grpCapsules);
|
||||||
|
|
||||||
grpDifficulties = new FlxSpriteGroup(-300, 80);
|
grpDifficulties = new FlxTypedSpriteGroup<DifficultySprite>(-300, 80);
|
||||||
add(grpDifficulties);
|
add(grpDifficulties);
|
||||||
|
|
||||||
exitMovers.set([grpDifficulties],
|
exitMovers.set([grpDifficulties],
|
||||||
|
@ -293,15 +310,22 @@ class FreeplayState extends MusicBeatSubState
|
||||||
wait: 0
|
wait: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
grpDifficulties.add(new FlxSprite().loadGraphic(Paths.image('freeplay/freeplayEasy')));
|
for (diffId in diffIdsTotal)
|
||||||
grpDifficulties.add(new FlxSprite().loadGraphic(Paths.image('freeplay/freeplayNorm')));
|
{
|
||||||
grpDifficulties.add(new FlxSprite().loadGraphic(Paths.image('freeplay/freeplayHard')));
|
var diffSprite:DifficultySprite = new DifficultySprite(diffId);
|
||||||
|
diffSprite.difficultyId = diffId;
|
||||||
|
grpDifficulties.add(diffSprite);
|
||||||
|
}
|
||||||
|
|
||||||
grpDifficulties.group.forEach(function(spr) {
|
grpDifficulties.group.forEach(function(spr) {
|
||||||
spr.visible = false;
|
spr.visible = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
grpDifficulties.group.members[curDifficulty].visible = true;
|
for (diffSprite in grpDifficulties.group.members)
|
||||||
|
{
|
||||||
|
if (diffSprite == null) continue;
|
||||||
|
if (diffSprite.difficultyId == currentDifficulty) diffSprite.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
var albumArt:FlxAtlasSprite = new FlxAtlasSprite(640, 360, Paths.animateAtlas("freeplay/albumRoll"));
|
var albumArt:FlxAtlasSprite = new FlxAtlasSprite(640, 360, Paths.animateAtlas("freeplay/albumRoll"));
|
||||||
albumArt.visible = false;
|
albumArt.visible = false;
|
||||||
|
@ -574,15 +598,12 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
FlxG.console.registerFunction("changeSelection", changeSelection);
|
FlxG.console.registerFunction("changeSelection", changeSelection);
|
||||||
|
|
||||||
|
rememberSelection();
|
||||||
|
|
||||||
changeSelection();
|
changeSelection();
|
||||||
changeDiff();
|
changeDiff();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addSong(songId:String, songName:String, levelId:String, songCharacter:String)
|
|
||||||
{
|
|
||||||
songs.push(new FreeplaySongData(songId, songName, levelId, songCharacter));
|
|
||||||
}
|
|
||||||
|
|
||||||
var touchY:Float = 0;
|
var touchY:Float = 0;
|
||||||
var touchX:Float = 0;
|
var touchX:Float = 0;
|
||||||
var dxTouch:Float = 0;
|
var dxTouch:Float = 0;
|
||||||
|
@ -861,28 +882,24 @@ class FreeplayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
touchTimer = 0;
|
touchTimer = 0;
|
||||||
|
|
||||||
curDifficulty += change;
|
var currentDifficultyIndex = diffIdsCurrent.indexOf(currentDifficulty);
|
||||||
|
|
||||||
if (curDifficulty < 0) curDifficulty = 2;
|
if (currentDifficultyIndex == -1) currentDifficultyIndex = diffIdsCurrent.indexOf(Constants.DEFAULT_DIFFICULTY);
|
||||||
if (curDifficulty > 2) curDifficulty = 0;
|
|
||||||
|
|
||||||
var targetDifficulty:String = switch (curDifficulty)
|
currentDifficultyIndex += change;
|
||||||
{
|
|
||||||
case 0:
|
if (currentDifficultyIndex < 0) currentDifficultyIndex = diffIdsCurrent.length - 1;
|
||||||
'easy';
|
if (currentDifficultyIndex >= diffIdsCurrent.length) currentDifficultyIndex = 0;
|
||||||
case 1:
|
|
||||||
'normal';
|
currentDifficulty = diffIdsCurrent[currentDifficultyIndex];
|
||||||
case 2:
|
|
||||||
'hard';
|
|
||||||
default: 'normal';
|
|
||||||
};
|
|
||||||
|
|
||||||
var daSong = songs[curSelected];
|
var daSong = songs[curSelected];
|
||||||
if (daSong != null)
|
if (daSong != null)
|
||||||
{
|
{
|
||||||
var songScore:SaveScoreData = Save.get().getSongScore(songs[curSelected].songId, targetDifficulty);
|
var songScore:SaveScoreData = Save.get().getSongScore(songs[curSelected].songId, currentDifficulty);
|
||||||
intendedScore = songScore?.score ?? 0;
|
intendedScore = songScore?.score ?? 0;
|
||||||
intendedCompletion = songScore?.accuracy ?? 0.0;
|
intendedCompletion = songScore?.accuracy ?? 0.0;
|
||||||
|
rememberedDifficulty = currentDifficulty;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -890,19 +907,31 @@ class FreeplayState extends MusicBeatSubState
|
||||||
intendedCompletion = 0.0;
|
intendedCompletion = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
grpDifficulties.group.forEach(function(spr) {
|
grpDifficulties.group.forEach(function(diffSprite) {
|
||||||
spr.visible = false;
|
diffSprite.visible = false;
|
||||||
});
|
});
|
||||||
|
|
||||||
var curShit:FlxSprite = grpDifficulties.group.members[curDifficulty];
|
for (diffSprite in grpDifficulties.group.members)
|
||||||
|
{
|
||||||
curShit.visible = true;
|
if (diffSprite == null) continue;
|
||||||
curShit.offset.y += 5;
|
if (diffSprite.difficultyId == currentDifficulty)
|
||||||
curShit.alpha = 0.5;
|
{
|
||||||
new FlxTimer().start(1 / 24, function(swag) {
|
if (change != 0)
|
||||||
curShit.alpha = 1;
|
{
|
||||||
curShit.updateHitbox();
|
diffSprite.visible = true;
|
||||||
});
|
diffSprite.offset.y += 5;
|
||||||
|
diffSprite.alpha = 0.5;
|
||||||
|
new FlxTimer().start(1 / 24, function(swag) {
|
||||||
|
diffSprite.alpha = 1;
|
||||||
|
diffSprite.updateHitbox();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
diffSprite.visible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String)
|
// Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String)
|
||||||
|
@ -950,35 +979,9 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
PlayStatePlaylist.isStoryMode = false;
|
PlayStatePlaylist.isStoryMode = false;
|
||||||
|
|
||||||
if (cap.songData == null)
|
var songId:String = cap.songTitle.toLowerCase();
|
||||||
{
|
var targetSong:Song = SongRegistry.instance.fetchEntry(songId);
|
||||||
trace('[WARN] Failure while trying to load song!');
|
var targetDifficulty:String = currentDifficulty;
|
||||||
busy = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetSong:Null<Song> = SongRegistry.instance.fetchEntry(cap.songData.songId);
|
|
||||||
if (targetSong == null)
|
|
||||||
{
|
|
||||||
trace('[WARN] Could not retrieve song ${targetSong}.');
|
|
||||||
}
|
|
||||||
|
|
||||||
var targetDifficulty:String = switch (curDifficulty)
|
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
'easy';
|
|
||||||
case 1:
|
|
||||||
'normal';
|
|
||||||
case 2:
|
|
||||||
'hard';
|
|
||||||
default: 'normal';
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Implement additional difficulties into the interface properly.
|
|
||||||
if (FlxG.keys.pressed.E)
|
|
||||||
{
|
|
||||||
targetDifficulty = 'erect';
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Implement Pico into the interface properly.
|
// TODO: Implement Pico into the interface properly.
|
||||||
var targetCharacter:String = 'bf';
|
var targetCharacter:String = 'bf';
|
||||||
|
@ -1008,6 +1011,22 @@ class FreeplayState extends MusicBeatSubState
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rememberSelection():Void
|
||||||
|
{
|
||||||
|
if (rememberedSongId != null)
|
||||||
|
{
|
||||||
|
curSelected = songs.findIndex(function(song) {
|
||||||
|
if (song == null) return false;
|
||||||
|
return song.songId == rememberedSongId;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rememberedDifficulty != null)
|
||||||
|
{
|
||||||
|
currentDifficulty = rememberedDifficulty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function changeSelection(change:Int = 0)
|
function changeSelection(change:Int = 0)
|
||||||
{
|
{
|
||||||
// NGio.logEvent('Fresh');
|
// NGio.logEvent('Fresh');
|
||||||
|
@ -1038,11 +1057,16 @@ class FreeplayState extends MusicBeatSubState
|
||||||
var songScore:SaveScoreData = Save.get().getSongScore(daSongCapsule.songData.songId, targetDifficulty);
|
var songScore:SaveScoreData = Save.get().getSongScore(daSongCapsule.songData.songId, targetDifficulty);
|
||||||
intendedScore = songScore?.score ?? 0;
|
intendedScore = songScore?.score ?? 0;
|
||||||
intendedCompletion = songScore?.accuracy ?? 0.0;
|
intendedCompletion = songScore?.accuracy ?? 0.0;
|
||||||
|
diffIdsCurrent = daSong.songDifficulties;
|
||||||
|
rememberedSongId = daSong.songId;
|
||||||
|
changeDiff();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
intendedScore = 0;
|
intendedScore = 0;
|
||||||
intendedCompletion = 0.0;
|
intendedCompletion = 0.0;
|
||||||
|
rememberedSongId = null;
|
||||||
|
rememberedDifficulty = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (index => capsule in grpCapsules.members)
|
for (index => capsule in grpCapsules.members)
|
||||||
|
@ -1140,19 +1164,21 @@ enum abstract FilterType(String)
|
||||||
|
|
||||||
class FreeplaySongData
|
class FreeplaySongData
|
||||||
{
|
{
|
||||||
|
public var isFav:Bool = false;
|
||||||
|
|
||||||
public var songId:String = "";
|
public var songId:String = "";
|
||||||
public var songName:String = "";
|
public var songName:String = "";
|
||||||
public var levelId:String = "";
|
public var levelId:String = "";
|
||||||
public var songCharacter:String = "";
|
public var songCharacter:String = "";
|
||||||
public var isFav:Bool = false;
|
public var songDifficulties:Array<String> = [];
|
||||||
|
|
||||||
public function new(songId:String, songName:String, levelId:String, songCharacter:String, isFav:Bool = false)
|
public function new(songId:String, songName:String, levelId:String, songCharacter:String, songDifficulties:Array<String>)
|
||||||
{
|
{
|
||||||
this.songId = songId;
|
this.songId = songId;
|
||||||
this.songName = songName;
|
this.songName = songName;
|
||||||
this.levelId = levelId;
|
this.levelId = levelId;
|
||||||
this.songCharacter = songCharacter;
|
this.songCharacter = songCharacter;
|
||||||
this.isFav = isFav;
|
this.songDifficulties = songDifficulties;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1163,3 +1189,17 @@ typedef MoveData =
|
||||||
var ?speed:Float;
|
var ?speed:Float;
|
||||||
var ?wait:Float;
|
var ?wait:Float;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DifficultySprite extends FlxSprite
|
||||||
|
{
|
||||||
|
public var difficultyId:String;
|
||||||
|
|
||||||
|
public function new(diffId:String)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
difficultyId = diffId;
|
||||||
|
|
||||||
|
loadGraphic(Paths.image('freeplay/freeplay' + diffId));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ class InitState extends FlxState
|
||||||
|
|
||||||
// loadSaveData(); // Moved to Main.hx
|
// loadSaveData(); // Moved to Main.hx
|
||||||
// Load player options from save data.
|
// Load player options from save data.
|
||||||
PreferencesMenu.initPrefs();
|
Preferences.init();
|
||||||
// Load controls from save data.
|
// Load controls from save data.
|
||||||
PlayerSettings.init();
|
PlayerSettings.init();
|
||||||
|
|
||||||
|
|
|
@ -13,23 +13,13 @@ import flixel.input.touch.FlxTouch;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.util.FlxColor;
|
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
import funkin.NGio;
|
|
||||||
import funkin.modding.events.ScriptEvent.UpdateScriptEvent;
|
|
||||||
import funkin.modding.module.ModuleHandler;
|
|
||||||
import funkin.shaderslmfao.ScreenWipeShader;
|
|
||||||
import funkin.ui.AtlasMenuList;
|
import funkin.ui.AtlasMenuList;
|
||||||
import funkin.ui.MenuList.MenuItem;
|
|
||||||
import funkin.ui.MenuList;
|
import funkin.ui.MenuList;
|
||||||
import funkin.ui.title.TitleState;
|
import funkin.ui.title.TitleState;
|
||||||
import funkin.ui.story.StoryMenuState;
|
import funkin.ui.story.StoryMenuState;
|
||||||
import funkin.ui.OptionsState;
|
|
||||||
import funkin.ui.PreferencesMenu;
|
|
||||||
import funkin.ui.Prompt;
|
import funkin.ui.Prompt;
|
||||||
import funkin.util.WindowUtil;
|
import funkin.util.WindowUtil;
|
||||||
import lime.app.Application;
|
|
||||||
import openfl.filters.ShaderFilter;
|
|
||||||
#if discord_rpc
|
#if discord_rpc
|
||||||
import Discord.DiscordClient;
|
import Discord.DiscordClient;
|
||||||
#end
|
#end
|
||||||
|
@ -82,8 +72,10 @@ class MainMenuState extends MusicBeatState
|
||||||
magenta.y = bg.y;
|
magenta.y = bg.y;
|
||||||
magenta.visible = false;
|
magenta.visible = false;
|
||||||
magenta.color = 0xFFfd719b;
|
magenta.color = 0xFFfd719b;
|
||||||
if (PreferencesMenu.preferences.get('flashing-menu')) add(magenta);
|
|
||||||
// magenta.scrollFactor.set();
|
// TODO: Why doesn't this line compile I'm going fucking feral
|
||||||
|
|
||||||
|
if (Preferences.flashingLights) add(magenta);
|
||||||
|
|
||||||
menuItems = new MenuTypedList<AtlasMenuItem>();
|
menuItems = new MenuTypedList<AtlasMenuItem>();
|
||||||
add(menuItems);
|
add(menuItems);
|
||||||
|
@ -116,7 +108,7 @@ class MainMenuState extends MusicBeatState
|
||||||
#end
|
#end
|
||||||
|
|
||||||
createMenuItem('options', 'mainmenu/options', function() {
|
createMenuItem('options', 'mainmenu/options', function() {
|
||||||
startExitState(new OptionsState());
|
startExitState(new funkin.ui.OptionsState());
|
||||||
});
|
});
|
||||||
|
|
||||||
// Reset position of menu items.
|
// Reset position of menu items.
|
||||||
|
|
|
@ -16,17 +16,18 @@ class PauseSubState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
var grpMenuShit:FlxTypedGroup<Alphabet>;
|
var grpMenuShit:FlxTypedGroup<Alphabet>;
|
||||||
|
|
||||||
var pauseOptionsBase:Array<String> = [
|
final pauseOptionsBase:Array<String> = [
|
||||||
'Resume',
|
'Resume',
|
||||||
'Restart Song',
|
'Restart Song',
|
||||||
'Change Difficulty',
|
'Change Difficulty',
|
||||||
'Toggle Practice Mode',
|
'Toggle Practice Mode',
|
||||||
'Exit to Menu'
|
'Exit to Menu'
|
||||||
];
|
];
|
||||||
|
final pauseOptionsCharting:Array<String> = ['Resume', 'Restart Song', 'Exit to Chart Editor'];
|
||||||
|
|
||||||
var pauseOptionsDifficulty:Array<String> = ['EASY', 'NORMAL', 'HARD', 'ERECT', 'BACK'];
|
final pauseOptionsDifficultyBase:Array<String> = ['BACK'];
|
||||||
|
|
||||||
var pauseOptionsCharting:Array<String> = ['Resume', 'Restart Song', 'Exit to Chart Editor'];
|
var pauseOptionsDifficulty:Array<String> = []; // AUTO-POPULATED
|
||||||
|
|
||||||
var menuItems:Array<String> = [];
|
var menuItems:Array<String> = [];
|
||||||
var curSelected:Int = 0;
|
var curSelected:Int = 0;
|
||||||
|
@ -48,6 +49,12 @@ class PauseSubState extends MusicBeatSubState
|
||||||
this.isChartingMode = isChartingMode;
|
this.isChartingMode = isChartingMode;
|
||||||
|
|
||||||
menuItems = this.isChartingMode ? pauseOptionsCharting : pauseOptionsBase;
|
menuItems = this.isChartingMode ? pauseOptionsCharting : pauseOptionsBase;
|
||||||
|
var difficultiesInVariation = PlayState.instance.currentSong.listDifficulties(PlayState.instance.currentChart.variation);
|
||||||
|
trace('DIFFICULTIES: ${difficultiesInVariation}');
|
||||||
|
|
||||||
|
pauseOptionsDifficulty = difficultiesInVariation.map(function(item:String):String {
|
||||||
|
return item.toUpperCase();
|
||||||
|
}).concat(pauseOptionsDifficultyBase);
|
||||||
|
|
||||||
if (PlayStatePlaylist.campaignId == 'week6')
|
if (PlayStatePlaylist.campaignId == 'week6')
|
||||||
{
|
{
|
||||||
|
@ -201,18 +208,6 @@ class PauseSubState extends MusicBeatSubState
|
||||||
menuItems = pauseOptionsDifficulty;
|
menuItems = pauseOptionsDifficulty;
|
||||||
regenMenu();
|
regenMenu();
|
||||||
|
|
||||||
case 'EASY' | 'NORMAL' | 'HARD' | 'ERECT':
|
|
||||||
PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase());
|
|
||||||
|
|
||||||
PlayState.instance.currentDifficulty = daSelected.toLowerCase();
|
|
||||||
|
|
||||||
PlayState.instance.needsReset = true;
|
|
||||||
|
|
||||||
close();
|
|
||||||
case 'BACK':
|
|
||||||
menuItems = this.isChartingMode ? pauseOptionsCharting : pauseOptionsBase;
|
|
||||||
regenMenu();
|
|
||||||
|
|
||||||
case 'Toggle Practice Mode':
|
case 'Toggle Practice Mode':
|
||||||
PlayState.instance.isPracticeMode = true;
|
PlayState.instance.isPracticeMode = true;
|
||||||
practiceText.visible = PlayState.instance.isPracticeMode;
|
practiceText.visible = PlayState.instance.isPracticeMode;
|
||||||
|
@ -245,8 +240,32 @@ class PauseSubState extends MusicBeatSubState
|
||||||
|
|
||||||
case 'Exit to Chart Editor':
|
case 'Exit to Chart Editor':
|
||||||
this.close();
|
this.close();
|
||||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
if (FlxG.sound.music != null) FlxG.sound.music.pause(); // Don't reset song position!
|
||||||
PlayState.instance.close(); // This only works because PlayState is a substate!
|
PlayState.instance.close(); // This only works because PlayState is a substate!
|
||||||
|
|
||||||
|
case 'BACK':
|
||||||
|
menuItems = this.isChartingMode ? pauseOptionsCharting : pauseOptionsBase;
|
||||||
|
regenMenu();
|
||||||
|
|
||||||
|
default:
|
||||||
|
if (pauseOptionsDifficulty.contains(daSelected))
|
||||||
|
{
|
||||||
|
PlayState.instance.currentSong = SongRegistry.instance.fetchEntry(PlayState.instance.currentSong.id.toLowerCase());
|
||||||
|
|
||||||
|
// Reset campaign score when changing difficulty
|
||||||
|
// So if you switch difficulty on the last song of a week you get a really low overall score.
|
||||||
|
PlayStatePlaylist.campaignScore = 0;
|
||||||
|
PlayStatePlaylist.campaignDifficulty = daSelected.toLowerCase();
|
||||||
|
PlayState.instance.currentDifficulty = PlayStatePlaylist.campaignDifficulty;
|
||||||
|
|
||||||
|
PlayState.instance.needsReset = true;
|
||||||
|
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('[WARN] Unhandled pause menu option: ${daSelected}');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,11 @@ class PlayerSettings
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.controls = new Controls('player$id', None);
|
this.controls = new Controls('player$id', None);
|
||||||
|
|
||||||
|
addKeyboard();
|
||||||
|
}
|
||||||
|
|
||||||
|
function addKeyboard():Void
|
||||||
|
{
|
||||||
var useDefault = true;
|
var useDefault = true;
|
||||||
if (Save.get().hasControls(id, Keys))
|
if (Save.get().hasControls(id, Keys))
|
||||||
{
|
{
|
||||||
|
@ -96,7 +101,6 @@ class PlayerSettings
|
||||||
controls.setKeyboardScheme(Solo);
|
controls.setKeyboardScheme(Solo);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply loaded settings.
|
|
||||||
PreciseInputManager.instance.initializeKeys(controls);
|
PreciseInputManager.instance.initializeKeys(controls);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,6 +128,7 @@ class PlayerSettings
|
||||||
trace("Loading gamepad control scheme");
|
trace("Loading gamepad control scheme");
|
||||||
controls.addDefaultGamepad(gamepad.id);
|
controls.addDefaultGamepad(gamepad.id);
|
||||||
}
|
}
|
||||||
|
PreciseInputManager.instance.initializeButtons(controls, gamepad);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
138
source/funkin/Preferences.hx
Normal file
138
source/funkin/Preferences.hx
Normal file
|
@ -0,0 +1,138 @@
|
||||||
|
package funkin;
|
||||||
|
|
||||||
|
import funkin.save.Save;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store of user-configurable, globally relevant values.
|
||||||
|
*/
|
||||||
|
class Preferences
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Whether some particularly fowl language is displayed.
|
||||||
|
* @default `true`
|
||||||
|
*/
|
||||||
|
public static var naughtyness(get, set):Bool;
|
||||||
|
|
||||||
|
static function get_naughtyness():Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.naughtyness;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function set_naughtyness(value:Bool):Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.naughtyness = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If enabled, the strumline is at the bottom of the screen rather than the top.
|
||||||
|
* @default `false`
|
||||||
|
*/
|
||||||
|
public static var downscroll(get, set):Bool;
|
||||||
|
|
||||||
|
static function get_downscroll():Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.downscroll;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function set_downscroll(value:Bool):Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.downscroll = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If disabled, flashing lights in the main menu and other areas will be less intense.
|
||||||
|
* @default `true`
|
||||||
|
*/
|
||||||
|
public static var flashingLights(get, set):Bool;
|
||||||
|
|
||||||
|
static function get_flashingLights():Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.flashingLights;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function set_flashingLights(value:Bool):Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.flashingLights = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If disabled, the camera bump synchronized to the beat.
|
||||||
|
* @default `false`
|
||||||
|
*/
|
||||||
|
public static var zoomCamera(get, set):Bool;
|
||||||
|
|
||||||
|
static function get_zoomCamera():Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.zoomCamera;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function set_zoomCamera(value:Bool):Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.zoomCamera = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If enabled, an FPS and memory counter will be displayed even if this is not a debug build.
|
||||||
|
* @default `false`
|
||||||
|
*/
|
||||||
|
public static var debugDisplay(get, set):Bool;
|
||||||
|
|
||||||
|
static function get_debugDisplay():Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.debugDisplay;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function set_debugDisplay(value:Bool):Bool
|
||||||
|
{
|
||||||
|
if (value != Save.get().options.debugDisplay)
|
||||||
|
{
|
||||||
|
toggleDebugDisplay(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Save.get().options.debugDisplay = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If enabled, the game will automatically pause when tabbing out.
|
||||||
|
* @default `true`
|
||||||
|
*/
|
||||||
|
public static var autoPause(get, set):Bool;
|
||||||
|
|
||||||
|
static function get_autoPause():Bool
|
||||||
|
{
|
||||||
|
return Save.get().options.autoPause;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function set_autoPause(value:Bool):Bool
|
||||||
|
{
|
||||||
|
if (value != Save.get().options.autoPause) FlxG.autoPause = value;
|
||||||
|
|
||||||
|
return Save.get().options.autoPause = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function init():Void
|
||||||
|
{
|
||||||
|
FlxG.autoPause = Preferences.autoPause;
|
||||||
|
toggleDebugDisplay(Preferences.debugDisplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function toggleDebugDisplay(show:Bool):Void
|
||||||
|
{
|
||||||
|
if (show)
|
||||||
|
{
|
||||||
|
// Enable the debug display.
|
||||||
|
FlxG.stage.addChild(Main.fpsCounter);
|
||||||
|
#if !html5
|
||||||
|
FlxG.stage.addChild(Main.memoryCounter);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Disable the debug display.
|
||||||
|
FlxG.stage.removeChild(Main.fpsCounter);
|
||||||
|
#if !html5
|
||||||
|
FlxG.stage.removeChild(Main.memoryCounter);
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,7 +7,6 @@ import flixel.graphics.frames.FlxAtlasFrames;
|
||||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import flixel.sound.FlxSound;
|
import flixel.sound.FlxSound;
|
||||||
import funkin.ui.PreferencesMenu.CheckboxThingie;
|
|
||||||
|
|
||||||
using Lambda;
|
using Lambda;
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
package funkin.data;
|
package funkin.data;
|
||||||
|
|
||||||
import funkin.data.song.importer.FNFLegacyData.LegacyNote;
|
import funkin.data.song.importer.FNFLegacyData.LegacyNote;
|
||||||
import hxjsonast.Json;
|
|
||||||
import hxjsonast.Tools;
|
|
||||||
import hxjsonast.Json.JObjectField;
|
|
||||||
import haxe.ds.Either;
|
|
||||||
import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection;
|
|
||||||
import funkin.data.song.importer.FNFLegacyData.LegacyNoteData;
|
import funkin.data.song.importer.FNFLegacyData.LegacyNoteData;
|
||||||
|
import funkin.data.song.importer.FNFLegacyData.LegacyNoteSection;
|
||||||
import funkin.data.song.importer.FNFLegacyData.LegacyScrollSpeeds;
|
import funkin.data.song.importer.FNFLegacyData.LegacyScrollSpeeds;
|
||||||
|
import haxe.ds.Either;
|
||||||
|
import hxjsonast.Json;
|
||||||
|
import hxjsonast.Json.JObjectField;
|
||||||
|
import hxjsonast.Tools;
|
||||||
|
import thx.semver.Version;
|
||||||
|
import thx.semver.VersionRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `json2object` has an annotation `@:jcustomparse` which allows for mutation of parsed values.
|
* `json2object` has an annotation `@:jcustomparse` which allows for mutation of parsed values.
|
||||||
|
@ -23,7 +25,8 @@ class DataParse
|
||||||
* `@:jcustomparse(funkin.data.DataParse.stringNotEmpty)`
|
* `@:jcustomparse(funkin.data.DataParse.stringNotEmpty)`
|
||||||
* @param json Contains the `pos` and `value` of the property.
|
* @param json Contains the `pos` and `value` of the property.
|
||||||
* @param name The name of the property.
|
* @param name The name of the property.
|
||||||
* @throws If the property is not a string or is empty.
|
* @throws Error If the property is not a string or is empty.
|
||||||
|
* @return The string value.
|
||||||
*/
|
*/
|
||||||
public static function stringNotEmpty(json:Json, name:String):String
|
public static function stringNotEmpty(json:Json, name:String):String
|
||||||
{
|
{
|
||||||
|
@ -37,6 +40,42 @@ class DataParse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `@:jcustomparse(funkin.data.DataParse.semverVersion)`
|
||||||
|
* @param json Contains the `pos` and `value` of the property.
|
||||||
|
* @param name The name of the property.
|
||||||
|
* @return The value of the property as a `thx.semver.Version`.
|
||||||
|
*/
|
||||||
|
public static function semverVersion(json:Json, name:String):Version
|
||||||
|
{
|
||||||
|
switch (json.value)
|
||||||
|
{
|
||||||
|
case JString(s):
|
||||||
|
if (s == "") throw 'Expected version property $name to be non-empty.';
|
||||||
|
return s;
|
||||||
|
default:
|
||||||
|
throw 'Expected version property $name to be a string, but it was ${json.value}.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `@:jcustomparse(funkin.data.DataParse.semverVersionRule)`
|
||||||
|
* @param json Contains the `pos` and `value` of the property.
|
||||||
|
* @param name The name of the property.
|
||||||
|
* @return The value of the property as a `thx.semver.VersionRule`.
|
||||||
|
*/
|
||||||
|
public static function semverVersionRule(json:Json, name:String):VersionRule
|
||||||
|
{
|
||||||
|
switch (json.value)
|
||||||
|
{
|
||||||
|
case JString(s):
|
||||||
|
if (s == "") throw 'Expected version rule property $name to be non-empty.';
|
||||||
|
return s;
|
||||||
|
default:
|
||||||
|
throw 'Expected version rule property $name to be a string, but it was ${json.value}.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parser which outputs a Dynamic value, either a object or something else.
|
* Parser which outputs a Dynamic value, either a object or something else.
|
||||||
* @param json
|
* @param json
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package funkin.data;
|
package funkin.data;
|
||||||
|
|
||||||
import funkin.util.SerializerUtil;
|
import funkin.util.SerializerUtil;
|
||||||
|
import thx.semver.Version;
|
||||||
|
import thx.semver.VersionRule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `json2object` has an annotation `@:jcustomwrite` which allows for custom serialization of values to be written to JSON.
|
* `json2object` has an annotation `@:jcustomwrite` which allows for custom serialization of values to be written to JSON.
|
||||||
|
@ -9,9 +11,30 @@ import funkin.util.SerializerUtil;
|
||||||
*/
|
*/
|
||||||
class DataWrite
|
class DataWrite
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* `@:jcustomwrite(funkin.data.DataWrite.dynamicValue)`
|
||||||
|
* @param value
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
public static function dynamicValue(value:Dynamic):String
|
public static function dynamicValue(value:Dynamic):String
|
||||||
{
|
{
|
||||||
// Is this cheating? Yes. Do I care? No.
|
// Is this cheating? Yes. Do I care? No.
|
||||||
return SerializerUtil.toJSON(value);
|
return SerializerUtil.toJSON(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `@:jcustomwrite(funkin.data.DataWrite.semverVersion)`
|
||||||
|
*/
|
||||||
|
public static function semverVersion(value:Version):String
|
||||||
|
{
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `@:jcustomwrite(funkin.data.DataWrite.semverVersionRule)`
|
||||||
|
*/
|
||||||
|
public static function semverVersionRule(value:VersionRule):String
|
||||||
|
{
|
||||||
|
return value.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,8 @@ class SongMetadata
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||||
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
|
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||||
public var version:Version;
|
public var version:Version;
|
||||||
|
|
||||||
@:default("Unknown")
|
@:default("Unknown")
|
||||||
|
@ -203,6 +205,8 @@ class SongMusicData
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||||
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
|
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||||
public var version:Version;
|
public var version:Version;
|
||||||
|
|
||||||
@:default("Unknown")
|
@:default("Unknown")
|
||||||
|
@ -367,6 +371,8 @@ class SongCharacterData
|
||||||
class SongChartData
|
class SongChartData
|
||||||
{
|
{
|
||||||
@:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION)
|
@:default(funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION)
|
||||||
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
|
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||||
public var version:Version;
|
public var version:Version;
|
||||||
|
|
||||||
public var scrollSpeed:Map<String, Float>;
|
public var scrollSpeed:Map<String, Float>;
|
||||||
|
|
|
@ -246,7 +246,8 @@ class SongDataUtils
|
||||||
|
|
||||||
typedef SongClipboardItems =
|
typedef SongClipboardItems =
|
||||||
{
|
{
|
||||||
?valid:Bool,
|
@:optional
|
||||||
notes:Array<SongNoteData>,
|
var valid:Bool;
|
||||||
events:Array<SongEventData>
|
var notes:Array<SongNoteData>;
|
||||||
|
var events:Array<SongEventData>;
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ class SongMetadata_v2_0_0
|
||||||
// ==========
|
// ==========
|
||||||
// UNMODIFIED VALUES
|
// UNMODIFIED VALUES
|
||||||
// ==========
|
// ==========
|
||||||
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
|
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||||
public var version:Version;
|
public var version:Version;
|
||||||
|
|
||||||
@:default("Unknown")
|
@:default("Unknown")
|
||||||
|
|
|
@ -4,6 +4,7 @@ package;
|
||||||
// Only import these when we aren't in a macro.
|
// Only import these when we aren't in a macro.
|
||||||
import funkin.util.Constants;
|
import funkin.util.Constants;
|
||||||
import funkin.Paths;
|
import funkin.Paths;
|
||||||
|
import funkin.Preferences;
|
||||||
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.
|
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.
|
||||||
|
|
||||||
// These are great.
|
// These are great.
|
||||||
|
|
|
@ -1,18 +1,25 @@
|
||||||
package funkin.input;
|
package funkin.input;
|
||||||
|
|
||||||
import openfl.ui.Keyboard;
|
|
||||||
import funkin.play.notes.NoteDirection;
|
|
||||||
import flixel.input.keyboard.FlxKeyboard.FlxKeyInput;
|
|
||||||
import openfl.events.KeyboardEvent;
|
|
||||||
import flixel.FlxG;
|
import flixel.FlxG;
|
||||||
|
import flixel.input.FlxInput;
|
||||||
import flixel.input.FlxInput.FlxInputState;
|
import flixel.input.FlxInput.FlxInputState;
|
||||||
import flixel.input.FlxKeyManager;
|
import flixel.input.FlxKeyManager;
|
||||||
|
import flixel.input.gamepad.FlxGamepad;
|
||||||
|
import flixel.input.gamepad.FlxGamepadInputID;
|
||||||
import flixel.input.keyboard.FlxKey;
|
import flixel.input.keyboard.FlxKey;
|
||||||
|
import flixel.input.keyboard.FlxKeyboard.FlxKeyInput;
|
||||||
import flixel.input.keyboard.FlxKeyList;
|
import flixel.input.keyboard.FlxKeyList;
|
||||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||||
|
import funkin.play.notes.NoteDirection;
|
||||||
|
import funkin.util.FlxGamepadUtil;
|
||||||
import haxe.Int64;
|
import haxe.Int64;
|
||||||
|
import lime.ui.Gamepad as LimeGamepad;
|
||||||
|
import lime.ui.GamepadAxis as LimeGamepadAxis;
|
||||||
|
import lime.ui.GamepadButton as LimeGamepadButton;
|
||||||
import lime.ui.KeyCode;
|
import lime.ui.KeyCode;
|
||||||
import lime.ui.KeyModifier;
|
import lime.ui.KeyModifier;
|
||||||
|
import openfl.events.KeyboardEvent;
|
||||||
|
import openfl.ui.Keyboard;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A precise input manager that:
|
* A precise input manager that:
|
||||||
|
@ -43,6 +50,20 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
*/
|
*/
|
||||||
var _keyListDir:Map<FlxKey, NoteDirection>;
|
var _keyListDir:Map<FlxKey, NoteDirection>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A FlxGamepadID->Array<FlxGamepadInputID>, with FlxGamepadInputID being the counterpart to FlxKey.
|
||||||
|
*/
|
||||||
|
var _buttonList:Map<Int, Array<FlxGamepadInputID>>;
|
||||||
|
|
||||||
|
var _buttonListArray:Array<FlxInput<FlxGamepadInputID>>;
|
||||||
|
|
||||||
|
var _buttonListMap:Map<Int, Map<FlxGamepadInputID, FlxInput<FlxGamepadInputID>>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A FlxGamepadID->FlxGamepadInputID->NoteDirection, with FlxGamepadInputID being the counterpart to FlxKey.
|
||||||
|
*/
|
||||||
|
var _buttonListDir:Map<Int, Map<FlxGamepadInputID, NoteDirection>>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The timestamp at which a given note direction was last pressed.
|
* The timestamp at which a given note direction was last pressed.
|
||||||
*/
|
*/
|
||||||
|
@ -53,15 +74,32 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
*/
|
*/
|
||||||
var _dirReleaseTimestamps:Map<NoteDirection, Int64>;
|
var _dirReleaseTimestamps:Map<NoteDirection, Int64>;
|
||||||
|
|
||||||
|
var _deviceBinds:Map<FlxGamepad,
|
||||||
|
{
|
||||||
|
onButtonDown:LimeGamepadButton->Int64->Void,
|
||||||
|
onButtonUp:LimeGamepadButton->Int64->Void
|
||||||
|
}>;
|
||||||
|
|
||||||
public function new()
|
public function new()
|
||||||
{
|
{
|
||||||
super(PreciseInputList.new);
|
super(PreciseInputList.new);
|
||||||
|
|
||||||
|
_deviceBinds = [];
|
||||||
|
|
||||||
_keyList = [];
|
_keyList = [];
|
||||||
_dirPressTimestamps = new Map<NoteDirection, Int64>();
|
// _keyListMap
|
||||||
_dirReleaseTimestamps = new Map<NoteDirection, Int64>();
|
// _keyListArray
|
||||||
_keyListDir = new Map<FlxKey, NoteDirection>();
|
_keyListDir = new Map<FlxKey, NoteDirection>();
|
||||||
|
|
||||||
|
_buttonList = [];
|
||||||
|
_buttonListMap = [];
|
||||||
|
_buttonListArray = [];
|
||||||
|
_buttonListDir = new Map<Int, Map<FlxGamepadInputID, NoteDirection>>();
|
||||||
|
|
||||||
|
_dirPressTimestamps = new Map<NoteDirection, Int64>();
|
||||||
|
_dirReleaseTimestamps = new Map<NoteDirection, Int64>();
|
||||||
|
|
||||||
|
// Keyboard
|
||||||
FlxG.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
|
FlxG.stage.removeEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);
|
||||||
FlxG.stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp);
|
FlxG.stage.removeEventListener(KeyboardEvent.KEY_UP, onKeyUp);
|
||||||
FlxG.stage.application.window.onKeyDownPrecise.add(handleKeyDown);
|
FlxG.stage.application.window.onKeyDownPrecise.add(handleKeyDown);
|
||||||
|
@ -84,6 +122,17 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function getButtonsForDirection(controls:Controls, noteDirection:NoteDirection)
|
||||||
|
{
|
||||||
|
return switch (noteDirection)
|
||||||
|
{
|
||||||
|
case NoteDirection.LEFT: controls.getButtonsForAction(NOTE_LEFT);
|
||||||
|
case NoteDirection.DOWN: controls.getButtonsForAction(NOTE_DOWN);
|
||||||
|
case NoteDirection.UP: controls.getButtonsForAction(NOTE_UP);
|
||||||
|
case NoteDirection.RIGHT: controls.getButtonsForAction(NOTE_RIGHT);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert from int to Int64.
|
* Convert from int to Int64.
|
||||||
*/
|
*/
|
||||||
|
@ -138,6 +187,43 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function initializeButtons(controls:Controls, gamepad:FlxGamepad):Void
|
||||||
|
{
|
||||||
|
clearButtons();
|
||||||
|
|
||||||
|
var limeGamepad = FlxGamepadUtil.getLimeGamepad(gamepad);
|
||||||
|
var callbacks =
|
||||||
|
{
|
||||||
|
onButtonDown: handleButtonDown.bind(gamepad),
|
||||||
|
onButtonUp: handleButtonUp.bind(gamepad)
|
||||||
|
};
|
||||||
|
limeGamepad.onButtonDownPrecise.add(callbacks.onButtonDown);
|
||||||
|
limeGamepad.onButtonUpPrecise.add(callbacks.onButtonUp);
|
||||||
|
|
||||||
|
for (noteDirection in DIRECTIONS)
|
||||||
|
{
|
||||||
|
var buttons = getButtonsForDirection(controls, noteDirection);
|
||||||
|
for (button in buttons)
|
||||||
|
{
|
||||||
|
var input = new FlxInput<FlxGamepadInputID>(button);
|
||||||
|
|
||||||
|
var buttonListEntry = _buttonList.get(gamepad.id);
|
||||||
|
if (buttonListEntry == null) _buttonList.set(gamepad.id, buttonListEntry = []);
|
||||||
|
buttonListEntry.push(button);
|
||||||
|
|
||||||
|
_buttonListArray.push(input);
|
||||||
|
|
||||||
|
var buttonListMapEntry = _buttonListMap.get(gamepad.id);
|
||||||
|
if (buttonListMapEntry == null) _buttonListMap.set(gamepad.id, buttonListMapEntry = new Map<FlxGamepadInputID, FlxInput<FlxGamepadInputID>>());
|
||||||
|
buttonListMapEntry.set(button, input);
|
||||||
|
|
||||||
|
var buttonListDirEntry = _buttonListDir.get(gamepad.id);
|
||||||
|
if (buttonListDirEntry == null) _buttonListDir.set(gamepad.id, buttonListDirEntry = new Map<FlxGamepadInputID, NoteDirection>());
|
||||||
|
buttonListDirEntry.set(button, noteDirection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the time, in nanoseconds, since the given note direction was last pressed.
|
* Get the time, in nanoseconds, since the given note direction was last pressed.
|
||||||
* @param noteDirection The note direction to check.
|
* @param noteDirection The note direction to check.
|
||||||
|
@ -165,11 +251,41 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
return _keyListMap.get(key);
|
return _keyListMap.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getInputByButton(gamepad:FlxGamepad, button:FlxGamepadInputID):FlxInput<FlxGamepadInputID>
|
||||||
|
{
|
||||||
|
return _buttonListMap.get(gamepad.id).get(button);
|
||||||
|
}
|
||||||
|
|
||||||
public function getDirectionForKey(key:FlxKey):NoteDirection
|
public function getDirectionForKey(key:FlxKey):NoteDirection
|
||||||
{
|
{
|
||||||
return _keyListDir.get(key);
|
return _keyListDir.get(key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDirectionForButton(gamepad:FlxGamepad, button:FlxGamepadInputID):NoteDirection
|
||||||
|
{
|
||||||
|
return _buttonListDir.get(gamepad.id).get(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getButton(gamepad:FlxGamepad, button:FlxGamepadInputID):FlxInput<FlxGamepadInputID>
|
||||||
|
{
|
||||||
|
return _buttonListMap.get(gamepad.id).get(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateButtonStates(gamepad:FlxGamepad, button:FlxGamepadInputID, down:Bool):Void
|
||||||
|
{
|
||||||
|
var input = getButton(gamepad, button);
|
||||||
|
if (input == null) return;
|
||||||
|
|
||||||
|
if (down)
|
||||||
|
{
|
||||||
|
input.press();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
input.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleKeyDown(keyCode:KeyCode, _:KeyModifier, timestamp:Int64):Void
|
function handleKeyDown(keyCode:KeyCode, _:KeyModifier, timestamp:Int64):Void
|
||||||
{
|
{
|
||||||
var key:FlxKey = convertKeyCode(keyCode);
|
var key:FlxKey = convertKeyCode(keyCode);
|
||||||
|
@ -198,7 +314,7 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
if (_keyList.indexOf(key) == -1) return;
|
if (_keyList.indexOf(key) == -1) return;
|
||||||
|
|
||||||
// TODO: Remove this line with SDL3 when timestamps change meaning.
|
// TODO: Remove this line with SDL3 when timestamps change meaning.
|
||||||
// This is because SDL3's timestamps are measured in nanoseconds, not milliseconds.
|
// This is because SDL3's timestamps ar e measured in nanoseconds, not milliseconds.
|
||||||
timestamp *= Constants.NS_PER_MS;
|
timestamp *= Constants.NS_PER_MS;
|
||||||
|
|
||||||
updateKeyStates(key, false);
|
updateKeyStates(key, false);
|
||||||
|
@ -214,6 +330,54 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleButtonDown(gamepad:FlxGamepad, button:LimeGamepadButton, timestamp:Int64):Void
|
||||||
|
{
|
||||||
|
var buttonId:FlxGamepadInputID = FlxGamepadUtil.getInputID(gamepad, button);
|
||||||
|
|
||||||
|
var buttonListEntry = _buttonList.get(gamepad.id);
|
||||||
|
if (buttonListEntry == null || buttonListEntry.indexOf(buttonId) == -1) return;
|
||||||
|
|
||||||
|
// TODO: Remove this line with SDL3 when timestamps change meaning.
|
||||||
|
// This is because SDL3's timestamps ar e measured in nanoseconds, not milliseconds.
|
||||||
|
timestamp *= Constants.NS_PER_MS;
|
||||||
|
|
||||||
|
updateButtonStates(gamepad, buttonId, true);
|
||||||
|
|
||||||
|
if (getInputByButton(gamepad, buttonId)?.justPressed ?? false)
|
||||||
|
{
|
||||||
|
onInputPressed.dispatch(
|
||||||
|
{
|
||||||
|
noteDirection: getDirectionForButton(gamepad, buttonId),
|
||||||
|
timestamp: timestamp
|
||||||
|
});
|
||||||
|
_dirPressTimestamps.set(getDirectionForButton(gamepad, buttonId), timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleButtonUp(gamepad:FlxGamepad, button:LimeGamepadButton, timestamp:Int64):Void
|
||||||
|
{
|
||||||
|
var buttonId:FlxGamepadInputID = FlxGamepadUtil.getInputID(gamepad, button);
|
||||||
|
|
||||||
|
var buttonListEntry = _buttonList.get(gamepad.id);
|
||||||
|
if (buttonListEntry == null || buttonListEntry.indexOf(buttonId) == -1) return;
|
||||||
|
|
||||||
|
// TODO: Remove this line with SDL3 when timestamps change meaning.
|
||||||
|
// This is because SDL3's timestamps ar e measured in nanoseconds, not milliseconds.
|
||||||
|
timestamp *= Constants.NS_PER_MS;
|
||||||
|
|
||||||
|
updateButtonStates(gamepad, buttonId, false);
|
||||||
|
|
||||||
|
if (getInputByButton(gamepad, buttonId)?.justReleased ?? false)
|
||||||
|
{
|
||||||
|
onInputReleased.dispatch(
|
||||||
|
{
|
||||||
|
noteDirection: getDirectionForButton(gamepad, buttonId),
|
||||||
|
timestamp: timestamp
|
||||||
|
});
|
||||||
|
_dirReleaseTimestamps.set(getDirectionForButton(gamepad, buttonId), timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static function convertKeyCode(input:KeyCode):FlxKey
|
static function convertKeyCode(input:KeyCode):FlxKey
|
||||||
{
|
{
|
||||||
@:privateAccess
|
@:privateAccess
|
||||||
|
@ -228,6 +392,31 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
||||||
_keyListMap.clear();
|
_keyListMap.clear();
|
||||||
_keyListDir.clear();
|
_keyListDir.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clearButtons():Void
|
||||||
|
{
|
||||||
|
_buttonListArray = [];
|
||||||
|
_buttonListDir.clear();
|
||||||
|
|
||||||
|
for (gamepad in _deviceBinds.keys())
|
||||||
|
{
|
||||||
|
var callbacks = _deviceBinds.get(gamepad);
|
||||||
|
var limeGamepad = FlxGamepadUtil.getLimeGamepad(gamepad);
|
||||||
|
limeGamepad.onButtonDownPrecise.remove(callbacks.onButtonDown);
|
||||||
|
limeGamepad.onButtonUpPrecise.remove(callbacks.onButtonUp);
|
||||||
|
}
|
||||||
|
_deviceBinds.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function destroy():Void
|
||||||
|
{
|
||||||
|
// Keyboard
|
||||||
|
FlxG.stage.application.window.onKeyDownPrecise.remove(handleKeyDown);
|
||||||
|
FlxG.stage.application.window.onKeyUpPrecise.remove(handleKeyUp);
|
||||||
|
|
||||||
|
clearKeys();
|
||||||
|
clearButtons();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreciseInputList extends FlxKeyList
|
class PreciseInputList extends FlxKeyList
|
||||||
|
|
|
@ -11,7 +11,6 @@ import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
import funkin.play.character.BaseCharacter;
|
import funkin.play.character.BaseCharacter;
|
||||||
import funkin.ui.PreferencesMenu;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A substate which renders over the PlayState when the player dies.
|
* A substate which renders over the PlayState when the player dies.
|
||||||
|
@ -103,6 +102,9 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
|
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
|
||||||
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
|
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
|
||||||
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
||||||
|
var offsets:Array<Float> = boyfriend.getDeathCameraOffsets();
|
||||||
|
cameraFollowPoint.x += offsets[0];
|
||||||
|
cameraFollowPoint.y += offsets[1];
|
||||||
add(cameraFollowPoint);
|
add(cameraFollowPoint);
|
||||||
|
|
||||||
FlxG.camera.target = null;
|
FlxG.camera.target = null;
|
||||||
|
@ -292,7 +294,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
var randomCensor:Array<Int> = [];
|
var randomCensor:Array<Int> = [];
|
||||||
|
|
||||||
if (PreferencesMenu.getPref('censor-naughty')) randomCensor = [1, 3, 8, 13, 17, 21];
|
if (!Preferences.naughtyness) randomCensor = [1, 3, 8, 13, 17, 21];
|
||||||
|
|
||||||
FlxG.sound.play(Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)), 1, false, null, true, function() {
|
FlxG.sound.play(Paths.sound('jeffGameover/jeffGameover-' + FlxG.random.int(1, 25, randomCensor)), 1, false, null, true, function() {
|
||||||
// Once the quote ends, fade in the game over music.
|
// Once the quote ends, fade in the game over music.
|
||||||
|
|
|
@ -24,13 +24,14 @@ import openfl.utils.Assets;
|
||||||
* - i.e. `PlayState.instance.iconP1.animation.addByPrefix("jumpscare", "jumpscare", 24, false);`
|
* - i.e. `PlayState.instance.iconP1.animation.addByPrefix("jumpscare", "jumpscare", 24, false);`
|
||||||
* @author MasterEric
|
* @author MasterEric
|
||||||
*/
|
*/
|
||||||
|
@:nullSafety
|
||||||
class HealthIcon extends FlxSprite
|
class HealthIcon extends FlxSprite
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The character this icon is representing.
|
* The character this icon is representing.
|
||||||
* Setting this variable will automatically update the graphic.
|
* Setting this variable will automatically update the graphic.
|
||||||
*/
|
*/
|
||||||
public var characterId(default, set):String;
|
public var characterId(default, set):Null<String>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether this health icon should automatically update its state based on the character's health.
|
* Whether this health icon should automatically update its state based on the character's health.
|
||||||
|
@ -123,13 +124,12 @@ class HealthIcon extends FlxSprite
|
||||||
initTargetSize();
|
initTargetSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_characterId(value:String):String
|
function set_characterId(value:Null<String>):Null<String>
|
||||||
{
|
{
|
||||||
if (value == characterId) return value;
|
if (value == characterId) return value;
|
||||||
|
|
||||||
characterId = value;
|
characterId = value ?? Constants.DEFAULT_HEALTH_ICON;
|
||||||
loadCharacter(characterId);
|
return characterId;
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_isPixel(value:Bool):Bool
|
function set_isPixel(value:Bool):Bool
|
||||||
|
@ -137,8 +137,7 @@ class HealthIcon extends FlxSprite
|
||||||
if (value == isPixel) return value;
|
if (value == isPixel) return value;
|
||||||
|
|
||||||
isPixel = value;
|
isPixel = value;
|
||||||
loadCharacter(characterId);
|
return isPixel;
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -156,6 +155,38 @@ class HealthIcon extends FlxSprite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Use the provided CharacterHealthIconData to configure this health icon's appearance.
|
||||||
|
* @param data The data to use to configure this health icon.
|
||||||
|
*/
|
||||||
|
public function configure(data:Null<HealthIconData>):Void
|
||||||
|
{
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
this.characterId = Constants.DEFAULT_HEALTH_ICON;
|
||||||
|
this.isPixel = false;
|
||||||
|
|
||||||
|
loadCharacter(characterId);
|
||||||
|
|
||||||
|
this.size.set(1.0, 1.0);
|
||||||
|
this.offset.x = 0.0;
|
||||||
|
this.offset.y = 0.0;
|
||||||
|
this.flipX = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.characterId = data.id;
|
||||||
|
this.isPixel = data.isPixel ?? false;
|
||||||
|
|
||||||
|
loadCharacter(characterId);
|
||||||
|
|
||||||
|
this.size.set(data.scale ?? 1.0, data.scale ?? 1.0);
|
||||||
|
this.offset.x = (data.offsets != null) ? data.offsets[0] : 0.0;
|
||||||
|
this.offset.y = (data.offsets != null) ? data.offsets[1] : 0.0;
|
||||||
|
this.flipX = data.flipX ?? false; // Face the OTHER way by default, since that is more common.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called by Flixel every frame. Includes logic to manage the currently playing animation.
|
* Called by Flixel every frame. Includes logic to manage the currently playing animation.
|
||||||
*/
|
*/
|
||||||
|
@ -341,12 +372,17 @@ class HealthIcon extends FlxSprite
|
||||||
this.animation.add(Losing, [1], 0, false, false);
|
this.animation.add(Losing, [1], 0, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function correctCharacterId(charId:String):String
|
function correctCharacterId(charId:Null<String>):String
|
||||||
{
|
{
|
||||||
|
if (charId == null)
|
||||||
|
{
|
||||||
|
return Constants.DEFAULT_HEALTH_ICON;
|
||||||
|
}
|
||||||
|
|
||||||
if (!Assets.exists(Paths.image('icons/icon-$charId')))
|
if (!Assets.exists(Paths.image('icons/icon-$charId')))
|
||||||
{
|
{
|
||||||
FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
|
FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
|
||||||
return 'face';
|
return Constants.DEFAULT_HEALTH_ICON;
|
||||||
}
|
}
|
||||||
|
|
||||||
return charId;
|
return charId;
|
||||||
|
@ -357,10 +393,11 @@ class HealthIcon extends FlxSprite
|
||||||
return Assets.exists(Paths.file('images/icons/icon-$characterId.xml'));
|
return Assets.exists(Paths.file('images/icons/icon-$characterId.xml'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadCharacter(charId:String):Void
|
function loadCharacter(charId:Null<String>):Void
|
||||||
{
|
{
|
||||||
if (correctCharacterId(charId) != charId)
|
if (charId == null || correctCharacterId(charId) != charId)
|
||||||
{
|
{
|
||||||
|
// This will recursively trigger loadCharacter to be called again.
|
||||||
characterId = correctCharacterId(charId);
|
characterId = correctCharacterId(charId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -698,7 +698,15 @@ class PlayState extends MusicBeatSubState
|
||||||
FlxG.sound.music.pause();
|
FlxG.sound.music.pause();
|
||||||
FlxG.sound.music.time = (startTimestamp);
|
FlxG.sound.music.time = (startTimestamp);
|
||||||
|
|
||||||
vocals = currentChart.buildVocals();
|
if (!overrideMusic)
|
||||||
|
{
|
||||||
|
vocals = currentChart.buildVocals();
|
||||||
|
|
||||||
|
if (vocals.members.length == 0)
|
||||||
|
{
|
||||||
|
trace('WARNING: No vocals found for this song.');
|
||||||
|
}
|
||||||
|
}
|
||||||
vocals.pause();
|
vocals.pause();
|
||||||
vocals.time = 0;
|
vocals.time = 0;
|
||||||
|
|
||||||
|
@ -920,7 +928,6 @@ class PlayState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle keybinds.
|
// Handle keybinds.
|
||||||
// if (!isInCutscene && !disableKeys) keyShit(true);
|
|
||||||
processInputQueue();
|
processInputQueue();
|
||||||
if (!isInCutscene && !disableKeys) debugKeyShit();
|
if (!isInCutscene && !disableKeys) debugKeyShit();
|
||||||
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
||||||
|
@ -1268,7 +1275,7 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
function initHealthBar():Void
|
function initHealthBar():Void
|
||||||
{
|
{
|
||||||
var healthBarYPos:Float = PreferencesMenu.getPref('downscroll') ? FlxG.height * 0.1 : FlxG.height * 0.9;
|
var healthBarYPos:Float = Preferences.downscroll ? FlxG.height * 0.1 : FlxG.height * 0.9;
|
||||||
healthBarBG = new FlxSprite(0, healthBarYPos).loadGraphic(Paths.image('healthBar'));
|
healthBarBG = new FlxSprite(0, healthBarYPos).loadGraphic(Paths.image('healthBar'));
|
||||||
healthBarBG.screenCenter(X);
|
healthBarBG.screenCenter(X);
|
||||||
healthBarBG.scrollFactor.set(0, 0);
|
healthBarBG.scrollFactor.set(0, 0);
|
||||||
|
@ -1477,13 +1484,13 @@ class PlayState extends MusicBeatSubState
|
||||||
// Position the player strumline on the right half of the screen
|
// Position the player strumline on the right half of the screen
|
||||||
playerStrumline.x = FlxG.width / 2 + Constants.STRUMLINE_X_OFFSET; // Classic style
|
playerStrumline.x = FlxG.width / 2 + Constants.STRUMLINE_X_OFFSET; // Classic style
|
||||||
// playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET; // Centered style
|
// playerStrumline.x = FlxG.width - playerStrumline.width - Constants.STRUMLINE_X_OFFSET; // Centered style
|
||||||
playerStrumline.y = PreferencesMenu.getPref('downscroll') ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
playerStrumline.y = Preferences.downscroll ? FlxG.height - playerStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
||||||
playerStrumline.zIndex = 200;
|
playerStrumline.zIndex = 200;
|
||||||
playerStrumline.cameras = [camHUD];
|
playerStrumline.cameras = [camHUD];
|
||||||
|
|
||||||
// Position the opponent strumline on the left half of the screen
|
// Position the opponent strumline on the left half of the screen
|
||||||
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET;
|
opponentStrumline.x = Constants.STRUMLINE_X_OFFSET;
|
||||||
opponentStrumline.y = PreferencesMenu.getPref('downscroll') ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
opponentStrumline.y = Preferences.downscroll ? FlxG.height - opponentStrumline.height - Constants.STRUMLINE_Y_OFFSET : Constants.STRUMLINE_Y_OFFSET;
|
||||||
opponentStrumline.zIndex = 100;
|
opponentStrumline.zIndex = 100;
|
||||||
opponentStrumline.cameras = [camHUD];
|
opponentStrumline.cameras = [camHUD];
|
||||||
|
|
||||||
|
@ -1642,7 +1649,7 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
function onConversationComplete():Void
|
function onConversationComplete():Void
|
||||||
{
|
{
|
||||||
isInCutscene = true;
|
isInCutscene = false;
|
||||||
remove(currentConversation);
|
remove(currentConversation);
|
||||||
currentConversation = null;
|
currentConversation = null;
|
||||||
|
|
||||||
|
@ -2464,9 +2471,9 @@ class PlayState extends MusicBeatSubState
|
||||||
accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes,
|
accuracy: Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (Save.get().isLevelHighScore(PlayStatePlaylist.campaignId, currentDifficulty, data))
|
if (Save.get().isLevelHighScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data))
|
||||||
{
|
{
|
||||||
Save.get().setLevelScore(PlayStatePlaylist.campaignId, currentDifficulty, data);
|
Save.get().setLevelScore(PlayStatePlaylist.campaignId, PlayStatePlaylist.campaignDifficulty, data);
|
||||||
#if newgrounds
|
#if newgrounds
|
||||||
NGio.postScore(score, 'Level ${PlayStatePlaylist.campaignId}');
|
NGio.postScore(score, 'Level ${PlayStatePlaylist.campaignId}');
|
||||||
#end
|
#end
|
||||||
|
@ -2514,7 +2521,7 @@ class PlayState extends MusicBeatSubState
|
||||||
var nextPlayState:PlayState = new PlayState(
|
var nextPlayState:PlayState = new PlayState(
|
||||||
{
|
{
|
||||||
targetSong: targetSong,
|
targetSong: targetSong,
|
||||||
targetDifficulty: currentDifficulty,
|
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||||
targetCharacter: currentPlayerId,
|
targetCharacter: currentPlayerId,
|
||||||
});
|
});
|
||||||
nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y);
|
nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y);
|
||||||
|
@ -2530,7 +2537,7 @@ class PlayState extends MusicBeatSubState
|
||||||
var nextPlayState:PlayState = new PlayState(
|
var nextPlayState:PlayState = new PlayState(
|
||||||
{
|
{
|
||||||
targetSong: targetSong,
|
targetSong: targetSong,
|
||||||
targetDifficulty: currentDifficulty,
|
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||||
targetCharacter: currentPlayerId,
|
targetCharacter: currentPlayerId,
|
||||||
});
|
});
|
||||||
nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y);
|
nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y);
|
||||||
|
@ -2656,7 +2663,12 @@ class PlayState extends MusicBeatSubState
|
||||||
persistentUpdate = false;
|
persistentUpdate = false;
|
||||||
vocals.stop();
|
vocals.stop();
|
||||||
camHUD.alpha = 1;
|
camHUD.alpha = 1;
|
||||||
var res:ResultState = new ResultState();
|
var res:ResultState = new ResultState(
|
||||||
|
{
|
||||||
|
storyMode: PlayStatePlaylist.isStoryMode,
|
||||||
|
title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'),
|
||||||
|
tallies: Highscore.tallies,
|
||||||
|
});
|
||||||
res.camera = camHUD;
|
res.camera = camHUD;
|
||||||
openSubState(res);
|
openSubState(res);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,10 +34,7 @@ class PlayStatePlaylist
|
||||||
*/
|
*/
|
||||||
public static var campaignId:String = 'unknown';
|
public static var campaignId:String = 'unknown';
|
||||||
|
|
||||||
/**
|
public static var campaignDifficulty:String = Constants.DEFAULT_DIFFICULTY;
|
||||||
* The current difficulty selected for this level (as a named ID).
|
|
||||||
*/
|
|
||||||
public static var currentDifficulty(default, default):String = Constants.DEFAULT_DIFFICULTY;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resets the playlist to its default state.
|
* Resets the playlist to its default state.
|
||||||
|
@ -49,6 +46,6 @@ class PlayStatePlaylist
|
||||||
campaignScore = 0;
|
campaignScore = 0;
|
||||||
campaignTitle = 'UNKNOWN';
|
campaignTitle = 'UNKNOWN';
|
||||||
campaignId = 'unknown';
|
campaignId = 'unknown';
|
||||||
currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
campaignDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,8 @@ import flxanimate.FlxAnimate.Settings;
|
||||||
|
|
||||||
class ResultState extends MusicBeatSubState
|
class ResultState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
|
final params:ResultsStateParams;
|
||||||
|
|
||||||
var resultsVariation:ResultVariations;
|
var resultsVariation:ResultVariations;
|
||||||
var songName:FlxBitmapText;
|
var songName:FlxBitmapText;
|
||||||
var difficulty:FlxSprite;
|
var difficulty:FlxSprite;
|
||||||
|
@ -29,13 +31,18 @@ class ResultState extends MusicBeatSubState
|
||||||
var maskShaderSongName = new LeftMaskShader();
|
var maskShaderSongName = new LeftMaskShader();
|
||||||
var maskShaderDifficulty = new LeftMaskShader();
|
var maskShaderDifficulty = new LeftMaskShader();
|
||||||
|
|
||||||
|
public function new(params:ResultsStateParams)
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.params = params;
|
||||||
|
}
|
||||||
|
|
||||||
override function create():Void
|
override function create():Void
|
||||||
{
|
{
|
||||||
if (Highscore.tallies.sick == Highscore.tallies.totalNotesHit
|
if (params.tallies.sick == params.tallies.totalNotesHit
|
||||||
&& Highscore.tallies.maxCombo == Highscore.tallies.totalNotesHit) resultsVariation = PERFECT;
|
&& params.tallies.maxCombo == params.tallies.totalNotesHit) resultsVariation = PERFECT;
|
||||||
else if (Highscore.tallies.missed
|
else if (params.tallies.missed + params.tallies.bad + params.tallies.shit >= params.tallies.totalNotes * 0.50)
|
||||||
+ Highscore.tallies.bad
|
|
||||||
+ Highscore.tallies.shit >= Highscore.tallies.totalNotes * 0.50)
|
|
||||||
resultsVariation = SHIT; // if more than half of your song was missed, bad, or shit notes, you get shit ending!
|
resultsVariation = SHIT; // if more than half of your song was missed, bad, or shit notes, you get shit ending!
|
||||||
else
|
else
|
||||||
resultsVariation = NORMAL;
|
resultsVariation = NORMAL;
|
||||||
|
@ -135,17 +142,7 @@ class ResultState extends MusicBeatSubState
|
||||||
|
|
||||||
var fontLetters:String = "AaBbCcDdEeFfGgHhiIJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz:1234567890";
|
var fontLetters:String = "AaBbCcDdEeFfGgHhiIJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz:1234567890";
|
||||||
songName = new FlxBitmapText(FlxBitmapFont.fromMonospace(Paths.image("resultScreen/tardlingSpritesheet"), fontLetters, FlxPoint.get(49, 62)));
|
songName = new FlxBitmapText(FlxBitmapFont.fromMonospace(Paths.image("resultScreen/tardlingSpritesheet"), fontLetters, FlxPoint.get(49, 62)));
|
||||||
|
songName.text = params.title;
|
||||||
// stole this from PauseSubState, I think eric wrote it!!
|
|
||||||
if (PlayState.instance.currentChart != null)
|
|
||||||
{
|
|
||||||
songName.text += '${PlayState.instance.currentChart.songName}:${PlayState.instance.currentChart.songArtist}';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
songName.text += PlayState.instance.currentSong.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
songName.letterSpacing = -15;
|
songName.letterSpacing = -15;
|
||||||
songName.angle = -4.1;
|
songName.angle = -4.1;
|
||||||
add(songName);
|
add(songName);
|
||||||
|
@ -194,27 +191,27 @@ class ResultState extends MusicBeatSubState
|
||||||
var ratingGrp:FlxTypedGroup<TallyCounter> = new FlxTypedGroup<TallyCounter>();
|
var ratingGrp:FlxTypedGroup<TallyCounter> = new FlxTypedGroup<TallyCounter>();
|
||||||
add(ratingGrp);
|
add(ratingGrp);
|
||||||
|
|
||||||
var totalHit:TallyCounter = new TallyCounter(375, hStuf * 3, Highscore.tallies.totalNotesHit);
|
var totalHit:TallyCounter = new TallyCounter(375, hStuf * 3, params.tallies.totalNotesHit);
|
||||||
ratingGrp.add(totalHit);
|
ratingGrp.add(totalHit);
|
||||||
|
|
||||||
var maxCombo:TallyCounter = new TallyCounter(375, hStuf * 4, Highscore.tallies.maxCombo);
|
var maxCombo:TallyCounter = new TallyCounter(375, hStuf * 4, params.tallies.maxCombo);
|
||||||
ratingGrp.add(maxCombo);
|
ratingGrp.add(maxCombo);
|
||||||
|
|
||||||
hStuf += 2;
|
hStuf += 2;
|
||||||
var extraYOffset:Float = 5;
|
var extraYOffset:Float = 5;
|
||||||
var tallySick:TallyCounter = new TallyCounter(230, (hStuf * 5) + extraYOffset, Highscore.tallies.sick, 0xFF89E59E);
|
var tallySick:TallyCounter = new TallyCounter(230, (hStuf * 5) + extraYOffset, params.tallies.sick, 0xFF89E59E);
|
||||||
ratingGrp.add(tallySick);
|
ratingGrp.add(tallySick);
|
||||||
|
|
||||||
var tallyGood:TallyCounter = new TallyCounter(210, (hStuf * 6) + extraYOffset, Highscore.tallies.good, 0xFF89C9E5);
|
var tallyGood:TallyCounter = new TallyCounter(210, (hStuf * 6) + extraYOffset, params.tallies.good, 0xFF89C9E5);
|
||||||
ratingGrp.add(tallyGood);
|
ratingGrp.add(tallyGood);
|
||||||
|
|
||||||
var tallyBad:TallyCounter = new TallyCounter(190, (hStuf * 7) + extraYOffset, Highscore.tallies.bad, 0xffE6CF8A);
|
var tallyBad:TallyCounter = new TallyCounter(190, (hStuf * 7) + extraYOffset, params.tallies.bad, 0xffE6CF8A);
|
||||||
ratingGrp.add(tallyBad);
|
ratingGrp.add(tallyBad);
|
||||||
|
|
||||||
var tallyShit:TallyCounter = new TallyCounter(220, (hStuf * 8) + extraYOffset, Highscore.tallies.shit, 0xFFE68C8A);
|
var tallyShit:TallyCounter = new TallyCounter(220, (hStuf * 8) + extraYOffset, params.tallies.shit, 0xFFE68C8A);
|
||||||
ratingGrp.add(tallyShit);
|
ratingGrp.add(tallyShit);
|
||||||
|
|
||||||
var tallyMissed:TallyCounter = new TallyCounter(260, (hStuf * 9) + extraYOffset, Highscore.tallies.missed, 0xFFC68AE6);
|
var tallyMissed:TallyCounter = new TallyCounter(260, (hStuf * 9) + extraYOffset, params.tallies.missed, 0xFFC68AE6);
|
||||||
ratingGrp.add(tallyMissed);
|
ratingGrp.add(tallyMissed);
|
||||||
|
|
||||||
for (ind => rating in ratingGrp.members)
|
for (ind => rating in ratingGrp.members)
|
||||||
|
@ -275,7 +272,7 @@ class ResultState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Highscore.tallies.isNewHighscore) trace("ITS A NEW HIGHSCORE!!!");
|
if (params.tallies.isNewHighscore) trace("ITS A NEW HIGHSCORE!!!");
|
||||||
|
|
||||||
super.create();
|
super.create();
|
||||||
}
|
}
|
||||||
|
@ -351,7 +348,7 @@ class ResultState extends MusicBeatSubState
|
||||||
|
|
||||||
if (controls.PAUSE)
|
if (controls.PAUSE)
|
||||||
{
|
{
|
||||||
if (PlayStatePlaylist.isStoryMode)
|
if (params.storyMode)
|
||||||
{
|
{
|
||||||
FlxG.switchState(new StoryMenuState());
|
FlxG.switchState(new StoryMenuState());
|
||||||
}
|
}
|
||||||
|
@ -372,3 +369,21 @@ enum abstract ResultVariations(String)
|
||||||
var NORMAL;
|
var NORMAL;
|
||||||
var SHIT;
|
var SHIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef ResultsStateParams =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* True if results are for a level, false if results are for a single song.
|
||||||
|
*/
|
||||||
|
var storyMode:Bool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Either "Song Name by Artist Name" or "Week Name"
|
||||||
|
*/
|
||||||
|
var title:String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The score, accuracy, and judgements.
|
||||||
|
*/
|
||||||
|
var tallies:Highscore.Tallies;
|
||||||
|
};
|
||||||
|
|
|
@ -188,6 +188,11 @@ class BaseCharacter extends Bopper
|
||||||
shouldBop = false;
|
shouldBop = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDeathCameraOffsets():Array<Float>
|
||||||
|
{
|
||||||
|
return _data.death?.cameraOffsets ?? [0.0, 0.0];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the value of flipX from the character data.
|
* Gets the value of flipX from the character data.
|
||||||
* `!getFlipX()` is the direction Boyfriend should face.
|
* `!getFlipX()` is the direction Boyfriend should face.
|
||||||
|
@ -312,12 +317,8 @@ class BaseCharacter extends Bopper
|
||||||
trace('[WARN] Player 1 health icon not found!');
|
trace('[WARN] Player 1 health icon not found!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PlayState.instance.iconP1.isPixel = _data.healthIcon?.isPixel ?? false;
|
PlayState.instance.iconP1.configure(_data.healthIcon);
|
||||||
PlayState.instance.iconP1.characterId = _data.healthIcon.id;
|
PlayState.instance.iconP1.flipX = !PlayState.instance.iconP1.flipX; // BF is looking the other way.
|
||||||
PlayState.instance.iconP1.size.set(_data.healthIcon.scale, _data.healthIcon.scale);
|
|
||||||
PlayState.instance.iconP1.offset.x = _data.healthIcon.offsets[0];
|
|
||||||
PlayState.instance.iconP1.offset.y = _data.healthIcon.offsets[1];
|
|
||||||
PlayState.instance.iconP1.flipX = !_data.healthIcon.flipX;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -326,12 +327,7 @@ class BaseCharacter extends Bopper
|
||||||
trace('[WARN] Player 2 health icon not found!');
|
trace('[WARN] Player 2 health icon not found!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PlayState.instance.iconP2.isPixel = _data.healthIcon?.isPixel ?? false;
|
PlayState.instance.iconP2.configure(_data.healthIcon);
|
||||||
PlayState.instance.iconP2.characterId = _data.healthIcon.id;
|
|
||||||
PlayState.instance.iconP2.size.set(_data.healthIcon.scale, _data.healthIcon.scale);
|
|
||||||
PlayState.instance.iconP2.offset.x = _data.healthIcon.offsets[0];
|
|
||||||
PlayState.instance.iconP2.offset.y = _data.healthIcon.offsets[1];
|
|
||||||
PlayState.instance.iconP2.flipX = _data.healthIcon.flipX;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -580,8 +576,7 @@ class BaseCharacter extends Bopper
|
||||||
|
|
||||||
public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void
|
public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void
|
||||||
{
|
{
|
||||||
FlxG.watch.addQuick('playAnim(${characterName})', name);
|
// FlxG.watch.addQuick('playAnim(${characterName})', name);
|
||||||
// trace('playAnim(${characterName}): ${name}');
|
|
||||||
super.playAnimation(name, restart, ignoreOther, reversed);
|
super.playAnimation(name, restart, ignoreOther, reversed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,10 @@ class CharacterDataParser
|
||||||
* The current version string for the stage data format.
|
* The current version string for the stage data format.
|
||||||
* Handle breaking changes by incrementing this value
|
* Handle breaking changes by incrementing this value
|
||||||
* and adding migration to the `migrateStageData()` function.
|
* and adding migration to the `migrateStageData()` function.
|
||||||
|
*
|
||||||
|
* - Version 1.0.1 adds `death.cameraOffsets`
|
||||||
*/
|
*/
|
||||||
public static final CHARACTER_DATA_VERSION:String = '1.0.0';
|
public static final CHARACTER_DATA_VERSION:String = '1.0.1';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current version rule check for the stage data format.
|
* The current version rule check for the stage data format.
|
||||||
|
@ -603,6 +605,8 @@ typedef CharacterData =
|
||||||
*/
|
*/
|
||||||
var healthIcon:Null<HealthIconData>;
|
var healthIcon:Null<HealthIconData>;
|
||||||
|
|
||||||
|
var death:Null<DeathData>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The global offset to the character's position, in pixels.
|
* The global offset to the character's position, in pixels.
|
||||||
* @default [0, 0]
|
* @default [0, 0]
|
||||||
|
@ -695,3 +699,13 @@ typedef HealthIconData =
|
||||||
*/
|
*/
|
||||||
var offsets:Null<Array<Float>>;
|
var offsets:Null<Array<Float>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef DeathData =
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The amount to offset the camera by while focusing on this character as they die.
|
||||||
|
* Default value focuses on the character's graphic midpoint.
|
||||||
|
* @default [0, 0]
|
||||||
|
*/
|
||||||
|
var ?cameraOffsets:Array<Float>;
|
||||||
|
}
|
||||||
|
|
|
@ -231,7 +231,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
notesVwoosh.add(note);
|
notesVwoosh.add(note);
|
||||||
|
|
||||||
var targetY:Float = FlxG.height + note.y;
|
var targetY:Float = FlxG.height + note.y;
|
||||||
if (PreferencesMenu.getPref('downscroll')) targetY = 0 - note.height;
|
if (Preferences.downscroll) targetY = 0 - note.height;
|
||||||
FlxTween.tween(note, {y: targetY}, 0.5,
|
FlxTween.tween(note, {y: targetY}, 0.5,
|
||||||
{
|
{
|
||||||
ease: FlxEase.expoIn,
|
ease: FlxEase.expoIn,
|
||||||
|
@ -252,7 +252,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
holdNotesVwoosh.add(holdNote);
|
holdNotesVwoosh.add(holdNote);
|
||||||
|
|
||||||
var targetY:Float = FlxG.height + holdNote.y;
|
var targetY:Float = FlxG.height + holdNote.y;
|
||||||
if (PreferencesMenu.getPref('downscroll')) targetY = 0 - holdNote.height;
|
if (Preferences.downscroll) targetY = 0 - holdNote.height;
|
||||||
FlxTween.tween(holdNote, {y: targetY}, 0.5,
|
FlxTween.tween(holdNote, {y: targetY}, 0.5,
|
||||||
{
|
{
|
||||||
ease: FlxEase.expoIn,
|
ease: FlxEase.expoIn,
|
||||||
|
@ -277,7 +277,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0;
|
var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 1.0;
|
||||||
var scrollSpeed:Float = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
|
var scrollSpeed:Float = PlayState.instance?.currentChart?.scrollSpeed ?? 1.0;
|
||||||
|
|
||||||
return Constants.PIXELS_PER_MS * (Conductor.songPosition - strumTime) * scrollSpeed * vwoosh * (PreferencesMenu.getPref('downscroll') ? 1 : -1);
|
return Constants.PIXELS_PER_MS * (Conductor.songPosition - strumTime) * scrollSpeed * vwoosh * (Preferences.downscroll ? 1 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateNotes():Void
|
function updateNotes():Void
|
||||||
|
@ -321,7 +321,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
note.y = this.y - INITIAL_OFFSET + calculateNoteYPos(note.strumTime, vwoosh);
|
note.y = this.y - INITIAL_OFFSET + calculateNoteYPos(note.strumTime, vwoosh);
|
||||||
|
|
||||||
// If the note is miss
|
// If the note is miss
|
||||||
var isOffscreen = PreferencesMenu.getPref('downscroll') ? note.y > FlxG.height : note.y < -note.height;
|
var isOffscreen = Preferences.downscroll ? note.y > FlxG.height : note.y < -note.height;
|
||||||
if (note.handledMiss && isOffscreen)
|
if (note.handledMiss && isOffscreen)
|
||||||
{
|
{
|
||||||
killNote(note);
|
killNote(note);
|
||||||
|
@ -388,7 +388,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
|
|
||||||
var vwoosh:Bool = false;
|
var vwoosh:Bool = false;
|
||||||
|
|
||||||
if (PreferencesMenu.getPref('downscroll'))
|
if (Preferences.downscroll)
|
||||||
{
|
{
|
||||||
holdNote.y = this.y + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
|
holdNote.y = this.y + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
|
||||||
}
|
}
|
||||||
|
@ -410,7 +410,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
holdNote.visible = false;
|
holdNote.visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (PreferencesMenu.getPref('downscroll'))
|
if (Preferences.downscroll)
|
||||||
{
|
{
|
||||||
holdNote.y = this.y - holdNote.height + STRUMLINE_SIZE / 2;
|
holdNote.y = this.y - holdNote.height + STRUMLINE_SIZE / 2;
|
||||||
}
|
}
|
||||||
|
@ -425,7 +425,7 @@ class Strumline extends FlxSpriteGroup
|
||||||
holdNote.visible = true;
|
holdNote.visible = true;
|
||||||
var vwoosh:Bool = false;
|
var vwoosh:Bool = false;
|
||||||
|
|
||||||
if (PreferencesMenu.getPref('downscroll'))
|
if (Preferences.downscroll)
|
||||||
{
|
{
|
||||||
holdNote.y = this.y + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
|
holdNote.y = this.y + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
|
||||||
}
|
}
|
||||||
|
|
|
@ -114,7 +114,7 @@ class SustainTrail extends FlxSprite
|
||||||
height = sustainHeight(sustainLength, getScrollSpeed());
|
height = sustainHeight(sustainLength, getScrollSpeed());
|
||||||
// instead of scrollSpeed, PlayState.SONG.speed
|
// instead of scrollSpeed, PlayState.SONG.speed
|
||||||
|
|
||||||
flipY = PreferencesMenu.getPref('downscroll');
|
flipY = Preferences.downscroll;
|
||||||
|
|
||||||
// alpha = 0.6;
|
// alpha = 0.6;
|
||||||
alpha = 1.0;
|
alpha = 1.0;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin.play.song;
|
package funkin.play.song;
|
||||||
|
|
||||||
|
import funkin.util.SortUtil;
|
||||||
import flixel.sound.FlxSound;
|
import flixel.sound.FlxSound;
|
||||||
import openfl.utils.Assets;
|
import openfl.utils.Assets;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
@ -56,8 +57,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
*/
|
*/
|
||||||
public var validScore:Bool = true;
|
public var validScore:Bool = true;
|
||||||
|
|
||||||
var difficultyIds:Array<String>;
|
|
||||||
|
|
||||||
public var songName(get, never):String;
|
public var songName(get, never):String;
|
||||||
|
|
||||||
function get_songName():String
|
function get_songName():String
|
||||||
|
@ -85,7 +84,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
|
||||||
variations = [];
|
variations = [];
|
||||||
difficultyIds = [];
|
|
||||||
difficulties = new Map<String, SongDifficulty>();
|
difficulties = new Map<String, SongDifficulty>();
|
||||||
|
|
||||||
_data = _fetchData(id);
|
_data = _fetchData(id);
|
||||||
|
@ -127,8 +125,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
for (vari in variations)
|
for (vari in variations)
|
||||||
result.variations.push(vari);
|
result.variations.push(vari);
|
||||||
|
|
||||||
result.difficultyIds.clear();
|
result.difficulties.clear();
|
||||||
|
|
||||||
result.populateDifficulties();
|
result.populateDifficulties();
|
||||||
|
|
||||||
for (variation => chartData in charts)
|
for (variation => chartData in charts)
|
||||||
|
@ -157,13 +154,17 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
{
|
{
|
||||||
if (metadata == null || metadata.playData == null) continue;
|
if (metadata == null || metadata.playData == null) continue;
|
||||||
|
|
||||||
|
// If there are no difficulties in the metadata, there's a problem.
|
||||||
|
if (metadata.playData.difficulties.length == 0)
|
||||||
|
{
|
||||||
|
throw 'Song $id has no difficulties listed in metadata!';
|
||||||
|
}
|
||||||
|
|
||||||
// There may be more difficulties in the chart file than in the metadata,
|
// There may be more difficulties in the chart file than in the metadata,
|
||||||
// (i.e. non-playable charts like the one used for Pico on the speaker in Stress)
|
// (i.e. non-playable charts like the one used for Pico on the speaker in Stress)
|
||||||
// but all the difficulties in the metadata must be in the chart file.
|
// but all the difficulties in the metadata must be in the chart file.
|
||||||
for (diffId in metadata.playData.difficulties)
|
for (diffId in metadata.playData.difficulties)
|
||||||
{
|
{
|
||||||
difficultyIds.push(diffId);
|
|
||||||
|
|
||||||
var difficulty:SongDifficulty = new SongDifficulty(this, diffId, metadata.variation);
|
var difficulty:SongDifficulty = new SongDifficulty(this, diffId, metadata.variation);
|
||||||
|
|
||||||
variations.push(metadata.variation);
|
variations.push(metadata.variation);
|
||||||
|
@ -237,19 +238,37 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
||||||
*/
|
*/
|
||||||
public inline function getDifficulty(?diffId:String):Null<SongDifficulty>
|
public inline function getDifficulty(?diffId:String):Null<SongDifficulty>
|
||||||
{
|
{
|
||||||
if (diffId == null) diffId = difficulties.keys().array()[0];
|
if (diffId == null) diffId = listDifficulties()[0];
|
||||||
|
|
||||||
return difficulties.get(diffId);
|
return difficulties.get(diffId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function listDifficulties():Array<String>
|
/**
|
||||||
|
* List all the difficulties in this song.
|
||||||
|
* @param variationId Optionally filter by variation.
|
||||||
|
* @return The list of difficulties.
|
||||||
|
*/
|
||||||
|
public function listDifficulties(?variationId:String):Array<String>
|
||||||
{
|
{
|
||||||
return difficultyIds;
|
if (variationId == '') variationId = null;
|
||||||
|
|
||||||
|
var diffFiltered:Array<String> = difficulties.keys().array().filter(function(diffId:String):Bool {
|
||||||
|
if (variationId == null) return true;
|
||||||
|
var difficulty:Null<SongDifficulty> = difficulties.get(diffId);
|
||||||
|
if (difficulty == null) return false;
|
||||||
|
return difficulty.variation == variationId;
|
||||||
|
});
|
||||||
|
|
||||||
|
diffFiltered.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST));
|
||||||
|
|
||||||
|
return diffFiltered;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasDifficulty(diffId:String):Bool
|
public function hasDifficulty(diffId:String, ?variationId:String):Bool
|
||||||
{
|
{
|
||||||
return difficulties.exists(diffId);
|
if (variationId == '') variationId = null;
|
||||||
|
var difficulty:Null<SongDifficulty> = difficulties.get(diffId);
|
||||||
|
return variationId == null ? (difficulty != null) : (difficulty != null && difficulty.variation == variationId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -63,10 +63,10 @@ abstract Save(RawSaveData)
|
||||||
// Reasonable defaults.
|
// Reasonable defaults.
|
||||||
naughtyness: true,
|
naughtyness: true,
|
||||||
downscroll: false,
|
downscroll: false,
|
||||||
flashingMenu: true,
|
flashingLights: true,
|
||||||
zoomCamera: true,
|
zoomCamera: true,
|
||||||
debugDisplay: false,
|
debugDisplay: false,
|
||||||
pauseOnTabOut: true,
|
autoPause: true,
|
||||||
|
|
||||||
controls:
|
controls:
|
||||||
{
|
{
|
||||||
|
@ -88,7 +88,7 @@ abstract Save(RawSaveData)
|
||||||
{
|
{
|
||||||
// No mods enabled.
|
// No mods enabled.
|
||||||
enabledMods: [],
|
enabledMods: [],
|
||||||
modSettings: [],
|
modOptions: [],
|
||||||
},
|
},
|
||||||
|
|
||||||
optionsChartEditor:
|
optionsChartEditor:
|
||||||
|
@ -98,6 +98,20 @@ abstract Save(RawSaveData)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public var options(get, never):SaveDataOptions;
|
||||||
|
|
||||||
|
function get_options():SaveDataOptions
|
||||||
|
{
|
||||||
|
return this.options;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var modOptions(get, never):Map<String, Dynamic>;
|
||||||
|
|
||||||
|
function get_modOptions():Map<String, Dynamic>
|
||||||
|
{
|
||||||
|
return this.mods.modOptions;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The current session ID for the logged-in Newgrounds user, or null if the user is cringe.
|
* The current session ID for the logged-in Newgrounds user, or null if the user is cringe.
|
||||||
*/
|
*/
|
||||||
|
@ -407,6 +421,8 @@ typedef RawSaveData =
|
||||||
/**
|
/**
|
||||||
* A semantic versioning string for the save data format.
|
* A semantic versioning string for the save data format.
|
||||||
*/
|
*/
|
||||||
|
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||||
|
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||||
var version:Version;
|
var version:Version;
|
||||||
|
|
||||||
var api:SaveApiData;
|
var api:SaveApiData;
|
||||||
|
@ -458,7 +474,7 @@ typedef SaveHighScoresData =
|
||||||
typedef SaveDataMods =
|
typedef SaveDataMods =
|
||||||
{
|
{
|
||||||
var enabledMods:Array<String>;
|
var enabledMods:Array<String>;
|
||||||
var modSettings:Map<String, Dynamic>;
|
var modOptions:Map<String, Dynamic>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -530,10 +546,10 @@ typedef SaveDataOptions =
|
||||||
var downscroll:Bool;
|
var downscroll:Bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If disabled, the main menu won't flash when entering a submenu.
|
* If disabled, flashing lights in the main menu and other areas will be less intense.
|
||||||
* @default `true`
|
* @default `true`
|
||||||
*/
|
*/
|
||||||
var flashingMenu:Bool;
|
var flashingLights:Bool;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If disabled, the camera bump synchronized to the beat.
|
* If disabled, the camera bump synchronized to the beat.
|
||||||
|
@ -551,7 +567,7 @@ typedef SaveDataOptions =
|
||||||
* If enabled, the game will automatically pause when tabbing out.
|
* If enabled, the game will automatically pause when tabbing out.
|
||||||
* @default `true`
|
* @default `true`
|
||||||
*/
|
*/
|
||||||
var pauseOnTabOut:Bool;
|
var autoPause:Bool;
|
||||||
|
|
||||||
var controls:
|
var controls:
|
||||||
{
|
{
|
||||||
|
|
|
@ -163,7 +163,17 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
|
|
||||||
function onSelect():Void
|
function onSelect():Void
|
||||||
{
|
{
|
||||||
keyUsedToEnterPrompt = FlxG.keys.firstJustPressed();
|
switch (currentDevice)
|
||||||
|
{
|
||||||
|
case Keys:
|
||||||
|
{
|
||||||
|
keyUsedToEnterPrompt = FlxG.keys.firstJustPressed();
|
||||||
|
}
|
||||||
|
case Gamepad(id):
|
||||||
|
{
|
||||||
|
buttonUsedToEnterPrompt = FlxG.gamepads.getByID(id).firstJustPressedID();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
controlGrid.enabled = false;
|
controlGrid.enabled = false;
|
||||||
canExit = false;
|
canExit = false;
|
||||||
|
@ -204,6 +214,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyUsedToEnterPrompt:Null<Int> = null;
|
var keyUsedToEnterPrompt:Null<Int> = null;
|
||||||
|
var buttonUsedToEnterPrompt:Null<Int> = null;
|
||||||
|
|
||||||
override function update(elapsed:Float):Void
|
override function update(elapsed:Float):Void
|
||||||
{
|
{
|
||||||
|
@ -246,19 +257,49 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
||||||
case Gamepad(id):
|
case Gamepad(id):
|
||||||
{
|
{
|
||||||
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
|
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
|
||||||
if (button != NONE && button != keyUsedToEnterPrompt)
|
if (button != NONE && button != buttonUsedToEnterPrompt)
|
||||||
{
|
{
|
||||||
if (button != BACK) onInputSelect(button);
|
if (button != BACK) onInputSelect(button);
|
||||||
closePrompt();
|
closePrompt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var key = FlxG.keys.firstJustReleased();
|
||||||
|
if (key != NONE && key != keyUsedToEnterPrompt)
|
||||||
|
{
|
||||||
|
if (key == ESCAPE)
|
||||||
|
{
|
||||||
|
closePrompt();
|
||||||
|
}
|
||||||
|
else if (key == BACKSPACE)
|
||||||
|
{
|
||||||
|
onInputSelect(NONE);
|
||||||
|
closePrompt();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var keyJustReleased:Int = FlxG.keys.firstJustReleased();
|
switch (currentDevice)
|
||||||
if (keyJustReleased != NONE && keyJustReleased == keyUsedToEnterPrompt)
|
|
||||||
{
|
{
|
||||||
keyUsedToEnterPrompt = null;
|
case Keys:
|
||||||
|
{
|
||||||
|
var keyJustReleased:Int = FlxG.keys.firstJustReleased();
|
||||||
|
if (keyJustReleased != NONE && keyJustReleased == keyUsedToEnterPrompt)
|
||||||
|
{
|
||||||
|
keyUsedToEnterPrompt = null;
|
||||||
|
}
|
||||||
|
buttonUsedToEnterPrompt = null;
|
||||||
|
}
|
||||||
|
case Gamepad(id):
|
||||||
|
{
|
||||||
|
var buttonJustReleased:Int = FlxG.gamepads.getByID(id).firstJustReleasedID();
|
||||||
|
if (buttonJustReleased != NONE && buttonJustReleased == buttonUsedToEnterPrompt)
|
||||||
|
{
|
||||||
|
buttonUsedToEnterPrompt = null;
|
||||||
|
}
|
||||||
|
keyUsedToEnterPrompt = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,16 @@ package funkin.ui;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||||
import funkin.ui.AtlasText.AtlasFont;
|
import funkin.ui.AtlasText.AtlasFont;
|
||||||
import funkin.ui.OptionsState.Page;
|
import funkin.ui.OptionsState.Page;
|
||||||
import funkin.ui.TextMenuList.TextMenuItem;
|
import funkin.ui.TextMenuList.TextMenuItem;
|
||||||
|
|
||||||
class PreferencesMenu extends Page
|
class PreferencesMenu extends Page
|
||||||
{
|
{
|
||||||
public static var preferences:Map<String, Dynamic> = new Map();
|
|
||||||
|
|
||||||
var items:TextMenuList;
|
var items:TextMenuList;
|
||||||
|
var preferenceItems:FlxTypedSpriteGroup<FlxSprite>;
|
||||||
|
|
||||||
var checkboxes:Array<CheckboxThingie> = [];
|
|
||||||
var menuCamera:FlxCamera;
|
var menuCamera:FlxCamera;
|
||||||
var camFollow:FlxObject;
|
var camFollow:FlxObject;
|
||||||
|
|
||||||
|
@ -27,13 +26,9 @@ class PreferencesMenu extends Page
|
||||||
camera = menuCamera;
|
camera = menuCamera;
|
||||||
|
|
||||||
add(items = new TextMenuList());
|
add(items = new TextMenuList());
|
||||||
|
add(preferenceItems = new FlxTypedSpriteGroup<FlxSprite>());
|
||||||
|
|
||||||
createPrefItem('naughtyness', 'censor-naughty', true);
|
createPrefItems();
|
||||||
createPrefItem('downscroll', 'downscroll', false);
|
|
||||||
createPrefItem('flashing menu', 'flashing-menu', true);
|
|
||||||
createPrefItem('Camera Zooming on Beat', 'camera-zoom', true);
|
|
||||||
createPrefItem('FPS Counter', 'fps-counter', true);
|
|
||||||
createPrefItem('Auto Pause', 'auto-pause', false);
|
|
||||||
|
|
||||||
camFollow = new FlxObject(FlxG.width / 2, 0, 140, 70);
|
camFollow = new FlxObject(FlxG.width / 2, 0, 140, 70);
|
||||||
if (items != null) camFollow.y = items.selectedItem.y;
|
if (items != null) camFollow.y = items.selectedItem.y;
|
||||||
|
@ -48,128 +43,63 @@ class PreferencesMenu extends Page
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function getPref(pref:String):Dynamic
|
/**
|
||||||
|
* Create the menu items for each of the preferences.
|
||||||
|
*/
|
||||||
|
function createPrefItems():Void
|
||||||
{
|
{
|
||||||
return preferences.get(pref);
|
createPrefItemCheckbox('Naughtyness', 'Toggle displaying raunchy content', function(value:Bool):Void {
|
||||||
|
Preferences.naughtyness = value;
|
||||||
|
}, Preferences.naughtyness);
|
||||||
|
createPrefItemCheckbox('Downscroll', 'Enable to make notes move downwards', function(value:Bool):Void {
|
||||||
|
Preferences.downscroll = value;
|
||||||
|
}, Preferences.downscroll);
|
||||||
|
createPrefItemCheckbox('Flashing Lights', 'Disable to dampen flashing effects', function(value:Bool):Void {
|
||||||
|
Preferences.flashingLights = value;
|
||||||
|
}, Preferences.flashingLights);
|
||||||
|
createPrefItemCheckbox('Camera Zooming on Beat', 'Disable to stop the camera bouncing to the song', function(value:Bool):Void {
|
||||||
|
Preferences.zoomCamera = value;
|
||||||
|
}, Preferences.zoomCamera);
|
||||||
|
createPrefItemCheckbox('Debug Display', 'Enable to show FPS and other debug stats', function(value:Bool):Void {
|
||||||
|
Preferences.debugDisplay = value;
|
||||||
|
}, Preferences.debugDisplay);
|
||||||
|
createPrefItemCheckbox('Auto Pause', 'Automatically pause the game when it loses focus', function(value:Bool):Void {
|
||||||
|
Preferences.autoPause = value;
|
||||||
|
}, Preferences.autoPause);
|
||||||
}
|
}
|
||||||
|
|
||||||
// easy shorthand?
|
function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool):Void
|
||||||
public static function setPref(pref:String, value:Dynamic):Void
|
|
||||||
{
|
{
|
||||||
preferences.set(pref, value);
|
var checkbox:CheckboxPreferenceItem = new CheckboxPreferenceItem(0, 120 * (items.length - 1 + 1), defaultValue);
|
||||||
}
|
|
||||||
|
|
||||||
public static function initPrefs():Void
|
|
||||||
{
|
|
||||||
preferenceCheck('censor-naughty', true);
|
|
||||||
preferenceCheck('downscroll', false);
|
|
||||||
preferenceCheck('flashing-menu', true);
|
|
||||||
preferenceCheck('camera-zoom', true);
|
|
||||||
preferenceCheck('fps-counter', true);
|
|
||||||
preferenceCheck('auto-pause', false);
|
|
||||||
preferenceCheck('master-volume', 1);
|
|
||||||
|
|
||||||
#if muted
|
|
||||||
setPref('master-volume', 0);
|
|
||||||
FlxG.sound.muted = true;
|
|
||||||
#end
|
|
||||||
|
|
||||||
if (!getPref('fps-counter')) FlxG.stage.removeChild(Main.fpsCounter);
|
|
||||||
|
|
||||||
FlxG.autoPause = getPref('auto-pause');
|
|
||||||
}
|
|
||||||
|
|
||||||
function createPrefItem(prefName:String, prefString:String, prefValue:Dynamic):Void
|
|
||||||
{
|
|
||||||
items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() {
|
items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() {
|
||||||
preferenceCheck(prefString, prefValue);
|
var value = !checkbox.currentValue;
|
||||||
|
onChange(value);
|
||||||
switch (Type.typeof(prefValue).getName())
|
checkbox.currentValue = value;
|
||||||
{
|
|
||||||
case 'TBool':
|
|
||||||
prefToggle(prefString);
|
|
||||||
|
|
||||||
default:
|
|
||||||
trace('swag');
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
switch (Type.typeof(prefValue).getName())
|
preferenceItems.add(checkbox);
|
||||||
{
|
|
||||||
case 'TBool':
|
|
||||||
createCheckbox(prefString);
|
|
||||||
|
|
||||||
default:
|
|
||||||
trace('swag');
|
|
||||||
}
|
|
||||||
|
|
||||||
trace(Type.typeof(prefValue).getName());
|
|
||||||
}
|
|
||||||
|
|
||||||
function createCheckbox(prefString:String)
|
|
||||||
{
|
|
||||||
var checkbox:CheckboxThingie = new CheckboxThingie(0, 120 * (items.length - 1), preferences.get(prefString));
|
|
||||||
checkboxes.push(checkbox);
|
|
||||||
add(checkbox);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assumes that the preference has already been checked/set?
|
|
||||||
*/
|
|
||||||
function prefToggle(prefName:String)
|
|
||||||
{
|
|
||||||
var daSwap:Bool = preferences.get(prefName);
|
|
||||||
daSwap = !daSwap;
|
|
||||||
preferences.set(prefName, daSwap);
|
|
||||||
checkboxes[items.selectedIndex].daValue = daSwap;
|
|
||||||
trace('toggled? ' + preferences.get(prefName));
|
|
||||||
|
|
||||||
switch (prefName)
|
|
||||||
{
|
|
||||||
case 'fps-counter':
|
|
||||||
if (getPref('fps-counter')) FlxG.stage.addChild(Main.fpsCounter);
|
|
||||||
else
|
|
||||||
FlxG.stage.removeChild(Main.fpsCounter);
|
|
||||||
case 'auto-pause':
|
|
||||||
FlxG.autoPause = getPref('auto-pause');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefName == 'fps-counter') {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float)
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
// menuCamera.followLerp = CoolUtil.camLerpShit(0.05);
|
// Indent the selected item.
|
||||||
|
// TODO: Only do this on menu change?
|
||||||
items.forEach(function(daItem:TextMenuItem) {
|
items.forEach(function(daItem:TextMenuItem) {
|
||||||
if (items.selectedItem == daItem) daItem.x = 150;
|
if (items.selectedItem == daItem) daItem.x = 150;
|
||||||
else
|
else
|
||||||
daItem.x = 120;
|
daItem.x = 120;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static function preferenceCheck(prefString:String, defaultValue:Dynamic):Void
|
|
||||||
{
|
|
||||||
if (preferences.get(prefString) == null)
|
|
||||||
{
|
|
||||||
// Set the value to default.
|
|
||||||
preferences.set(prefString, defaultValue);
|
|
||||||
trace('Set preference to default: ${prefString} = ${defaultValue}');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
trace('Found preference: ${prefString} = ${preferences.get(prefString)}');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class CheckboxThingie extends FlxSprite
|
class CheckboxPreferenceItem extends FlxSprite
|
||||||
{
|
{
|
||||||
public var daValue(default, set):Bool;
|
public var currentValue(default, set):Bool;
|
||||||
|
|
||||||
public function new(x:Float, y:Float, daValue:Bool = false)
|
public function new(x:Float, y:Float, defaultValue:Bool = false)
|
||||||
{
|
{
|
||||||
super(x, y);
|
super(x, y);
|
||||||
|
|
||||||
|
@ -180,7 +110,7 @@ class CheckboxThingie extends FlxSprite
|
||||||
setGraphicSize(Std.int(width * 0.7));
|
setGraphicSize(Std.int(width * 0.7));
|
||||||
updateHitbox();
|
updateHitbox();
|
||||||
|
|
||||||
this.daValue = daValue;
|
this.currentValue = defaultValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
override function update(elapsed:Float)
|
override function update(elapsed:Float)
|
||||||
|
@ -196,12 +126,17 @@ class CheckboxThingie extends FlxSprite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function set_daValue(value:Bool):Bool
|
function set_currentValue(value:Bool):Bool
|
||||||
{
|
{
|
||||||
if (value) animation.play('checked', true);
|
if (value)
|
||||||
|
{
|
||||||
|
animation.play('checked', true);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
animation.play('static');
|
animation.play('static');
|
||||||
|
}
|
||||||
|
|
||||||
return value;
|
return currentValue = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,7 +122,7 @@ class StickerSubState extends MusicBeatSubState
|
||||||
var daSound:String = FlxG.random.getObject(sounds);
|
var daSound:String = FlxG.random.getObject(sounds);
|
||||||
FlxG.sound.play(Paths.sound(daSound));
|
FlxG.sound.play(Paths.sound(daSound));
|
||||||
|
|
||||||
if (ind == grpStickers.members.length - 1)
|
if (grpStickers == null || ind == grpStickers.members.length - 1)
|
||||||
{
|
{
|
||||||
switchingState = false;
|
switchingState = false;
|
||||||
close();
|
close();
|
||||||
|
|
|
@ -10,7 +10,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
|
||||||
super(navControls, wrapMode);
|
super(navControls, wrapMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function createItem(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, callback, fireInstantly = false)
|
public function createItem(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, ?callback:Void->Void, fireInstantly = false)
|
||||||
{
|
{
|
||||||
var item = new TextMenuItem(x, y, name, font, callback);
|
var item = new TextMenuItem(x, y, name, font, callback);
|
||||||
item.fireInstantly = fireInstantly;
|
item.fireInstantly = fireInstantly;
|
||||||
|
@ -20,7 +20,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
|
||||||
|
|
||||||
class TextMenuItem extends TextTypedMenuItem<AtlasText>
|
class TextMenuItem extends TextTypedMenuItem<AtlasText>
|
||||||
{
|
{
|
||||||
public function new(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, callback)
|
public function new(x = 0.0, y = 0.0, name:String, font:AtlasFont = BOLD, ?callback:Void->Void)
|
||||||
{
|
{
|
||||||
super(x, y, new AtlasText(0, 0, name, font), name, callback);
|
super(x, y, new AtlasText(0, 0, name, font), name, callback);
|
||||||
setEmptyBackground();
|
setEmptyBackground();
|
||||||
|
@ -29,7 +29,7 @@ class TextMenuItem extends TextTypedMenuItem<AtlasText>
|
||||||
|
|
||||||
class TextTypedMenuItem<T:AtlasText> extends MenuTypedItem<T>
|
class TextTypedMenuItem<T:AtlasText> extends MenuTypedItem<T>
|
||||||
{
|
{
|
||||||
public function new(x = 0.0, y = 0.0, label:T, name:String, callback)
|
public function new(x = 0.0, y = 0.0, label:T, name:String, ?callback:Void->Void)
|
||||||
{
|
{
|
||||||
super(x, y, label, name, callback);
|
super(x, y, label, name, callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -253,6 +253,10 @@ class DebugBoundingState extends FlxState
|
||||||
offsetView.add(animDropDownMenu);
|
offsetView.add(animDropDownMenu);
|
||||||
|
|
||||||
var characters:Array<String> = CharacterDataParser.listCharacterIds();
|
var characters:Array<String> = CharacterDataParser.listCharacterIds();
|
||||||
|
characters = characters.filter(function(charId:String) {
|
||||||
|
var char = CharacterDataParser.fetchCharacterData(charId);
|
||||||
|
return char.renderType != AnimateAtlas;
|
||||||
|
});
|
||||||
characters.sort(SortUtil.alphabetically);
|
characters.sort(SortUtil.alphabetically);
|
||||||
|
|
||||||
var charDropdown:DropDown = cast uiStuff.findComponent('characterDropdown');
|
var charDropdown:DropDown = cast uiStuff.findComponent('characterDropdown');
|
||||||
|
|
|
@ -66,7 +66,7 @@ class AddNotesCommand implements ChartEditorCommand
|
||||||
state.currentEventSelection = [];
|
state.currentEventSelection = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -80,7 +80,7 @@ class AddNotesCommand implements ChartEditorCommand
|
||||||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
||||||
state.currentNoteSelection = [];
|
state.currentNoteSelection = [];
|
||||||
state.currentEventSelection = [];
|
state.currentEventSelection = [];
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -116,7 +116,8 @@ class RemoveNotesCommand implements ChartEditorCommand
|
||||||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
||||||
state.currentNoteSelection = [];
|
state.currentNoteSelection = [];
|
||||||
state.currentEventSelection = [];
|
state.currentEventSelection = [];
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -133,7 +134,7 @@ class RemoveNotesCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
state.currentNoteSelection = notes;
|
state.currentNoteSelection = notes;
|
||||||
state.currentEventSelection = [];
|
state.currentEventSelection = [];
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -254,7 +255,7 @@ class AddEventsCommand implements ChartEditorCommand
|
||||||
state.currentEventSelection = events;
|
state.currentEventSelection = events;
|
||||||
}
|
}
|
||||||
|
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -298,7 +299,8 @@ class RemoveEventsCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
|
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
|
||||||
state.currentEventSelection = [];
|
state.currentEventSelection = [];
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -314,7 +316,7 @@ class RemoveEventsCommand implements ChartEditorCommand
|
||||||
state.currentSongChartEventData.push(event);
|
state.currentSongChartEventData.push(event);
|
||||||
}
|
}
|
||||||
state.currentEventSelection = events;
|
state.currentEventSelection = events;
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -354,7 +356,7 @@ class RemoveItemsCommand implements ChartEditorCommand
|
||||||
state.currentNoteSelection = [];
|
state.currentNoteSelection = [];
|
||||||
state.currentEventSelection = [];
|
state.currentEventSelection = [];
|
||||||
|
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -378,7 +380,7 @@ class RemoveItemsCommand implements ChartEditorCommand
|
||||||
state.currentNoteSelection = notes;
|
state.currentNoteSelection = notes;
|
||||||
state.currentEventSelection = events;
|
state.currentEventSelection = events;
|
||||||
|
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
@ -805,6 +807,8 @@ class PasteItemsCommand implements ChartEditorCommand
|
||||||
|
|
||||||
public function undo(state:ChartEditorState):Void
|
public function undo(state:ChartEditorState):Void
|
||||||
{
|
{
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||||
|
|
||||||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, addedNotes);
|
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, addedNotes);
|
||||||
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, addedEvents);
|
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, addedEvents);
|
||||||
state.currentNoteSelection = [];
|
state.currentNoteSelection = [];
|
||||||
|
@ -857,6 +861,8 @@ class ExtendNoteLengthCommand implements ChartEditorCommand
|
||||||
|
|
||||||
public function undo(state:ChartEditorState):Void
|
public function undo(state:ChartEditorState):Void
|
||||||
{
|
{
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||||
|
|
||||||
note.length = oldLength;
|
note.length = oldLength;
|
||||||
|
|
||||||
state.saveDataDirty = true;
|
state.saveDataDirty = true;
|
||||||
|
|
|
@ -404,7 +404,6 @@ class ChartEditorDialogHandler
|
||||||
{
|
{
|
||||||
if (ChartEditorAudioHandler.loadInstFromBytes(state, selectedFile.bytes, instId))
|
if (ChartEditorAudioHandler.loadInstFromBytes(state, selectedFile.bytes, instId))
|
||||||
{
|
{
|
||||||
trace('Selected file: ' + selectedFile.fullPath);
|
|
||||||
#if !mac
|
#if !mac
|
||||||
NotificationManager.instance.addNotification(
|
NotificationManager.instance.addNotification(
|
||||||
{
|
{
|
||||||
|
@ -415,13 +414,12 @@ class ChartEditorDialogHandler
|
||||||
});
|
});
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
state.switchToCurrentInstrumental();
|
||||||
dialog.hideDialog(DialogButton.APPLY);
|
dialog.hideDialog(DialogButton.APPLY);
|
||||||
removeDropHandler(onDropFile);
|
removeDropHandler(onDropFile);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
trace('Failed to load instrumental (${selectedFile.fullPath})');
|
|
||||||
|
|
||||||
#if !mac
|
#if !mac
|
||||||
NotificationManager.instance.addNotification(
|
NotificationManager.instance.addNotification(
|
||||||
{
|
{
|
||||||
|
@ -452,6 +450,7 @@ class ChartEditorDialogHandler
|
||||||
});
|
});
|
||||||
#end
|
#end
|
||||||
|
|
||||||
|
state.switchToCurrentInstrumental();
|
||||||
dialog.hideDialog(DialogButton.APPLY);
|
dialog.hideDialog(DialogButton.APPLY);
|
||||||
removeDropHandler(onDropFile);
|
removeDropHandler(onDropFile);
|
||||||
}
|
}
|
||||||
|
@ -570,6 +569,12 @@ class ChartEditorDialogHandler
|
||||||
|
|
||||||
var newSongMetadata:SongMetadata = new SongMetadata('', '', 'default');
|
var newSongMetadata:SongMetadata = new SongMetadata('', '', 'default');
|
||||||
|
|
||||||
|
newSongMetadata.playData.difficulties = switch (targetVariation)
|
||||||
|
{
|
||||||
|
case 'erect': ['erect', 'nightmare'];
|
||||||
|
default: ['easy', 'normal', 'hard'];
|
||||||
|
};
|
||||||
|
|
||||||
var inputSongName:Null<TextField> = dialog.findComponent('inputSongName', TextField);
|
var inputSongName:Null<TextField> = dialog.findComponent('inputSongName', TextField);
|
||||||
if (inputSongName == null) throw 'Could not locate inputSongName TextField in Song Metadata dialog';
|
if (inputSongName == null) throw 'Could not locate inputSongName TextField in Song Metadata dialog';
|
||||||
inputSongName.onChange = function(event:UIEvent) {
|
inputSongName.onChange = function(event:UIEvent) {
|
||||||
|
@ -667,8 +672,6 @@ class ChartEditorDialogHandler
|
||||||
timeChanges[0].bpm = event.value;
|
timeChanges[0].bpm = event.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Conductor.forceBPM(event.value);
|
|
||||||
|
|
||||||
newSongMetadata.timeChanges = timeChanges;
|
newSongMetadata.timeChanges = timeChanges;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -677,6 +680,8 @@ class ChartEditorDialogHandler
|
||||||
dialogContinue.onClick = (_event) -> {
|
dialogContinue.onClick = (_event) -> {
|
||||||
state.songMetadata.set(targetVariation, newSongMetadata);
|
state.songMetadata.set(targetVariation, newSongMetadata);
|
||||||
|
|
||||||
|
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
|
|
||||||
dialog.hideDialog(DialogButton.APPLY);
|
dialog.hideDialog(DialogButton.APPLY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,6 +701,8 @@ class ChartEditorDialogHandler
|
||||||
|
|
||||||
var charData:SongCharacterData = state.currentSongMetadata.playData.characters;
|
var charData:SongCharacterData = state.currentSongMetadata.playData.characters;
|
||||||
|
|
||||||
|
var hasClearedVocals:Bool = false;
|
||||||
|
|
||||||
charIdsForVocals.push(charData.player);
|
charIdsForVocals.push(charData.player);
|
||||||
charIdsForVocals.push(charData.opponent);
|
charIdsForVocals.push(charData.opponent);
|
||||||
|
|
||||||
|
@ -715,6 +722,7 @@ class ChartEditorDialogHandler
|
||||||
if (dialogNoVocals == null) throw 'Could not locate dialogNoVocals button in Upload Vocals dialog';
|
if (dialogNoVocals == null) throw 'Could not locate dialogNoVocals button in Upload Vocals dialog';
|
||||||
dialogNoVocals.onClick = function(_event) {
|
dialogNoVocals.onClick = function(_event) {
|
||||||
// Dismiss
|
// Dismiss
|
||||||
|
ChartEditorAudioHandler.stopExistingVocals(state);
|
||||||
dialog.hideDialog(DialogButton.APPLY);
|
dialog.hideDialog(DialogButton.APPLY);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -738,6 +746,12 @@ class ChartEditorDialogHandler
|
||||||
trace('Selected file: $pathStr');
|
trace('Selected file: $pathStr');
|
||||||
var path:Path = new Path(pathStr);
|
var path:Path = new Path(pathStr);
|
||||||
|
|
||||||
|
if (!hasClearedVocals)
|
||||||
|
{
|
||||||
|
hasClearedVocals = true;
|
||||||
|
ChartEditorAudioHandler.stopExistingVocals(state);
|
||||||
|
}
|
||||||
|
|
||||||
if (ChartEditorAudioHandler.loadVocalsFromPath(state, path, charKey, instId))
|
if (ChartEditorAudioHandler.loadVocalsFromPath(state, path, charKey, instId))
|
||||||
{
|
{
|
||||||
// Tell the user the load was successful.
|
// Tell the user the load was successful.
|
||||||
|
@ -788,6 +802,11 @@ class ChartEditorDialogHandler
|
||||||
if (selectedFile != null && selectedFile.bytes != null)
|
if (selectedFile != null && selectedFile.bytes != null)
|
||||||
{
|
{
|
||||||
trace('Selected file: ' + selectedFile.name);
|
trace('Selected file: ' + selectedFile.name);
|
||||||
|
if (!hasClearedVocals)
|
||||||
|
{
|
||||||
|
hasClearedVocals = true;
|
||||||
|
ChartEditorAudioHandler.stopExistingVocals(state);
|
||||||
|
}
|
||||||
if (ChartEditorAudioHandler.loadVocalsFromBytes(state, selectedFile.bytes, charKey, instId))
|
if (ChartEditorAudioHandler.loadVocalsFromBytes(state, selectedFile.bytes, charKey, instId))
|
||||||
{
|
{
|
||||||
// Tell the user the load was successful.
|
// Tell the user the load was successful.
|
||||||
|
|
|
@ -87,9 +87,8 @@ using Lambda;
|
||||||
*
|
*
|
||||||
* @author MasterEric
|
* @author MasterEric
|
||||||
*/
|
*/
|
||||||
|
@:nullSafety
|
||||||
// Give other classes access to private instance fields
|
// Give other classes access to private instance fields
|
||||||
// @:nullSafety(Loose) // Enable this while developing, then disable to keep unit tests functional!
|
|
||||||
|
|
||||||
@:allow(funkin.ui.debug.charting.ChartEditorCommand)
|
@:allow(funkin.ui.debug.charting.ChartEditorCommand)
|
||||||
@:allow(funkin.ui.debug.charting.ChartEditorDropdowns)
|
@:allow(funkin.ui.debug.charting.ChartEditorDropdowns)
|
||||||
@:allow(funkin.ui.debug.charting.ChartEditorDialogHandler)
|
@:allow(funkin.ui.debug.charting.ChartEditorDialogHandler)
|
||||||
|
@ -555,6 +554,9 @@ class ChartEditorState extends HaxeUIState
|
||||||
notePreviewDirty = true;
|
notePreviewDirty = true;
|
||||||
notePreviewViewportBoundsDirty = true;
|
notePreviewViewportBoundsDirty = true;
|
||||||
|
|
||||||
|
// Make sure the difficulty we selected is in the list of difficulties.
|
||||||
|
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||||
|
|
||||||
return selectedDifficulty;
|
return selectedDifficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -965,13 +967,14 @@ class ChartEditorState extends HaxeUIState
|
||||||
|
|
||||||
function get_currentSongChartNoteData():Array<SongNoteData>
|
function get_currentSongChartNoteData():Array<SongNoteData>
|
||||||
{
|
{
|
||||||
var result:Array<SongNoteData> = currentSongChartData.notes.get(selectedDifficulty);
|
var result:Null<Array<SongNoteData>> = currentSongChartData.notes.get(selectedDifficulty);
|
||||||
if (result == null)
|
if (result == null)
|
||||||
{
|
{
|
||||||
// Initialize to the default value if not set.
|
// Initialize to the default value if not set.
|
||||||
result = [];
|
result = [];
|
||||||
trace('Initializing blank note data for difficulty ' + selectedDifficulty);
|
trace('Initializing blank note data for difficulty ' + selectedDifficulty);
|
||||||
currentSongChartData.notes.set(selectedDifficulty, result);
|
currentSongChartData.notes.set(selectedDifficulty, result);
|
||||||
|
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
@ -980,6 +983,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
function set_currentSongChartNoteData(value:Array<SongNoteData>):Array<SongNoteData>
|
function set_currentSongChartNoteData(value:Array<SongNoteData>):Array<SongNoteData>
|
||||||
{
|
{
|
||||||
currentSongChartData.notes.set(selectedDifficulty, value);
|
currentSongChartData.notes.set(selectedDifficulty, value);
|
||||||
|
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1391,16 +1395,12 @@ class ChartEditorState extends HaxeUIState
|
||||||
healthIconDad = new HealthIcon(currentSongMetadata.playData.characters.opponent);
|
healthIconDad = new HealthIcon(currentSongMetadata.playData.characters.opponent);
|
||||||
healthIconDad.autoUpdate = false;
|
healthIconDad.autoUpdate = false;
|
||||||
healthIconDad.size.set(0.5, 0.5);
|
healthIconDad.size.set(0.5, 0.5);
|
||||||
healthIconDad.x = gridTiledSprite.x - 15 - (HealthIcon.HEALTH_ICON_SIZE * 0.5);
|
|
||||||
healthIconDad.y = gridTiledSprite.y + 5;
|
|
||||||
add(healthIconDad);
|
add(healthIconDad);
|
||||||
healthIconDad.zIndex = 30;
|
healthIconDad.zIndex = 30;
|
||||||
|
|
||||||
healthIconBF = new HealthIcon(currentSongMetadata.playData.characters.player);
|
healthIconBF = new HealthIcon(currentSongMetadata.playData.characters.player);
|
||||||
healthIconBF.autoUpdate = false;
|
healthIconBF.autoUpdate = false;
|
||||||
healthIconBF.size.set(0.5, 0.5);
|
healthIconBF.size.set(0.5, 0.5);
|
||||||
healthIconBF.x = gridTiledSprite.x + gridTiledSprite.width + 15;
|
|
||||||
healthIconBF.y = gridTiledSprite.y + 5;
|
|
||||||
healthIconBF.flipX = true;
|
healthIconBF.flipX = true;
|
||||||
add(healthIconBF);
|
add(healthIconBF);
|
||||||
healthIconBF.zIndex = 30;
|
healthIconBF.zIndex = 30;
|
||||||
|
@ -1627,6 +1627,12 @@ class ChartEditorState extends HaxeUIState
|
||||||
addUIClickListener('playbarForward', _ -> playbarButtonPressed = 'playbarForward');
|
addUIClickListener('playbarForward', _ -> playbarButtonPressed = 'playbarForward');
|
||||||
addUIClickListener('playbarEnd', _ -> playbarButtonPressed = 'playbarEnd');
|
addUIClickListener('playbarEnd', _ -> playbarButtonPressed = 'playbarEnd');
|
||||||
|
|
||||||
|
// Cycle note snap quant.
|
||||||
|
addUIClickListener('playbarNoteSnap', function(_) {
|
||||||
|
noteSnapQuantIndex++;
|
||||||
|
if (noteSnapQuantIndex >= SNAP_QUANTS.length) noteSnapQuantIndex = 0;
|
||||||
|
});
|
||||||
|
|
||||||
// Add functionality to the menu items.
|
// Add functionality to the menu items.
|
||||||
|
|
||||||
addUIClickListener('menubarItemNewChart', _ -> ChartEditorDialogHandler.openWelcomeDialog(this, true));
|
addUIClickListener('menubarItemNewChart', _ -> ChartEditorDialogHandler.openWelcomeDialog(this, true));
|
||||||
|
@ -1897,6 +1903,16 @@ class ChartEditorState extends HaxeUIState
|
||||||
handleViewKeybinds();
|
handleViewKeybinds();
|
||||||
handleTestKeybinds();
|
handleTestKeybinds();
|
||||||
handleHelpKeybinds();
|
handleHelpKeybinds();
|
||||||
|
|
||||||
|
#if debug
|
||||||
|
handleQuickWatch();
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleQuickWatch():Void
|
||||||
|
{
|
||||||
|
FlxG.watch.addQuick('scrollPosInPixels', scrollPositionInPixels);
|
||||||
|
FlxG.watch.addQuick('playheadPosInPixels', playheadPositionInPixels);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2128,11 +2144,18 @@ class ChartEditorState extends HaxeUIState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var dragLengthCurrent:Float = 0;
|
||||||
|
var stretchySounds:Bool = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle display of the mouse cursor.
|
* Handle display of the mouse cursor.
|
||||||
*/
|
*/
|
||||||
function handleCursor():Void
|
function handleCursor():Void
|
||||||
{
|
{
|
||||||
|
// Mouse sounds
|
||||||
|
if (FlxG.mouse.justPressed) FlxG.sound.play(Paths.sound("chartingSounds/ClickDown"));
|
||||||
|
if (FlxG.mouse.justReleased) FlxG.sound.play(Paths.sound("chartingSounds/ClickUp"));
|
||||||
|
|
||||||
// Note: If a menu is open in HaxeUI, don't handle cursor behavior.
|
// Note: If a menu is open in HaxeUI, don't handle cursor behavior.
|
||||||
var shouldHandleCursor:Bool = !isCursorOverHaxeUI || (selectionBoxStartPos != null);
|
var shouldHandleCursor:Bool = !isCursorOverHaxeUI || (selectionBoxStartPos != null);
|
||||||
var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1;
|
var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1;
|
||||||
|
@ -2477,25 +2500,37 @@ class ChartEditorState extends HaxeUIState
|
||||||
var dragLengthMs:Float = dragLengthSteps * Conductor.stepLengthMs;
|
var dragLengthMs:Float = dragLengthSteps * Conductor.stepLengthMs;
|
||||||
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
||||||
|
|
||||||
if (dragLengthSteps > 0)
|
if (gridGhostNote != null && gridGhostNote.noteData != null && gridGhostHoldNote != null)
|
||||||
{
|
{
|
||||||
gridGhostHoldNote.visible = true;
|
if (dragLengthSteps > 0)
|
||||||
gridGhostHoldNote.noteData = gridGhostNote.noteData;
|
{
|
||||||
gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
|
if (dragLengthCurrent != dragLengthSteps)
|
||||||
|
{
|
||||||
|
stretchySounds = !stretchySounds;
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/stretch' + (stretchySounds ? '1' : '2') + '_UI'));
|
||||||
|
|
||||||
gridGhostHoldNote.setHeightDirectly(dragLengthPixels);
|
dragLengthCurrent = dragLengthSteps;
|
||||||
|
}
|
||||||
|
|
||||||
gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes);
|
gridGhostHoldNote.visible = true;
|
||||||
}
|
gridGhostHoldNote.noteData = gridGhostNote.noteData;
|
||||||
else
|
gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
|
||||||
{
|
|
||||||
gridGhostHoldNote.visible = false;
|
gridGhostHoldNote.setHeightDirectly(dragLengthPixels);
|
||||||
|
|
||||||
|
gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
gridGhostHoldNote.visible = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FlxG.mouse.justReleased)
|
if (FlxG.mouse.justReleased)
|
||||||
{
|
{
|
||||||
if (dragLengthSteps > 0)
|
if (dragLengthSteps > 0)
|
||||||
{
|
{
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/stretchSNAP_UI'));
|
||||||
// Apply the new length.
|
// Apply the new length.
|
||||||
performCommand(new ExtendNoteLengthCommand(currentPlaceNoteData, dragLengthMs));
|
performCommand(new ExtendNoteLengthCommand(currentPlaceNoteData, dragLengthMs));
|
||||||
}
|
}
|
||||||
|
@ -2644,7 +2679,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
if (cursorColumn == eventColumn)
|
if (cursorColumn == eventColumn)
|
||||||
{
|
{
|
||||||
if (gridGhostNote != null) gridGhostNote.visible = false;
|
if (gridGhostNote != null) gridGhostNote.visible = false;
|
||||||
gridGhostHoldNote.visible = false;
|
if (gridGhostHoldNote != null) gridGhostHoldNote.visible = false;
|
||||||
|
|
||||||
if (gridGhostEvent == null) throw "ERROR: Tried to handle cursor, but gridGhostEvent is null! Check ChartEditorState.buildGrid()";
|
if (gridGhostEvent == null) throw "ERROR: Tried to handle cursor, but gridGhostEvent is null! Check ChartEditorState.buildGrid()";
|
||||||
|
|
||||||
|
@ -2704,11 +2739,11 @@ class ChartEditorState extends HaxeUIState
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (FlxG.mouse.overlaps(notePreview))
|
if (notePreview != null && FlxG.mouse.overlaps(notePreview))
|
||||||
{
|
{
|
||||||
targetCursorMode = Pointer;
|
targetCursorMode = Pointer;
|
||||||
}
|
}
|
||||||
else if (FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
||||||
{
|
{
|
||||||
targetCursorMode = Pointer;
|
targetCursorMode = Pointer;
|
||||||
}
|
}
|
||||||
|
@ -3020,18 +3055,35 @@ class ChartEditorState extends HaxeUIState
|
||||||
{
|
{
|
||||||
if (healthIconsDirty)
|
if (healthIconsDirty)
|
||||||
{
|
{
|
||||||
if (healthIconBF != null) healthIconBF.characterId = currentSongMetadata.playData.characters.player;
|
var charDataBF = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.player);
|
||||||
if (healthIconDad != null) healthIconDad.characterId = currentSongMetadata.playData.characters.opponent;
|
var charDataDad = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.opponent);
|
||||||
|
if (healthIconBF != null)
|
||||||
|
{
|
||||||
|
healthIconBF.configure(charDataBF?.healthIcon);
|
||||||
|
healthIconBF.size *= 0.5; // Make the icon smaller in Chart Editor.
|
||||||
|
healthIconBF.flipX = !healthIconBF.flipX; // BF faces the other way.
|
||||||
|
}
|
||||||
|
if (healthIconDad != null)
|
||||||
|
{
|
||||||
|
healthIconDad.configure(charDataDad?.healthIcon);
|
||||||
|
healthIconDad.size *= 0.5; // Make the icon smaller in Chart Editor.
|
||||||
|
}
|
||||||
|
healthIconsDirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right align the BF health icon.
|
// Right align, and visibly center, the BF health icon.
|
||||||
if (healthIconBF != null)
|
if (healthIconBF != null)
|
||||||
{
|
{
|
||||||
// Base X position to the right of the grid.
|
// Base X position to the right of the grid.
|
||||||
var baseHealthIconXPos:Float = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 15);
|
healthIconBF.x = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 45 - (healthIconBF.width / 2));
|
||||||
// Will be 0 when not bopping. When bopping, will increase to push the icon left.
|
healthIconBF.y = (gridTiledSprite == null) ? (0) : (MENU_BAR_HEIGHT + GRID_TOP_PAD + 30 - (healthIconBF.height / 2));
|
||||||
var healthIconOffset:Float = healthIconBF.width - (HealthIcon.HEALTH_ICON_SIZE * 0.5);
|
}
|
||||||
healthIconBF.x = baseHealthIconXPos - healthIconOffset;
|
|
||||||
|
// Visibly center the Dad health icon.
|
||||||
|
if (healthIconDad != null)
|
||||||
|
{
|
||||||
|
healthIconDad.x = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x - 45 - (healthIconDad.width / 2));
|
||||||
|
healthIconDad.y = (gridTiledSprite == null) ? (0) : (MENU_BAR_HEIGHT + GRID_TOP_PAD + 30 - (healthIconDad.height / 2));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3119,6 +3171,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
function quitChartEditor():Void
|
function quitChartEditor():Void
|
||||||
{
|
{
|
||||||
autoSave();
|
autoSave();
|
||||||
|
stopWelcomeMusic();
|
||||||
FlxG.switchState(new MainMenuState());
|
FlxG.switchState(new MainMenuState());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3342,6 +3395,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
|
if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
|
||||||
{
|
{
|
||||||
var minimal = FlxG.keys.pressed.SHIFT;
|
var minimal = FlxG.keys.pressed.SHIFT;
|
||||||
|
ChartEditorToolboxHandler.hideAllToolboxes(this);
|
||||||
testSongInPlayState(minimal);
|
testSongInPlayState(minimal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3656,49 +3710,41 @@ class ChartEditorState extends HaxeUIState
|
||||||
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
||||||
var stageId:String = currentSongMetadata.playData.stage;
|
var stageId:String = currentSongMetadata.playData.stage;
|
||||||
var stageData:Null<StageData> = StageDataParser.parseStageData(stageId);
|
var stageData:Null<StageData> = StageDataParser.parseStageData(stageId);
|
||||||
if (stageData != null)
|
if (inputStage != null)
|
||||||
{
|
{
|
||||||
inputStage.value = {id: stageId, text: stageData.name};
|
inputStage.value = (stageData != null) ?
|
||||||
}
|
{id: stageId, text: stageData.name} :
|
||||||
else
|
{id: "mainStage", text: "Main Stage"};
|
||||||
{
|
|
||||||
inputStage.value = {id: "mainStage", text: "Main Stage"};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputCharacterPlayer:Null<DropDown> = toolbox.findComponent('inputCharacterPlayer', DropDown);
|
var inputCharacterPlayer:Null<DropDown> = toolbox.findComponent('inputCharacterPlayer', DropDown);
|
||||||
var charIdPlayer:String = currentSongMetadata.playData.characters.player;
|
var charIdPlayer:String = currentSongMetadata.playData.characters.player;
|
||||||
var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdPlayer);
|
var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdPlayer);
|
||||||
if (charDataPlayer != null)
|
if (inputCharacterPlayer != null)
|
||||||
{
|
{
|
||||||
inputCharacterPlayer.value = {id: charIdPlayer, text: charDataPlayer.name};
|
inputCharacterPlayer.value = (charDataPlayer != null) ?
|
||||||
}
|
{id: charIdPlayer, text: charDataPlayer.name} :
|
||||||
else
|
{id: "bf", text: "Boyfriend"};
|
||||||
{
|
|
||||||
inputCharacterPlayer.value = {id: "bf", text: "Boyfriend"};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputCharacterOpponent:Null<DropDown> = toolbox.findComponent('inputCharacterOpponent', DropDown);
|
var inputCharacterOpponent:Null<DropDown> = toolbox.findComponent('inputCharacterOpponent', DropDown);
|
||||||
var charIdOpponent:String = currentSongMetadata.playData.characters.opponent;
|
var charIdOpponent:String = currentSongMetadata.playData.characters.opponent;
|
||||||
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdOpponent);
|
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdOpponent);
|
||||||
if (charDataOpponent != null)
|
if (inputCharacterOpponent != null)
|
||||||
{
|
{
|
||||||
inputCharacterOpponent.value = {id: charIdOpponent, text: charDataOpponent.name};
|
inputCharacterOpponent.value = (charDataOpponent != null) ?
|
||||||
}
|
{id: charIdOpponent, text: charDataOpponent.name} :
|
||||||
else
|
{id: "dad", text: "Dad"};
|
||||||
{
|
|
||||||
inputCharacterOpponent.value = {id: "dad", text: "Dad"};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var inputCharacterGirlfriend:Null<DropDown> = toolbox.findComponent('inputCharacterGirlfriend', DropDown);
|
var inputCharacterGirlfriend:Null<DropDown> = toolbox.findComponent('inputCharacterGirlfriend', DropDown);
|
||||||
var charIdGirlfriend:String = currentSongMetadata.playData.characters.girlfriend;
|
var charIdGirlfriend:String = currentSongMetadata.playData.characters.girlfriend;
|
||||||
var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdGirlfriend);
|
var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdGirlfriend);
|
||||||
if (charDataGirlfriend != null)
|
if (inputCharacterGirlfriend != null)
|
||||||
{
|
{
|
||||||
inputCharacterGirlfriend.value = {id: charIdGirlfriend, text: charDataGirlfriend.name};
|
inputCharacterGirlfriend.value = (charDataGirlfriend != null) ?
|
||||||
}
|
{id: charIdGirlfriend, text: charDataGirlfriend.name} :
|
||||||
else
|
{id: "none", text: "None"};
|
||||||
{
|
|
||||||
inputCharacterGirlfriend.value = {id: "none", text: "None"};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3885,9 +3931,9 @@ class ChartEditorState extends HaxeUIState
|
||||||
switch (noteData.getStrumlineIndex())
|
switch (noteData.getStrumlineIndex())
|
||||||
{
|
{
|
||||||
case 0: // Player
|
case 0: // Player
|
||||||
if (hitsoundsEnabledPlayer) ChartEditorAudioHandler.playSound(Paths.sound('ui/chart-editor/playerHitsound'));
|
if (hitsoundsEnabledPlayer) ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/hitNotePlayer'));
|
||||||
case 1: // Opponent
|
case 1: // Opponent
|
||||||
if (hitsoundsEnabledOpponent) ChartEditorAudioHandler.playSound(Paths.sound('ui/chart-editor/opponentHitsound'));
|
if (hitsoundsEnabledOpponent) ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/hitNoteOpponent'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4004,7 +4050,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
this.scrollPositionInPixels = value;
|
this.scrollPositionInPixels = value;
|
||||||
|
|
||||||
// Move the grid sprite to the correct position.
|
// Move the grid sprite to the correct position.
|
||||||
if (gridTiledSprite != null)
|
if (gridTiledSprite != null && gridPlayheadScrollArea != null)
|
||||||
{
|
{
|
||||||
if (isViewDownscroll)
|
if (isViewDownscroll)
|
||||||
{
|
{
|
||||||
|
@ -4064,7 +4110,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
}
|
}
|
||||||
|
|
||||||
subStateClosed.add(fixCamera);
|
subStateClosed.add(fixCamera);
|
||||||
subStateClosed.add(updateConductor);
|
subStateClosed.add(resetConductorAfterTest);
|
||||||
|
|
||||||
FlxTransitionableState.skipNextTransIn = false;
|
FlxTransitionableState.skipNextTransIn = false;
|
||||||
FlxTransitionableState.skipNextTransOut = false;
|
FlxTransitionableState.skipNextTransOut = false;
|
||||||
|
@ -4097,10 +4143,9 @@ class ChartEditorState extends HaxeUIState
|
||||||
add(this.component);
|
add(this.component);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateConductor(_:FlxSubState = null):Void
|
function resetConductorAfterTest(_:FlxSubState = null):Void
|
||||||
{
|
{
|
||||||
var targetPos = scrollPositionInMs;
|
moveSongToScrollPosition();
|
||||||
Conductor.update(targetPos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function postLoadInstrumental():Void
|
public function postLoadInstrumental():Void
|
||||||
|
@ -4153,12 +4198,13 @@ class ChartEditorState extends HaxeUIState
|
||||||
*/
|
*/
|
||||||
function moveSongToScrollPosition():Void
|
function moveSongToScrollPosition():Void
|
||||||
{
|
{
|
||||||
// Update the songPosition in the Conductor.
|
|
||||||
var targetPos = scrollPositionInMs;
|
|
||||||
Conductor.update(targetPos);
|
|
||||||
|
|
||||||
// Update the songPosition in the audio tracks.
|
// Update the songPosition in the audio tracks.
|
||||||
if (audioInstTrack != null) audioInstTrack.time = scrollPositionInMs + playheadPositionInMs;
|
if (audioInstTrack != null)
|
||||||
|
{
|
||||||
|
audioInstTrack.time = scrollPositionInMs + playheadPositionInMs;
|
||||||
|
// Update the songPosition in the Conductor.
|
||||||
|
Conductor.update(audioInstTrack.time);
|
||||||
|
}
|
||||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = scrollPositionInMs + playheadPositionInMs;
|
if (audioVocalTrackGroup != null) audioVocalTrackGroup.time = scrollPositionInMs + playheadPositionInMs;
|
||||||
|
|
||||||
// We need to update the note sprites because we changed the scroll position.
|
// We need to update the note sprites because we changed the scroll position.
|
||||||
|
@ -4264,7 +4310,7 @@ class ChartEditorState extends HaxeUIState
|
||||||
|
|
||||||
function playMetronomeTick(high:Bool = false):Void
|
function playMetronomeTick(high:Bool = false):Void
|
||||||
{
|
{
|
||||||
ChartEditorAudioHandler.playSound(Paths.sound('pianoStuff/piano-${high ? '001' : '008'}'));
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/metronome${high ? '1' : '2'}'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNoteSelected(note:Null<SongNoteData>):Bool
|
function isNoteSelected(note:Null<SongNoteData>):Bool
|
||||||
|
|
|
@ -72,6 +72,8 @@ class ChartEditorToolboxHandler
|
||||||
{
|
{
|
||||||
toolbox.showDialog(false);
|
toolbox.showDialog(false);
|
||||||
|
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/openWindow'));
|
||||||
|
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
||||||
|
@ -109,6 +111,8 @@ class ChartEditorToolboxHandler
|
||||||
{
|
{
|
||||||
toolbox.hideDialog(DialogButton.CANCEL);
|
toolbox.hideDialog(DialogButton.CANCEL);
|
||||||
|
|
||||||
|
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/exitWindow'));
|
||||||
|
|
||||||
switch (id)
|
switch (id)
|
||||||
{
|
{
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
||||||
|
@ -136,6 +140,18 @@ class ChartEditorToolboxHandler
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function rememberOpenToolboxes(state:ChartEditorState):Void {}
|
||||||
|
|
||||||
|
public static function openRememberedToolboxes(state:ChartEditorState):Void {}
|
||||||
|
|
||||||
|
public static function hideAllToolboxes(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
for (toolbox in state.activeToolboxes.values())
|
||||||
|
{
|
||||||
|
toolbox.hideDialog(DialogButton.CANCEL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static function minimizeToolbox(state:ChartEditorState, id:String):Void
|
public static function minimizeToolbox(state:ChartEditorState, id:String):Void
|
||||||
{
|
{
|
||||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||||
|
@ -634,9 +650,9 @@ class ChartEditorToolboxHandler
|
||||||
timeChanges[0].bpm = event.value;
|
timeChanges[0].bpm = event.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Conductor.forceBPM(event.value);
|
|
||||||
|
|
||||||
state.currentSongMetadata.timeChanges = timeChanges;
|
state.currentSongMetadata.timeChanges = timeChanges;
|
||||||
|
|
||||||
|
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||||
};
|
};
|
||||||
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
|
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
|
||||||
|
|
||||||
|
|
30
source/funkin/ui/haxeui/components/FunkinClickLabel.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinClickLabel.hx
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
package funkin.ui.haxeui.components;
|
||||||
|
|
||||||
|
import haxe.ui.components.Label;
|
||||||
|
import funkin.input.Cursor;
|
||||||
|
import haxe.ui.events.MouseEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A HaxeUI label which:
|
||||||
|
* - Changes the current cursor when hovered over (assume an onClick handler will be added!).
|
||||||
|
*/
|
||||||
|
class FunkinClickLabel extends Label
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.onMouseOver = handleMouseOver;
|
||||||
|
this.onMouseOut = handleMouseOut;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleMouseOver(event:MouseEvent)
|
||||||
|
{
|
||||||
|
Cursor.cursorMode = Pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function handleMouseOut(event:MouseEvent)
|
||||||
|
{
|
||||||
|
Cursor.cursorMode = Default;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
package funkin.ui.story;
|
package funkin.ui.story;
|
||||||
|
|
||||||
|
import funkin.util.SortUtil;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import funkin.play.song.Song;
|
import funkin.play.song.Song;
|
||||||
|
@ -155,6 +156,8 @@ class Level implements IRegistryEntry<LevelData>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
difficulties.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST));
|
||||||
|
|
||||||
// Filter to only include difficulties that are present in all songs
|
// Filter to only include difficulties that are present in all songs
|
||||||
for (songIndex in 1...songList.length)
|
for (songIndex in 1...songList.length)
|
||||||
{
|
{
|
||||||
|
|
|
@ -99,6 +99,9 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
var stickerSubState:StickerSubState;
|
var stickerSubState:StickerSubState;
|
||||||
|
|
||||||
|
static var rememberedLevelId:Null<String> = null;
|
||||||
|
static var rememberedDifficulty:Null<String> = "normal";
|
||||||
|
|
||||||
public function new(?stickers:StickerSubState = null)
|
public function new(?stickers:StickerSubState = null)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
@ -133,6 +136,8 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
updateData();
|
updateData();
|
||||||
|
|
||||||
|
rememberSelection();
|
||||||
|
|
||||||
// Explicitly define the background color.
|
// Explicitly define the background color.
|
||||||
this.bgColor = FlxColor.BLACK;
|
this.bgColor = FlxColor.BLACK;
|
||||||
|
|
||||||
|
@ -185,6 +190,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
leftDifficultyArrow.animation.play('idle');
|
leftDifficultyArrow.animation.play('idle');
|
||||||
add(leftDifficultyArrow);
|
add(leftDifficultyArrow);
|
||||||
|
|
||||||
|
buildDifficultySprite(Constants.DEFAULT_DIFFICULTY);
|
||||||
buildDifficultySprite();
|
buildDifficultySprite();
|
||||||
|
|
||||||
rightDifficultyArrow = new FlxSprite(difficultySprite.x + difficultySprite.width + 10, leftDifficultyArrow.y);
|
rightDifficultyArrow = new FlxSprite(difficultySprite.x + difficultySprite.width + 10, leftDifficultyArrow.y);
|
||||||
|
@ -207,6 +213,18 @@ class StoryMenuState extends MusicBeatState
|
||||||
#end
|
#end
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function rememberSelection():Void
|
||||||
|
{
|
||||||
|
if (rememberedLevelId != null)
|
||||||
|
{
|
||||||
|
currentLevelId = rememberedLevelId;
|
||||||
|
}
|
||||||
|
if (rememberedDifficulty != null)
|
||||||
|
{
|
||||||
|
currentDifficultyId = rememberedDifficulty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function playMenuMusic():Void
|
function playMenuMusic():Void
|
||||||
{
|
{
|
||||||
if (FlxG.sound.music == null || !FlxG.sound.music.playing)
|
if (FlxG.sound.music == null || !FlxG.sound.music.playing)
|
||||||
|
@ -228,34 +246,35 @@ class StoryMenuState extends MusicBeatState
|
||||||
isLevelUnlocked = currentLevel == null ? false : currentLevel.isUnlocked();
|
isLevelUnlocked = currentLevel == null ? false : currentLevel.isUnlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildDifficultySprite():Void
|
function buildDifficultySprite(?diff:String):Void
|
||||||
{
|
{
|
||||||
|
if (diff == null) diff = currentDifficultyId;
|
||||||
remove(difficultySprite);
|
remove(difficultySprite);
|
||||||
difficultySprite = difficultySprites.get(currentDifficultyId);
|
difficultySprite = difficultySprites.get(diff);
|
||||||
if (difficultySprite == null)
|
if (difficultySprite == null)
|
||||||
{
|
{
|
||||||
difficultySprite = new FlxSprite(leftDifficultyArrow.x + leftDifficultyArrow.width + 10, leftDifficultyArrow.y);
|
difficultySprite = new FlxSprite(leftDifficultyArrow.x + leftDifficultyArrow.width + 10, leftDifficultyArrow.y);
|
||||||
|
|
||||||
if (Assets.exists(Paths.file('images/storymenu/difficulties/${currentDifficultyId}.xml')))
|
if (Assets.exists(Paths.file('images/storymenu/difficulties/${diff}.xml')))
|
||||||
{
|
{
|
||||||
difficultySprite.frames = Paths.getSparrowAtlas('storymenu/difficulties/${currentDifficultyId}');
|
difficultySprite.frames = Paths.getSparrowAtlas('storymenu/difficulties/${diff}');
|
||||||
difficultySprite.animation.addByPrefix('idle', 'idle0', 24, true);
|
difficultySprite.animation.addByPrefix('idle', 'idle0', 24, true);
|
||||||
difficultySprite.animation.play('idle');
|
difficultySprite.animation.play('idle');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
difficultySprite.loadGraphic(Paths.image('storymenu/difficulties/${currentDifficultyId}'));
|
difficultySprite.loadGraphic(Paths.image('storymenu/difficulties/${diff}'));
|
||||||
}
|
}
|
||||||
|
|
||||||
difficultySprites.set(currentDifficultyId, difficultySprite);
|
difficultySprites.set(diff, difficultySprite);
|
||||||
|
|
||||||
difficultySprite.x += (difficultySprites.get('normal').width - difficultySprite.width) / 2;
|
difficultySprite.x += (difficultySprites.get(Constants.DEFAULT_DIFFICULTY).width - difficultySprite.width) / 2;
|
||||||
}
|
}
|
||||||
difficultySprite.alpha = 0;
|
difficultySprite.alpha = 0;
|
||||||
|
|
||||||
difficultySprite.y = leftDifficultyArrow.y - 15;
|
difficultySprite.y = leftDifficultyArrow.y - 15;
|
||||||
var targetY:Float = leftDifficultyArrow.y + 10;
|
var targetY:Float = leftDifficultyArrow.y + 10;
|
||||||
targetY -= (difficultySprite.height - difficultySprites.get('normal').height) / 2;
|
targetY -= (difficultySprite.height - difficultySprites.get(Constants.DEFAULT_DIFFICULTY).height) / 2;
|
||||||
FlxTween.tween(difficultySprite, {y: targetY, alpha: 1}, 0.07);
|
FlxTween.tween(difficultySprite, {y: targetY, alpha: 1}, 0.07);
|
||||||
|
|
||||||
add(difficultySprite);
|
add(difficultySprite);
|
||||||
|
@ -399,6 +418,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
var previousLevelId:String = currentLevelId;
|
var previousLevelId:String = currentLevelId;
|
||||||
currentLevelId = levelList[currentIndex];
|
currentLevelId = levelList[currentIndex];
|
||||||
|
rememberedLevelId = currentLevelId;
|
||||||
|
|
||||||
updateData();
|
updateData();
|
||||||
|
|
||||||
|
@ -442,6 +462,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
var hasChanged:Bool = currentDifficultyId != difficultyList[currentIndex];
|
var hasChanged:Bool = currentDifficultyId != difficultyList[currentIndex];
|
||||||
currentDifficultyId = difficultyList[currentIndex];
|
currentDifficultyId = difficultyList[currentIndex];
|
||||||
|
rememberedDifficulty = currentDifficultyId;
|
||||||
|
|
||||||
if (difficultyList.length <= 1)
|
if (difficultyList.length <= 1)
|
||||||
{
|
{
|
||||||
|
@ -523,6 +544,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
|
|
||||||
PlayStatePlaylist.campaignId = currentLevel.id;
|
PlayStatePlaylist.campaignId = currentLevel.id;
|
||||||
PlayStatePlaylist.campaignTitle = currentLevel.getTitle();
|
PlayStatePlaylist.campaignTitle = currentLevel.getTitle();
|
||||||
|
PlayStatePlaylist.campaignDifficulty = currentDifficultyId;
|
||||||
|
|
||||||
if (targetSong != null)
|
if (targetSong != null)
|
||||||
{
|
{
|
||||||
|
@ -538,7 +560,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
LoadingState.loadAndSwitchState(new PlayState(
|
LoadingState.loadAndSwitchState(new PlayState(
|
||||||
{
|
{
|
||||||
targetSong: targetSong,
|
targetSong: targetSong,
|
||||||
targetDifficulty: currentDifficultyId,
|
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||||
}), true);
|
}), true);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import flixel.util.FlxColor;
|
||||||
import lime.app.Application;
|
import lime.app.Application;
|
||||||
import funkin.data.song.SongData.SongTimeFormat;
|
import funkin.data.song.SongData.SongTimeFormat;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A store of unchanging, globally relevant values.
|
||||||
|
*/
|
||||||
class Constants
|
class Constants
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -118,11 +121,21 @@ class Constants
|
||||||
*/
|
*/
|
||||||
public static final DEFAULT_DIFFICULTY:String = 'normal';
|
public static final DEFAULT_DIFFICULTY:String = 'normal';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default list of difficulties for charts.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_DIFFICULTY_LIST:Array<String> = ['easy', 'normal', 'hard'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default player character for charts.
|
* Default player character for charts.
|
||||||
*/
|
*/
|
||||||
public static final DEFAULT_CHARACTER:String = 'bf';
|
public static final DEFAULT_CHARACTER:String = 'bf';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default player character for health icons.
|
||||||
|
*/
|
||||||
|
public static final DEFAULT_HEALTH_ICON:String = 'face';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default stage for charts.
|
* Default stage for charts.
|
||||||
*/
|
*/
|
||||||
|
|
44
source/funkin/util/FlxGamepadUtil.hx
Normal file
44
source/funkin/util/FlxGamepadUtil.hx
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package funkin.util;
|
||||||
|
|
||||||
|
import flixel.input.gamepad.FlxGamepad;
|
||||||
|
import flixel.input.gamepad.FlxGamepadInputID;
|
||||||
|
import lime.ui.Gamepad as LimeGamepad;
|
||||||
|
import lime.ui.GamepadAxis as LimeGamepadAxis;
|
||||||
|
import lime.ui.GamepadButton as LimeGamepadButton;
|
||||||
|
|
||||||
|
class FlxGamepadUtil
|
||||||
|
{
|
||||||
|
public static function getInputID(gamepad:FlxGamepad, button:LimeGamepadButton):FlxGamepadInputID
|
||||||
|
{
|
||||||
|
#if FLX_GAMEINPUT_API
|
||||||
|
// FLX_GAMEINPUT_API internally assigns 6 axes to IDs 0-5, which LimeGamepadButton doesn't account for, so we need to offset the ID by 6.
|
||||||
|
final OFFSET:Int = 6;
|
||||||
|
#else
|
||||||
|
final OFFSET:Int = 0;
|
||||||
|
#end
|
||||||
|
|
||||||
|
var result:FlxGamepadInputID = gamepad.mapping.getID(button + OFFSET);
|
||||||
|
if (result == NONE) return NONE;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getLimeGamepad(input:FlxGamepad):Null<LimeGamepad>
|
||||||
|
{
|
||||||
|
#if FLX_GAMEINPUT_API @:privateAccess
|
||||||
|
return input._device.getLimeGamepad();
|
||||||
|
#else
|
||||||
|
return null;
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
@:privateAccess
|
||||||
|
public static function getFlxGamepadByLimeGamepad(gamepad:LimeGamepad):FlxGamepad
|
||||||
|
{
|
||||||
|
// Why is this so elaborate?
|
||||||
|
@:privateAccess
|
||||||
|
var gameInputDevice:openfl.ui.GameInputDevice = openfl.ui.GameInput.__getDevice(gamepad);
|
||||||
|
@:privateAccess
|
||||||
|
var gamepadIndex:Int = FlxG.gamepads.findGamepadIndex(gameInputDevice);
|
||||||
|
return FlxG.gamepads.getByID(gamepadIndex);
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,13 @@ class ArrayTools
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function pushUnique<T>(array:Array<T>, element:T):Bool
|
||||||
|
{
|
||||||
|
if (array.contains(element)) return false;
|
||||||
|
array.push(element);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the first element of the array that satisfies the predicate, or null if none do.
|
* Return the first element of the array that satisfies the predicate, or null if none do.
|
||||||
* @param input The array to search
|
* @param input The array to search
|
||||||
|
@ -38,6 +45,34 @@ class ArrayTools
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the index of the first element of the array that satisfies the predicate, or `-1` if none do.
|
||||||
|
* @param input The array to search
|
||||||
|
* @param predicate The predicate to call
|
||||||
|
* @return The index of the result
|
||||||
|
*/
|
||||||
|
public static function findIndex<T>(input:Array<T>, predicate:T->Bool):Int
|
||||||
|
{
|
||||||
|
for (index in 0...input.length)
|
||||||
|
{
|
||||||
|
if (predicate(input[index])) return index;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Push an element to the array if it is not already present.
|
||||||
|
* @param input The array to push to
|
||||||
|
* @param element The element to push
|
||||||
|
* @return Whether the element was pushed
|
||||||
|
*/
|
||||||
|
public static function pushUnique<T>(input:Array<T>, element:T):Bool
|
||||||
|
{
|
||||||
|
if (input.contains(element)) return false;
|
||||||
|
input.push(element);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all elements from the array, without creating a new array.
|
* Remove all elements from the array, without creating a new array.
|
||||||
* @param array The array to clear.
|
* @param array The array to clear.
|
||||||
|
|
Loading…
Reference in a new issue