mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-07-04 17:56:24 +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:
|
||||
path: .haxelib
|
||||
key: ${{ runner.os }}-hmm-${{ hashFiles('**/hmm.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-hmm-
|
||||
- if: ${{ steps.cache-hmm.outputs.cache-hit != 'true' }}
|
||||
name: hmm install
|
||||
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 }}
|
||||
- uses: ./.github/actions/setup-haxeshit
|
||||
- name: Make HXCPP cache dir
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p ${{ runner.temp }}\\hxcpp_cache
|
||||
mkdir -p ${{ runner.temp }}\hxcpp_cache
|
||||
- name: Restore build cache
|
||||
id: cache-build-win
|
||||
uses: actions/cache@v3
|
||||
|
@ -63,10 +62,8 @@ jobs:
|
|||
path: |
|
||||
.haxelib
|
||||
export
|
||||
${{ runner.temp }}\\hxcpp_cache
|
||||
key: ${{ runner.os }}-build-win-${{ github.ref_name }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-win-
|
||||
${{ runner.temp }}\hxcpp_cache
|
||||
key: ${{ runner.os }}-build-win-${{ github.ref_name }}-${{ hashFiles('**/hmm.json') }}
|
||||
- name: Build game
|
||||
run: |
|
||||
haxelib run lime build windows -release -DNO_REDIRECT_ASSETS_FOLDER
|
||||
|
|
1
.gitmodules
vendored
1
.gitmodules
vendored
|
@ -1,6 +1,7 @@
|
|||
[submodule "assets"]
|
||||
path = assets
|
||||
url = https://github.com/FunkinCrew/Funkin-history-rewrite-assets
|
||||
branch = master
|
||||
[submodule "art"]
|
||||
path = 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",
|
||||
"type": "haxe-eval",
|
||||
"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",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "f195121ebec688b417e38ab115185c8d93c349d3",
|
||||
"ref": "737b86f121cdc90358d59e2e527934f267c94a2c",
|
||||
"url": "https://github.com/EliteMasterEric/lime"
|
||||
},
|
||||
{
|
||||
|
@ -139,7 +139,7 @@
|
|||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "de9395d2f367a80f93f082e1b639b9cde2258bf1",
|
||||
"ref": "f229d76361c7e31025a048fe7909847f75bb5d5e",
|
||||
"url": "https://github.com/EliteMasterEric/openfl"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -85,6 +85,13 @@ class Main extends Sprite
|
|||
|
||||
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.
|
||||
Save.load();
|
||||
|
||||
|
@ -93,15 +100,6 @@ class Main extends Sprite
|
|||
#if hxcpp_debug_server
|
||||
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
|
||||
#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
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
|
||||
package funkin;
|
||||
|
||||
import flixel.input.gamepad.FlxGamepad;
|
||||
import flixel.util.FlxDirectionFlags;
|
||||
import flixel.FlxObject;
|
||||
import flixel.input.FlxInput;
|
||||
|
@ -832,6 +834,14 @@ class Controls extends FlxActionSet
|
|||
fromSaveData(padData, Gamepad(id));
|
||||
}
|
||||
|
||||
public function getGamepadIds():Array<Int> {
|
||||
return gamepadsAdded;
|
||||
}
|
||||
|
||||
public function getGamepads():Array<FlxGamepad> {
|
||||
return [for (id in gamepadsAdded) FlxG.gamepads.getByID(id)];
|
||||
}
|
||||
|
||||
inline function addGamepadLiteral(id:Int, ?buttonMap:Map<Control, Array<FlxGamepadInputID>>):Void
|
||||
{
|
||||
gamepadsAdded.push(id);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin;
|
||||
|
||||
import funkin.play.song.Song;
|
||||
import flash.text.TextField;
|
||||
import flixel.addons.display.FlxGridOverlay;
|
||||
import flixel.addons.transition.FlxTransitionableState;
|
||||
|
@ -48,10 +49,13 @@ import lime.utils.Assets;
|
|||
|
||||
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 curDifficulty:Int = 1;
|
||||
var currentDifficulty:String = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
var fp:FreeplayScore;
|
||||
var txtCompletion:FlxText;
|
||||
|
@ -60,7 +64,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
var lerpScore:Float = 0;
|
||||
var intendedScore:Int = 0;
|
||||
|
||||
var grpDifficulties:FlxSpriteGroup;
|
||||
var grpDifficulties:FlxTypedSpriteGroup<DifficultySprite>;
|
||||
|
||||
var coolColors:Array<Int> = [
|
||||
0xff9271fd,
|
||||
|
@ -85,6 +89,10 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
var stickerSubState:StickerSubState;
|
||||
|
||||
//
|
||||
static var rememberedDifficulty:Null<String> = "normal";
|
||||
static var rememberedSongId:Null<String> = null;
|
||||
|
||||
public function new(?stickers:StickerSubState = null)
|
||||
{
|
||||
if (stickers != null)
|
||||
|
@ -130,14 +138,23 @@ class FreeplayState extends MusicBeatSubState
|
|||
songs.push(null);
|
||||
|
||||
// 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 char = metadata.playData.characters.opponent;
|
||||
var songName = metadata.songName;
|
||||
addSong(songId, songName, coolWeek, char);
|
||||
var song:Song = SongRegistry.instance.fetchEntry(songId);
|
||||
var songBaseDifficulty:SongDifficulty = song.getDifficulty(Constants.DEFAULT_DIFFICULTY);
|
||||
|
||||
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>();
|
||||
add(grpCapsules);
|
||||
|
||||
grpDifficulties = new FlxSpriteGroup(-300, 80);
|
||||
grpDifficulties = new FlxTypedSpriteGroup<DifficultySprite>(-300, 80);
|
||||
add(grpDifficulties);
|
||||
|
||||
exitMovers.set([grpDifficulties],
|
||||
|
@ -293,15 +310,22 @@ class FreeplayState extends MusicBeatSubState
|
|||
wait: 0
|
||||
});
|
||||
|
||||
grpDifficulties.add(new FlxSprite().loadGraphic(Paths.image('freeplay/freeplayEasy')));
|
||||
grpDifficulties.add(new FlxSprite().loadGraphic(Paths.image('freeplay/freeplayNorm')));
|
||||
grpDifficulties.add(new FlxSprite().loadGraphic(Paths.image('freeplay/freeplayHard')));
|
||||
for (diffId in diffIdsTotal)
|
||||
{
|
||||
var diffSprite:DifficultySprite = new DifficultySprite(diffId);
|
||||
diffSprite.difficultyId = diffId;
|
||||
grpDifficulties.add(diffSprite);
|
||||
}
|
||||
|
||||
grpDifficulties.group.forEach(function(spr) {
|
||||
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"));
|
||||
albumArt.visible = false;
|
||||
|
@ -574,15 +598,12 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
FlxG.console.registerFunction("changeSelection", changeSelection);
|
||||
|
||||
rememberSelection();
|
||||
|
||||
changeSelection();
|
||||
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 touchX:Float = 0;
|
||||
var dxTouch:Float = 0;
|
||||
|
@ -861,28 +882,24 @@ class FreeplayState extends MusicBeatSubState
|
|||
{
|
||||
touchTimer = 0;
|
||||
|
||||
curDifficulty += change;
|
||||
var currentDifficultyIndex = diffIdsCurrent.indexOf(currentDifficulty);
|
||||
|
||||
if (curDifficulty < 0) curDifficulty = 2;
|
||||
if (curDifficulty > 2) curDifficulty = 0;
|
||||
if (currentDifficultyIndex == -1) currentDifficultyIndex = diffIdsCurrent.indexOf(Constants.DEFAULT_DIFFICULTY);
|
||||
|
||||
var targetDifficulty:String = switch (curDifficulty)
|
||||
{
|
||||
case 0:
|
||||
'easy';
|
||||
case 1:
|
||||
'normal';
|
||||
case 2:
|
||||
'hard';
|
||||
default: 'normal';
|
||||
};
|
||||
currentDifficultyIndex += change;
|
||||
|
||||
if (currentDifficultyIndex < 0) currentDifficultyIndex = diffIdsCurrent.length - 1;
|
||||
if (currentDifficultyIndex >= diffIdsCurrent.length) currentDifficultyIndex = 0;
|
||||
|
||||
currentDifficulty = diffIdsCurrent[currentDifficultyIndex];
|
||||
|
||||
var daSong = songs[curSelected];
|
||||
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;
|
||||
intendedCompletion = songScore?.accuracy ?? 0.0;
|
||||
rememberedDifficulty = currentDifficulty;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -890,19 +907,31 @@ class FreeplayState extends MusicBeatSubState
|
|||
intendedCompletion = 0.0;
|
||||
}
|
||||
|
||||
grpDifficulties.group.forEach(function(spr) {
|
||||
spr.visible = false;
|
||||
grpDifficulties.group.forEach(function(diffSprite) {
|
||||
diffSprite.visible = false;
|
||||
});
|
||||
|
||||
var curShit:FlxSprite = grpDifficulties.group.members[curDifficulty];
|
||||
|
||||
curShit.visible = true;
|
||||
curShit.offset.y += 5;
|
||||
curShit.alpha = 0.5;
|
||||
new FlxTimer().start(1 / 24, function(swag) {
|
||||
curShit.alpha = 1;
|
||||
curShit.updateHitbox();
|
||||
});
|
||||
for (diffSprite in grpDifficulties.group.members)
|
||||
{
|
||||
if (diffSprite == null) continue;
|
||||
if (diffSprite.difficultyId == currentDifficulty)
|
||||
{
|
||||
if (change != 0)
|
||||
{
|
||||
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)
|
||||
|
@ -950,35 +979,9 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
PlayStatePlaylist.isStoryMode = false;
|
||||
|
||||
if (cap.songData == null)
|
||||
{
|
||||
trace('[WARN] Failure while trying to load song!');
|
||||
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';
|
||||
}
|
||||
var songId:String = cap.songTitle.toLowerCase();
|
||||
var targetSong:Song = SongRegistry.instance.fetchEntry(songId);
|
||||
var targetDifficulty:String = currentDifficulty;
|
||||
|
||||
// TODO: Implement Pico into the interface properly.
|
||||
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)
|
||||
{
|
||||
// NGio.logEvent('Fresh');
|
||||
|
@ -1038,11 +1057,16 @@ class FreeplayState extends MusicBeatSubState
|
|||
var songScore:SaveScoreData = Save.get().getSongScore(daSongCapsule.songData.songId, targetDifficulty);
|
||||
intendedScore = songScore?.score ?? 0;
|
||||
intendedCompletion = songScore?.accuracy ?? 0.0;
|
||||
diffIdsCurrent = daSong.songDifficulties;
|
||||
rememberedSongId = daSong.songId;
|
||||
changeDiff();
|
||||
}
|
||||
else
|
||||
{
|
||||
intendedScore = 0;
|
||||
intendedCompletion = 0.0;
|
||||
rememberedSongId = null;
|
||||
rememberedDifficulty = null;
|
||||
}
|
||||
|
||||
for (index => capsule in grpCapsules.members)
|
||||
|
@ -1140,19 +1164,21 @@ enum abstract FilterType(String)
|
|||
|
||||
class FreeplaySongData
|
||||
{
|
||||
public var isFav:Bool = false;
|
||||
|
||||
public var songId:String = "";
|
||||
public var songName:String = "";
|
||||
public var levelId: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.songName = songName;
|
||||
this.levelId = levelId;
|
||||
this.songCharacter = songCharacter;
|
||||
this.isFav = isFav;
|
||||
this.songDifficulties = songDifficulties;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1163,3 +1189,17 @@ typedef MoveData =
|
|||
var ?speed: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
|
||||
// Load player options from save data.
|
||||
PreferencesMenu.initPrefs();
|
||||
Preferences.init();
|
||||
// Load controls from save data.
|
||||
PlayerSettings.init();
|
||||
|
||||
|
|
|
@ -13,23 +13,13 @@ import flixel.input.touch.FlxTouch;
|
|||
import flixel.text.FlxText;
|
||||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxColor;
|
||||
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.MenuList.MenuItem;
|
||||
import funkin.ui.MenuList;
|
||||
import funkin.ui.title.TitleState;
|
||||
import funkin.ui.story.StoryMenuState;
|
||||
import funkin.ui.OptionsState;
|
||||
import funkin.ui.PreferencesMenu;
|
||||
import funkin.ui.Prompt;
|
||||
import funkin.util.WindowUtil;
|
||||
import lime.app.Application;
|
||||
import openfl.filters.ShaderFilter;
|
||||
#if discord_rpc
|
||||
import Discord.DiscordClient;
|
||||
#end
|
||||
|
@ -82,8 +72,10 @@ class MainMenuState extends MusicBeatState
|
|||
magenta.y = bg.y;
|
||||
magenta.visible = false;
|
||||
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>();
|
||||
add(menuItems);
|
||||
|
@ -116,7 +108,7 @@ class MainMenuState extends MusicBeatState
|
|||
#end
|
||||
|
||||
createMenuItem('options', 'mainmenu/options', function() {
|
||||
startExitState(new OptionsState());
|
||||
startExitState(new funkin.ui.OptionsState());
|
||||
});
|
||||
|
||||
// Reset position of menu items.
|
||||
|
|
|
@ -16,17 +16,18 @@ class PauseSubState extends MusicBeatSubState
|
|||
{
|
||||
var grpMenuShit:FlxTypedGroup<Alphabet>;
|
||||
|
||||
var pauseOptionsBase:Array<String> = [
|
||||
final pauseOptionsBase:Array<String> = [
|
||||
'Resume',
|
||||
'Restart Song',
|
||||
'Change Difficulty',
|
||||
'Toggle Practice Mode',
|
||||
'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 curSelected:Int = 0;
|
||||
|
@ -48,6 +49,12 @@ class PauseSubState extends MusicBeatSubState
|
|||
this.isChartingMode = isChartingMode;
|
||||
|
||||
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')
|
||||
{
|
||||
|
@ -201,18 +208,6 @@ class PauseSubState extends MusicBeatSubState
|
|||
menuItems = pauseOptionsDifficulty;
|
||||
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':
|
||||
PlayState.instance.isPracticeMode = true;
|
||||
practiceText.visible = PlayState.instance.isPracticeMode;
|
||||
|
@ -245,8 +240,32 @@ class PauseSubState extends MusicBeatSubState
|
|||
|
||||
case 'Exit to Chart Editor':
|
||||
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!
|
||||
|
||||
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.controls = new Controls('player$id', None);
|
||||
|
||||
addKeyboard();
|
||||
}
|
||||
|
||||
function addKeyboard():Void
|
||||
{
|
||||
var useDefault = true;
|
||||
if (Save.get().hasControls(id, Keys))
|
||||
{
|
||||
|
@ -96,7 +101,6 @@ class PlayerSettings
|
|||
controls.setKeyboardScheme(Solo);
|
||||
}
|
||||
|
||||
// Apply loaded settings.
|
||||
PreciseInputManager.instance.initializeKeys(controls);
|
||||
}
|
||||
|
||||
|
@ -124,6 +128,7 @@ class PlayerSettings
|
|||
trace("Loading gamepad control scheme");
|
||||
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.math.FlxMath;
|
||||
import flixel.sound.FlxSound;
|
||||
import funkin.ui.PreferencesMenu.CheckboxThingie;
|
||||
|
||||
using Lambda;
|
||||
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
package funkin.data;
|
||||
|
||||
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.LegacyNoteSection;
|
||||
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.
|
||||
|
@ -23,7 +25,8 @@ class DataParse
|
|||
* `@:jcustomparse(funkin.data.DataParse.stringNotEmpty)`
|
||||
* @param json Contains the `pos` and `value` 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
|
||||
{
|
||||
|
@ -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.
|
||||
* @param json
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package funkin.data;
|
||||
|
||||
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.
|
||||
|
@ -9,9 +11,30 @@ import funkin.util.SerializerUtil;
|
|||
*/
|
||||
class DataWrite
|
||||
{
|
||||
/**
|
||||
* `@:jcustomwrite(funkin.data.DataWrite.dynamicValue)`
|
||||
* @param value
|
||||
* @return String
|
||||
*/
|
||||
public static function dynamicValue(value:Dynamic):String
|
||||
{
|
||||
// Is this cheating? Yes. Do I care? No.
|
||||
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)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:default("Unknown")
|
||||
|
@ -203,6 +205,8 @@ class SongMusicData
|
|||
*
|
||||
*/
|
||||
// @:default(funkin.data.song.SongRegistry.SONG_METADATA_VERSION)
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:default("Unknown")
|
||||
|
@ -367,6 +371,8 @@ class SongCharacterData
|
|||
class SongChartData
|
||||
{
|
||||
@: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 scrollSpeed:Map<String, Float>;
|
||||
|
|
|
@ -246,7 +246,8 @@ class SongDataUtils
|
|||
|
||||
typedef SongClipboardItems =
|
||||
{
|
||||
?valid:Bool,
|
||||
notes:Array<SongNoteData>,
|
||||
events:Array<SongEventData>
|
||||
@:optional
|
||||
var valid:Bool;
|
||||
var notes:Array<SongNoteData>;
|
||||
var events:Array<SongEventData>;
|
||||
}
|
||||
|
|
|
@ -24,6 +24,8 @@ class SongMetadata_v2_0_0
|
|||
// ==========
|
||||
// UNMODIFIED VALUES
|
||||
// ==========
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
public var version:Version;
|
||||
|
||||
@:default("Unknown")
|
||||
|
|
|
@ -4,6 +4,7 @@ package;
|
|||
// Only import these when we aren't in a macro.
|
||||
import funkin.util.Constants;
|
||||
import funkin.Paths;
|
||||
import funkin.Preferences;
|
||||
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.
|
||||
|
||||
// These are great.
|
||||
|
|
|
@ -1,18 +1,25 @@
|
|||
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.input.FlxInput;
|
||||
import flixel.input.FlxInput.FlxInputState;
|
||||
import flixel.input.FlxKeyManager;
|
||||
import flixel.input.gamepad.FlxGamepad;
|
||||
import flixel.input.gamepad.FlxGamepadInputID;
|
||||
import flixel.input.keyboard.FlxKey;
|
||||
import flixel.input.keyboard.FlxKeyboard.FlxKeyInput;
|
||||
import flixel.input.keyboard.FlxKeyList;
|
||||
import flixel.util.FlxSignal.FlxTypedSignal;
|
||||
import funkin.play.notes.NoteDirection;
|
||||
import funkin.util.FlxGamepadUtil;
|
||||
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.KeyModifier;
|
||||
import openfl.events.KeyboardEvent;
|
||||
import openfl.ui.Keyboard;
|
||||
|
||||
/**
|
||||
* A precise input manager that:
|
||||
|
@ -43,6 +50,20 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
|
@ -53,15 +74,32 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
|||
*/
|
||||
var _dirReleaseTimestamps:Map<NoteDirection, Int64>;
|
||||
|
||||
var _deviceBinds:Map<FlxGamepad,
|
||||
{
|
||||
onButtonDown:LimeGamepadButton->Int64->Void,
|
||||
onButtonUp:LimeGamepadButton->Int64->Void
|
||||
}>;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super(PreciseInputList.new);
|
||||
|
||||
_deviceBinds = [];
|
||||
|
||||
_keyList = [];
|
||||
_dirPressTimestamps = new Map<NoteDirection, Int64>();
|
||||
_dirReleaseTimestamps = new Map<NoteDirection, Int64>();
|
||||
// _keyListMap
|
||||
// _keyListArray
|
||||
_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_UP, onKeyUp);
|
||||
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.
|
||||
*/
|
||||
|
@ -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.
|
||||
* @param noteDirection The note direction to check.
|
||||
|
@ -165,11 +251,41 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
|||
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
|
||||
{
|
||||
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
|
||||
{
|
||||
var key:FlxKey = convertKeyCode(keyCode);
|
||||
|
@ -198,7 +314,7 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
|||
if (_keyList.indexOf(key) == -1) return;
|
||||
|
||||
// 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;
|
||||
|
||||
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
|
||||
{
|
||||
@:privateAccess
|
||||
|
@ -228,6 +392,31 @@ class PreciseInputManager extends FlxKeyManager<FlxKey, PreciseInputList>
|
|||
_keyListMap.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
|
||||
|
|
|
@ -11,7 +11,6 @@ import funkin.modding.events.ScriptEvent;
|
|||
import funkin.modding.events.ScriptEventDispatcher;
|
||||
import funkin.play.PlayState;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
import funkin.ui.PreferencesMenu;
|
||||
|
||||
/**
|
||||
* 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.x = boyfriend.getGraphicMidpoint().x;
|
||||
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
|
||||
var offsets:Array<Float> = boyfriend.getDeathCameraOffsets();
|
||||
cameraFollowPoint.x += offsets[0];
|
||||
cameraFollowPoint.y += offsets[1];
|
||||
add(cameraFollowPoint);
|
||||
|
||||
FlxG.camera.target = null;
|
||||
|
@ -292,7 +294,7 @@ class GameOverSubState extends MusicBeatSubState
|
|||
{
|
||||
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() {
|
||||
// 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);`
|
||||
* @author MasterEric
|
||||
*/
|
||||
@:nullSafety
|
||||
class HealthIcon extends FlxSprite
|
||||
{
|
||||
/**
|
||||
* The character this icon is representing.
|
||||
* 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.
|
||||
|
@ -123,13 +124,12 @@ class HealthIcon extends FlxSprite
|
|||
initTargetSize();
|
||||
}
|
||||
|
||||
function set_characterId(value:String):String
|
||||
function set_characterId(value:Null<String>):Null<String>
|
||||
{
|
||||
if (value == characterId) return value;
|
||||
|
||||
characterId = value;
|
||||
loadCharacter(characterId);
|
||||
return value;
|
||||
characterId = value ?? Constants.DEFAULT_HEALTH_ICON;
|
||||
return characterId;
|
||||
}
|
||||
|
||||
function set_isPixel(value:Bool):Bool
|
||||
|
@ -137,8 +137,7 @@ class HealthIcon extends FlxSprite
|
|||
if (value == isPixel) return value;
|
||||
|
||||
isPixel = value;
|
||||
loadCharacter(characterId);
|
||||
return value;
|
||||
return isPixel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -341,12 +372,17 @@ class HealthIcon extends FlxSprite
|
|||
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')))
|
||||
{
|
||||
FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
|
||||
return 'face';
|
||||
return Constants.DEFAULT_HEALTH_ICON;
|
||||
}
|
||||
|
||||
return charId;
|
||||
|
@ -357,10 +393,11 @@ class HealthIcon extends FlxSprite
|
|||
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);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -698,7 +698,15 @@ class PlayState extends MusicBeatSubState
|
|||
FlxG.sound.music.pause();
|
||||
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.time = 0;
|
||||
|
||||
|
@ -920,7 +928,6 @@ class PlayState extends MusicBeatSubState
|
|||
}
|
||||
|
||||
// Handle keybinds.
|
||||
// if (!isInCutscene && !disableKeys) keyShit(true);
|
||||
processInputQueue();
|
||||
if (!isInCutscene && !disableKeys) debugKeyShit();
|
||||
if (isInCutscene && !disableKeys) handleCutsceneKeys(elapsed);
|
||||
|
@ -1268,7 +1275,7 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
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.screenCenter(X);
|
||||
healthBarBG.scrollFactor.set(0, 0);
|
||||
|
@ -1477,13 +1484,13 @@ class PlayState extends MusicBeatSubState
|
|||
// 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 - 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.cameras = [camHUD];
|
||||
|
||||
// Position the opponent strumline on the left half of the screen
|
||||
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.cameras = [camHUD];
|
||||
|
||||
|
@ -1642,7 +1649,7 @@ class PlayState extends MusicBeatSubState
|
|||
*/
|
||||
function onConversationComplete():Void
|
||||
{
|
||||
isInCutscene = true;
|
||||
isInCutscene = false;
|
||||
remove(currentConversation);
|
||||
currentConversation = null;
|
||||
|
||||
|
@ -2464,9 +2471,9 @@ class PlayState extends MusicBeatSubState
|
|||
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
|
||||
NGio.postScore(score, 'Level ${PlayStatePlaylist.campaignId}');
|
||||
#end
|
||||
|
@ -2514,7 +2521,7 @@ class PlayState extends MusicBeatSubState
|
|||
var nextPlayState:PlayState = new PlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: currentDifficulty,
|
||||
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||
targetCharacter: currentPlayerId,
|
||||
});
|
||||
nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y);
|
||||
|
@ -2530,7 +2537,7 @@ class PlayState extends MusicBeatSubState
|
|||
var nextPlayState:PlayState = new PlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: currentDifficulty,
|
||||
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||
targetCharacter: currentPlayerId,
|
||||
});
|
||||
nextPlayState.previousCameraFollowPoint = new FlxSprite(cameraFollowPoint.x, cameraFollowPoint.y);
|
||||
|
@ -2656,7 +2663,12 @@ class PlayState extends MusicBeatSubState
|
|||
persistentUpdate = false;
|
||||
vocals.stop();
|
||||
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;
|
||||
openSubState(res);
|
||||
}
|
||||
|
|
|
@ -34,10 +34,7 @@ class PlayStatePlaylist
|
|||
*/
|
||||
public static var campaignId:String = 'unknown';
|
||||
|
||||
/**
|
||||
* The current difficulty selected for this level (as a named ID).
|
||||
*/
|
||||
public static var currentDifficulty(default, default):String = Constants.DEFAULT_DIFFICULTY;
|
||||
public static var campaignDifficulty:String = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
/**
|
||||
* Resets the playlist to its default state.
|
||||
|
@ -49,6 +46,6 @@ class PlayStatePlaylist
|
|||
campaignScore = 0;
|
||||
campaignTitle = 'UNKNOWN';
|
||||
campaignId = 'unknown';
|
||||
currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
campaignDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ import flxanimate.FlxAnimate.Settings;
|
|||
|
||||
class ResultState extends MusicBeatSubState
|
||||
{
|
||||
final params:ResultsStateParams;
|
||||
|
||||
var resultsVariation:ResultVariations;
|
||||
var songName:FlxBitmapText;
|
||||
var difficulty:FlxSprite;
|
||||
|
@ -29,13 +31,18 @@ class ResultState extends MusicBeatSubState
|
|||
var maskShaderSongName = new LeftMaskShader();
|
||||
var maskShaderDifficulty = new LeftMaskShader();
|
||||
|
||||
public function new(params:ResultsStateParams)
|
||||
{
|
||||
super();
|
||||
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
override function create():Void
|
||||
{
|
||||
if (Highscore.tallies.sick == Highscore.tallies.totalNotesHit
|
||||
&& Highscore.tallies.maxCombo == Highscore.tallies.totalNotesHit) resultsVariation = PERFECT;
|
||||
else if (Highscore.tallies.missed
|
||||
+ Highscore.tallies.bad
|
||||
+ Highscore.tallies.shit >= Highscore.tallies.totalNotes * 0.50)
|
||||
if (params.tallies.sick == params.tallies.totalNotesHit
|
||||
&& params.tallies.maxCombo == params.tallies.totalNotesHit) resultsVariation = PERFECT;
|
||||
else if (params.tallies.missed + params.tallies.bad + params.tallies.shit >= params.tallies.totalNotes * 0.50)
|
||||
resultsVariation = SHIT; // if more than half of your song was missed, bad, or shit notes, you get shit ending!
|
||||
else
|
||||
resultsVariation = NORMAL;
|
||||
|
@ -135,17 +142,7 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
var fontLetters:String = "AaBbCcDdEeFfGgHhiIJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz:1234567890";
|
||||
songName = new FlxBitmapText(FlxBitmapFont.fromMonospace(Paths.image("resultScreen/tardlingSpritesheet"), fontLetters, FlxPoint.get(49, 62)));
|
||||
|
||||
// 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.text = params.title;
|
||||
songName.letterSpacing = -15;
|
||||
songName.angle = -4.1;
|
||||
add(songName);
|
||||
|
@ -194,27 +191,27 @@ class ResultState extends MusicBeatSubState
|
|||
var ratingGrp:FlxTypedGroup<TallyCounter> = new FlxTypedGroup<TallyCounter>();
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
hStuf += 2;
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
|
@ -351,7 +348,7 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
if (controls.PAUSE)
|
||||
{
|
||||
if (PlayStatePlaylist.isStoryMode)
|
||||
if (params.storyMode)
|
||||
{
|
||||
FlxG.switchState(new StoryMenuState());
|
||||
}
|
||||
|
@ -372,3 +369,21 @@ enum abstract ResultVariations(String)
|
|||
var NORMAL;
|
||||
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;
|
||||
}
|
||||
|
||||
public function getDeathCameraOffsets():Array<Float>
|
||||
{
|
||||
return _data.death?.cameraOffsets ?? [0.0, 0.0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the value of flipX from the character data.
|
||||
* `!getFlipX()` is the direction Boyfriend should face.
|
||||
|
@ -312,12 +317,8 @@ class BaseCharacter extends Bopper
|
|||
trace('[WARN] Player 1 health icon not found!');
|
||||
return;
|
||||
}
|
||||
PlayState.instance.iconP1.isPixel = _data.healthIcon?.isPixel ?? false;
|
||||
PlayState.instance.iconP1.characterId = _data.healthIcon.id;
|
||||
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;
|
||||
PlayState.instance.iconP1.configure(_data.healthIcon);
|
||||
PlayState.instance.iconP1.flipX = !PlayState.instance.iconP1.flipX; // BF is looking the other way.
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -326,12 +327,7 @@ class BaseCharacter extends Bopper
|
|||
trace('[WARN] Player 2 health icon not found!');
|
||||
return;
|
||||
}
|
||||
PlayState.instance.iconP2.isPixel = _data.healthIcon?.isPixel ?? false;
|
||||
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;
|
||||
PlayState.instance.iconP2.configure(_data.healthIcon);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -580,8 +576,7 @@ class BaseCharacter extends Bopper
|
|||
|
||||
public override function playAnimation(name:String, restart:Bool = false, ignoreOther:Bool = false, reversed:Bool = false):Void
|
||||
{
|
||||
FlxG.watch.addQuick('playAnim(${characterName})', name);
|
||||
// trace('playAnim(${characterName}): ${name}');
|
||||
// FlxG.watch.addQuick('playAnim(${characterName})', name);
|
||||
super.playAnimation(name, restart, ignoreOther, reversed);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,8 +19,10 @@ class CharacterDataParser
|
|||
* The current version string for the stage data format.
|
||||
* Handle breaking changes by incrementing this value
|
||||
* 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.
|
||||
|
@ -603,6 +605,8 @@ typedef CharacterData =
|
|||
*/
|
||||
var healthIcon:Null<HealthIconData>;
|
||||
|
||||
var death:Null<DeathData>;
|
||||
|
||||
/**
|
||||
* The global offset to the character's position, in pixels.
|
||||
* @default [0, 0]
|
||||
|
@ -695,3 +699,13 @@ typedef HealthIconData =
|
|||
*/
|
||||
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);
|
||||
|
||||
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,
|
||||
{
|
||||
ease: FlxEase.expoIn,
|
||||
|
@ -252,7 +252,7 @@ class Strumline extends FlxSpriteGroup
|
|||
holdNotesVwoosh.add(holdNote);
|
||||
|
||||
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,
|
||||
{
|
||||
ease: FlxEase.expoIn,
|
||||
|
@ -277,7 +277,7 @@ class Strumline extends FlxSpriteGroup
|
|||
var vwoosh:Float = (strumTime < Conductor.songPosition) && vwoosh ? 2.0 : 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
|
||||
|
@ -321,7 +321,7 @@ class Strumline extends FlxSpriteGroup
|
|||
note.y = this.y - INITIAL_OFFSET + calculateNoteYPos(note.strumTime, vwoosh);
|
||||
|
||||
// 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)
|
||||
{
|
||||
killNote(note);
|
||||
|
@ -388,7 +388,7 @@ class Strumline extends FlxSpriteGroup
|
|||
|
||||
var vwoosh:Bool = false;
|
||||
|
||||
if (PreferencesMenu.getPref('downscroll'))
|
||||
if (Preferences.downscroll)
|
||||
{
|
||||
holdNote.y = this.y + calculateNoteYPos(holdNote.strumTime, vwoosh) - holdNote.height + STRUMLINE_SIZE / 2;
|
||||
}
|
||||
|
@ -410,7 +410,7 @@ class Strumline extends FlxSpriteGroup
|
|||
holdNote.visible = false;
|
||||
}
|
||||
|
||||
if (PreferencesMenu.getPref('downscroll'))
|
||||
if (Preferences.downscroll)
|
||||
{
|
||||
holdNote.y = this.y - holdNote.height + STRUMLINE_SIZE / 2;
|
||||
}
|
||||
|
@ -425,7 +425,7 @@ class Strumline extends FlxSpriteGroup
|
|||
holdNote.visible = true;
|
||||
var vwoosh:Bool = false;
|
||||
|
||||
if (PreferencesMenu.getPref('downscroll'))
|
||||
if (Preferences.downscroll)
|
||||
{
|
||||
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());
|
||||
// instead of scrollSpeed, PlayState.SONG.speed
|
||||
|
||||
flipY = PreferencesMenu.getPref('downscroll');
|
||||
flipY = Preferences.downscroll;
|
||||
|
||||
// alpha = 0.6;
|
||||
alpha = 1.0;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package funkin.play.song;
|
||||
|
||||
import funkin.util.SortUtil;
|
||||
import flixel.sound.FlxSound;
|
||||
import openfl.utils.Assets;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
|
@ -56,8 +57,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
*/
|
||||
public var validScore:Bool = true;
|
||||
|
||||
var difficultyIds:Array<String>;
|
||||
|
||||
public var songName(get, never):String;
|
||||
|
||||
function get_songName():String
|
||||
|
@ -85,7 +84,6 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
this.id = id;
|
||||
|
||||
variations = [];
|
||||
difficultyIds = [];
|
||||
difficulties = new Map<String, SongDifficulty>();
|
||||
|
||||
_data = _fetchData(id);
|
||||
|
@ -127,8 +125,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
for (vari in variations)
|
||||
result.variations.push(vari);
|
||||
|
||||
result.difficultyIds.clear();
|
||||
|
||||
result.difficulties.clear();
|
||||
result.populateDifficulties();
|
||||
|
||||
for (variation => chartData in charts)
|
||||
|
@ -157,13 +154,17 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
{
|
||||
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,
|
||||
// (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.
|
||||
for (diffId in metadata.playData.difficulties)
|
||||
{
|
||||
difficultyIds.push(diffId);
|
||||
|
||||
var difficulty:SongDifficulty = new SongDifficulty(this, diffId, 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>
|
||||
{
|
||||
if (diffId == null) diffId = difficulties.keys().array()[0];
|
||||
if (diffId == null) diffId = listDifficulties()[0];
|
||||
|
||||
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.
|
||||
naughtyness: true,
|
||||
downscroll: false,
|
||||
flashingMenu: true,
|
||||
flashingLights: true,
|
||||
zoomCamera: true,
|
||||
debugDisplay: false,
|
||||
pauseOnTabOut: true,
|
||||
autoPause: true,
|
||||
|
||||
controls:
|
||||
{
|
||||
|
@ -88,7 +88,7 @@ abstract Save(RawSaveData)
|
|||
{
|
||||
// No mods enabled.
|
||||
enabledMods: [],
|
||||
modSettings: [],
|
||||
modOptions: [],
|
||||
},
|
||||
|
||||
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.
|
||||
*/
|
||||
|
@ -407,6 +421,8 @@ typedef RawSaveData =
|
|||
/**
|
||||
* A semantic versioning string for the save data format.
|
||||
*/
|
||||
@:jcustomparse(funkin.data.DataParse.semverVersion)
|
||||
@:jcustomwrite(funkin.data.DataWrite.semverVersion)
|
||||
var version:Version;
|
||||
|
||||
var api:SaveApiData;
|
||||
|
@ -458,7 +474,7 @@ typedef SaveHighScoresData =
|
|||
typedef SaveDataMods =
|
||||
{
|
||||
var enabledMods:Array<String>;
|
||||
var modSettings:Map<String, Dynamic>;
|
||||
var modOptions:Map<String, Dynamic>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -530,10 +546,10 @@ typedef SaveDataOptions =
|
|||
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`
|
||||
*/
|
||||
var flashingMenu:Bool;
|
||||
var flashingLights:Bool;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @default `true`
|
||||
*/
|
||||
var pauseOnTabOut:Bool;
|
||||
var autoPause:Bool;
|
||||
|
||||
var controls:
|
||||
{
|
||||
|
|
|
@ -163,7 +163,17 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
|||
|
||||
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;
|
||||
canExit = false;
|
||||
|
@ -204,6 +214,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
|||
}
|
||||
|
||||
var keyUsedToEnterPrompt:Null<Int> = null;
|
||||
var buttonUsedToEnterPrompt:Null<Int> = null;
|
||||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
|
@ -246,19 +257,49 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
|
|||
case Gamepad(id):
|
||||
{
|
||||
var button = FlxG.gamepads.getByID(id).firstJustReleasedID();
|
||||
if (button != NONE && button != keyUsedToEnterPrompt)
|
||||
if (button != NONE && button != buttonUsedToEnterPrompt)
|
||||
{
|
||||
if (button != BACK) onInputSelect(button);
|
||||
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();
|
||||
if (keyJustReleased != NONE && keyJustReleased == keyUsedToEnterPrompt)
|
||||
switch (currentDevice)
|
||||
{
|
||||
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.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import funkin.ui.AtlasText.AtlasFont;
|
||||
import funkin.ui.OptionsState.Page;
|
||||
import funkin.ui.TextMenuList.TextMenuItem;
|
||||
|
||||
class PreferencesMenu extends Page
|
||||
{
|
||||
public static var preferences:Map<String, Dynamic> = new Map();
|
||||
|
||||
var items:TextMenuList;
|
||||
var preferenceItems:FlxTypedSpriteGroup<FlxSprite>;
|
||||
|
||||
var checkboxes:Array<CheckboxThingie> = [];
|
||||
var menuCamera:FlxCamera;
|
||||
var camFollow:FlxObject;
|
||||
|
||||
|
@ -27,13 +26,9 @@ class PreferencesMenu extends Page
|
|||
camera = menuCamera;
|
||||
|
||||
add(items = new TextMenuList());
|
||||
add(preferenceItems = new FlxTypedSpriteGroup<FlxSprite>());
|
||||
|
||||
createPrefItem('naughtyness', 'censor-naughty', true);
|
||||
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);
|
||||
createPrefItems();
|
||||
|
||||
camFollow = new FlxObject(FlxG.width / 2, 0, 140, 70);
|
||||
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?
|
||||
public static function setPref(pref:String, value:Dynamic):Void
|
||||
function createPrefItemCheckbox(prefName:String, prefDesc:String, onChange:Bool->Void, defaultValue:Bool):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() {
|
||||
preferenceCheck(prefString, prefValue);
|
||||
|
||||
switch (Type.typeof(prefValue).getName())
|
||||
{
|
||||
case 'TBool':
|
||||
prefToggle(prefString);
|
||||
|
||||
default:
|
||||
trace('swag');
|
||||
}
|
||||
var value = !checkbox.currentValue;
|
||||
onChange(value);
|
||||
checkbox.currentValue = value;
|
||||
});
|
||||
|
||||
switch (Type.typeof(prefValue).getName())
|
||||
{
|
||||
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') {}
|
||||
preferenceItems.add(checkbox);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
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) {
|
||||
if (items.selectedItem == daItem) daItem.x = 150;
|
||||
else
|
||||
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);
|
||||
|
||||
|
@ -180,7 +110,7 @@ class CheckboxThingie extends FlxSprite
|
|||
setGraphicSize(Std.int(width * 0.7));
|
||||
updateHitbox();
|
||||
|
||||
this.daValue = daValue;
|
||||
this.currentValue = defaultValue;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
animation.play('static');
|
||||
}
|
||||
|
||||
return value;
|
||||
return currentValue = value;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ class StickerSubState extends MusicBeatSubState
|
|||
var daSound:String = FlxG.random.getObject(sounds);
|
||||
FlxG.sound.play(Paths.sound(daSound));
|
||||
|
||||
if (ind == grpStickers.members.length - 1)
|
||||
if (grpStickers == null || ind == grpStickers.members.length - 1)
|
||||
{
|
||||
switchingState = false;
|
||||
close();
|
||||
|
|
|
@ -10,7 +10,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
|
|||
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);
|
||||
item.fireInstantly = fireInstantly;
|
||||
|
@ -20,7 +20,7 @@ class TextMenuList extends MenuTypedList<TextMenuItem>
|
|||
|
||||
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);
|
||||
setEmptyBackground();
|
||||
|
@ -29,7 +29,7 @@ class TextMenuItem extends TextTypedMenuItem<AtlasText>
|
|||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -253,6 +253,10 @@ class DebugBoundingState extends FlxState
|
|||
offsetView.add(animDropDownMenu);
|
||||
|
||||
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);
|
||||
|
||||
var charDropdown:DropDown = cast uiStuff.findComponent('characterDropdown');
|
||||
|
|
|
@ -66,7 +66,7 @@ class AddNotesCommand implements ChartEditorCommand
|
|||
state.currentEventSelection = [];
|
||||
}
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -80,7 +80,7 @@ class AddNotesCommand implements ChartEditorCommand
|
|||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
||||
state.currentNoteSelection = [];
|
||||
state.currentEventSelection = [];
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -116,7 +116,8 @@ class RemoveNotesCommand implements ChartEditorCommand
|
|||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
||||
state.currentNoteSelection = [];
|
||||
state.currentEventSelection = [];
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -133,7 +134,7 @@ class RemoveNotesCommand implements ChartEditorCommand
|
|||
}
|
||||
state.currentNoteSelection = notes;
|
||||
state.currentEventSelection = [];
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -254,7 +255,7 @@ class AddEventsCommand implements ChartEditorCommand
|
|||
state.currentEventSelection = events;
|
||||
}
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteLay'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -298,7 +299,8 @@ class RemoveEventsCommand implements ChartEditorCommand
|
|||
{
|
||||
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
|
||||
state.currentEventSelection = [];
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -314,7 +316,7 @@ class RemoveEventsCommand implements ChartEditorCommand
|
|||
state.currentSongChartEventData.push(event);
|
||||
}
|
||||
state.currentEventSelection = events;
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -354,7 +356,7 @@ class RemoveItemsCommand implements ChartEditorCommand
|
|||
state.currentNoteSelection = [];
|
||||
state.currentEventSelection = [];
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-01'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/noteErase'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -378,7 +380,7 @@ class RemoveItemsCommand implements ChartEditorCommand
|
|||
state.currentNoteSelection = notes;
|
||||
state.currentEventSelection = events;
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
state.saveDataDirty = true;
|
||||
state.noteDisplayDirty = true;
|
||||
|
@ -805,6 +807,8 @@ class PasteItemsCommand implements ChartEditorCommand
|
|||
|
||||
public function undo(state:ChartEditorState):Void
|
||||
{
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, addedNotes);
|
||||
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, addedEvents);
|
||||
state.currentNoteSelection = [];
|
||||
|
@ -857,6 +861,8 @@ class ExtendNoteLengthCommand implements ChartEditorCommand
|
|||
|
||||
public function undo(state:ChartEditorState):Void
|
||||
{
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/undo'));
|
||||
|
||||
note.length = oldLength;
|
||||
|
||||
state.saveDataDirty = true;
|
||||
|
|
|
@ -404,7 +404,6 @@ class ChartEditorDialogHandler
|
|||
{
|
||||
if (ChartEditorAudioHandler.loadInstFromBytes(state, selectedFile.bytes, instId))
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.fullPath);
|
||||
#if !mac
|
||||
NotificationManager.instance.addNotification(
|
||||
{
|
||||
|
@ -415,13 +414,12 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
state.switchToCurrentInstrumental();
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
removeDropHandler(onDropFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Failed to load instrumental (${selectedFile.fullPath})');
|
||||
|
||||
#if !mac
|
||||
NotificationManager.instance.addNotification(
|
||||
{
|
||||
|
@ -452,6 +450,7 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
state.switchToCurrentInstrumental();
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
removeDropHandler(onDropFile);
|
||||
}
|
||||
|
@ -570,6 +569,12 @@ class ChartEditorDialogHandler
|
|||
|
||||
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);
|
||||
if (inputSongName == null) throw 'Could not locate inputSongName TextField in Song Metadata dialog';
|
||||
inputSongName.onChange = function(event:UIEvent) {
|
||||
|
@ -667,8 +672,6 @@ class ChartEditorDialogHandler
|
|||
timeChanges[0].bpm = event.value;
|
||||
}
|
||||
|
||||
Conductor.forceBPM(event.value);
|
||||
|
||||
newSongMetadata.timeChanges = timeChanges;
|
||||
};
|
||||
|
||||
|
@ -677,6 +680,8 @@ class ChartEditorDialogHandler
|
|||
dialogContinue.onClick = (_event) -> {
|
||||
state.songMetadata.set(targetVariation, newSongMetadata);
|
||||
|
||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
}
|
||||
|
||||
|
@ -696,6 +701,8 @@ class ChartEditorDialogHandler
|
|||
|
||||
var charData:SongCharacterData = state.currentSongMetadata.playData.characters;
|
||||
|
||||
var hasClearedVocals:Bool = false;
|
||||
|
||||
charIdsForVocals.push(charData.player);
|
||||
charIdsForVocals.push(charData.opponent);
|
||||
|
||||
|
@ -715,6 +722,7 @@ class ChartEditorDialogHandler
|
|||
if (dialogNoVocals == null) throw 'Could not locate dialogNoVocals button in Upload Vocals dialog';
|
||||
dialogNoVocals.onClick = function(_event) {
|
||||
// Dismiss
|
||||
ChartEditorAudioHandler.stopExistingVocals(state);
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
};
|
||||
|
||||
|
@ -738,6 +746,12 @@ class ChartEditorDialogHandler
|
|||
trace('Selected file: $pathStr');
|
||||
var path:Path = new Path(pathStr);
|
||||
|
||||
if (!hasClearedVocals)
|
||||
{
|
||||
hasClearedVocals = true;
|
||||
ChartEditorAudioHandler.stopExistingVocals(state);
|
||||
}
|
||||
|
||||
if (ChartEditorAudioHandler.loadVocalsFromPath(state, path, charKey, instId))
|
||||
{
|
||||
// Tell the user the load was successful.
|
||||
|
@ -788,6 +802,11 @@ class ChartEditorDialogHandler
|
|||
if (selectedFile != null && selectedFile.bytes != null)
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.name);
|
||||
if (!hasClearedVocals)
|
||||
{
|
||||
hasClearedVocals = true;
|
||||
ChartEditorAudioHandler.stopExistingVocals(state);
|
||||
}
|
||||
if (ChartEditorAudioHandler.loadVocalsFromBytes(state, selectedFile.bytes, charKey, instId))
|
||||
{
|
||||
// Tell the user the load was successful.
|
||||
|
|
|
@ -87,9 +87,8 @@ using Lambda;
|
|||
*
|
||||
* @author MasterEric
|
||||
*/
|
||||
@:nullSafety
|
||||
// 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.ChartEditorDropdowns)
|
||||
@:allow(funkin.ui.debug.charting.ChartEditorDialogHandler)
|
||||
|
@ -555,6 +554,9 @@ class ChartEditorState extends HaxeUIState
|
|||
notePreviewDirty = true;
|
||||
notePreviewViewportBoundsDirty = true;
|
||||
|
||||
// Make sure the difficulty we selected is in the list of difficulties.
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
|
||||
return selectedDifficulty;
|
||||
}
|
||||
|
||||
|
@ -965,13 +967,14 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
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)
|
||||
{
|
||||
// Initialize to the default value if not set.
|
||||
result = [];
|
||||
trace('Initializing blank note data for difficulty ' + selectedDifficulty);
|
||||
currentSongChartData.notes.set(selectedDifficulty, result);
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
|
@ -980,6 +983,7 @@ class ChartEditorState extends HaxeUIState
|
|||
function set_currentSongChartNoteData(value:Array<SongNoteData>):Array<SongNoteData>
|
||||
{
|
||||
currentSongChartData.notes.set(selectedDifficulty, value);
|
||||
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -1391,16 +1395,12 @@ class ChartEditorState extends HaxeUIState
|
|||
healthIconDad = new HealthIcon(currentSongMetadata.playData.characters.opponent);
|
||||
healthIconDad.autoUpdate = false;
|
||||
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);
|
||||
healthIconDad.zIndex = 30;
|
||||
|
||||
healthIconBF = new HealthIcon(currentSongMetadata.playData.characters.player);
|
||||
healthIconBF.autoUpdate = false;
|
||||
healthIconBF.size.set(0.5, 0.5);
|
||||
healthIconBF.x = gridTiledSprite.x + gridTiledSprite.width + 15;
|
||||
healthIconBF.y = gridTiledSprite.y + 5;
|
||||
healthIconBF.flipX = true;
|
||||
add(healthIconBF);
|
||||
healthIconBF.zIndex = 30;
|
||||
|
@ -1627,6 +1627,12 @@ class ChartEditorState extends HaxeUIState
|
|||
addUIClickListener('playbarForward', _ -> playbarButtonPressed = 'playbarForward');
|
||||
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.
|
||||
|
||||
addUIClickListener('menubarItemNewChart', _ -> ChartEditorDialogHandler.openWelcomeDialog(this, true));
|
||||
|
@ -1897,6 +1903,16 @@ class ChartEditorState extends HaxeUIState
|
|||
handleViewKeybinds();
|
||||
handleTestKeybinds();
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
var shouldHandleCursor:Bool = !isCursorOverHaxeUI || (selectionBoxStartPos != null);
|
||||
var eventColumn:Int = (STRUMLINE_SIZE * 2 + 1) - 1;
|
||||
|
@ -2477,25 +2500,37 @@ class ChartEditorState extends HaxeUIState
|
|||
var dragLengthMs:Float = dragLengthSteps * Conductor.stepLengthMs;
|
||||
var dragLengthPixels:Float = dragLengthSteps * GRID_SIZE;
|
||||
|
||||
if (dragLengthSteps > 0)
|
||||
if (gridGhostNote != null && gridGhostNote.noteData != null && gridGhostHoldNote != null)
|
||||
{
|
||||
gridGhostHoldNote.visible = true;
|
||||
gridGhostHoldNote.noteData = gridGhostNote.noteData;
|
||||
gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
|
||||
if (dragLengthSteps > 0)
|
||||
{
|
||||
if (dragLengthCurrent != dragLengthSteps)
|
||||
{
|
||||
stretchySounds = !stretchySounds;
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/stretch' + (stretchySounds ? '1' : '2') + '_UI'));
|
||||
|
||||
gridGhostHoldNote.setHeightDirectly(dragLengthPixels);
|
||||
dragLengthCurrent = dragLengthSteps;
|
||||
}
|
||||
|
||||
gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes);
|
||||
}
|
||||
else
|
||||
{
|
||||
gridGhostHoldNote.visible = false;
|
||||
gridGhostHoldNote.visible = true;
|
||||
gridGhostHoldNote.noteData = gridGhostNote.noteData;
|
||||
gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
|
||||
|
||||
gridGhostHoldNote.setHeightDirectly(dragLengthPixels);
|
||||
|
||||
gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes);
|
||||
}
|
||||
else
|
||||
{
|
||||
gridGhostHoldNote.visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (FlxG.mouse.justReleased)
|
||||
{
|
||||
if (dragLengthSteps > 0)
|
||||
{
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/stretchSNAP_UI'));
|
||||
// Apply the new length.
|
||||
performCommand(new ExtendNoteLengthCommand(currentPlaceNoteData, dragLengthMs));
|
||||
}
|
||||
|
@ -2644,7 +2679,7 @@ class ChartEditorState extends HaxeUIState
|
|||
if (cursorColumn == eventColumn)
|
||||
{
|
||||
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()";
|
||||
|
||||
|
@ -2704,11 +2739,11 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
else
|
||||
{
|
||||
if (FlxG.mouse.overlaps(notePreview))
|
||||
if (notePreview != null && FlxG.mouse.overlaps(notePreview))
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
else if (FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
||||
else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
|
@ -3020,18 +3055,35 @@ class ChartEditorState extends HaxeUIState
|
|||
{
|
||||
if (healthIconsDirty)
|
||||
{
|
||||
if (healthIconBF != null) healthIconBF.characterId = currentSongMetadata.playData.characters.player;
|
||||
if (healthIconDad != null) healthIconDad.characterId = currentSongMetadata.playData.characters.opponent;
|
||||
var charDataBF = CharacterDataParser.fetchCharacterData(currentSongMetadata.playData.characters.player);
|
||||
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)
|
||||
{
|
||||
// Base X position to the right of the grid.
|
||||
var baseHealthIconXPos:Float = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 15);
|
||||
// Will be 0 when not bopping. When bopping, will increase to push the icon left.
|
||||
var healthIconOffset:Float = healthIconBF.width - (HealthIcon.HEALTH_ICON_SIZE * 0.5);
|
||||
healthIconBF.x = baseHealthIconXPos - healthIconOffset;
|
||||
healthIconBF.x = (gridTiledSprite == null) ? (0) : (gridTiledSprite.x + gridTiledSprite.width + 45 - (healthIconBF.width / 2));
|
||||
healthIconBF.y = (gridTiledSprite == null) ? (0) : (MENU_BAR_HEIGHT + GRID_TOP_PAD + 30 - (healthIconBF.height / 2));
|
||||
}
|
||||
|
||||
// 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
|
||||
{
|
||||
autoSave();
|
||||
stopWelcomeMusic();
|
||||
FlxG.switchState(new MainMenuState());
|
||||
}
|
||||
|
||||
|
@ -3342,6 +3395,7 @@ class ChartEditorState extends HaxeUIState
|
|||
if (!isHaxeUIDialogOpen && !isCursorOverHaxeUI && FlxG.keys.justPressed.ENTER)
|
||||
{
|
||||
var minimal = FlxG.keys.pressed.SHIFT;
|
||||
ChartEditorToolboxHandler.hideAllToolboxes(this);
|
||||
testSongInPlayState(minimal);
|
||||
}
|
||||
}
|
||||
|
@ -3656,49 +3710,41 @@ class ChartEditorState extends HaxeUIState
|
|||
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
||||
var stageId:String = currentSongMetadata.playData.stage;
|
||||
var stageData:Null<StageData> = StageDataParser.parseStageData(stageId);
|
||||
if (stageData != null)
|
||||
if (inputStage != null)
|
||||
{
|
||||
inputStage.value = {id: stageId, text: stageData.name};
|
||||
}
|
||||
else
|
||||
{
|
||||
inputStage.value = {id: "mainStage", text: "Main Stage"};
|
||||
inputStage.value = (stageData != null) ?
|
||||
{id: stageId, text: stageData.name} :
|
||||
{id: "mainStage", text: "Main Stage"};
|
||||
}
|
||||
|
||||
var inputCharacterPlayer:Null<DropDown> = toolbox.findComponent('inputCharacterPlayer', DropDown);
|
||||
var charIdPlayer:String = currentSongMetadata.playData.characters.player;
|
||||
var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdPlayer);
|
||||
if (charDataPlayer != null)
|
||||
if (inputCharacterPlayer != null)
|
||||
{
|
||||
inputCharacterPlayer.value = {id: charIdPlayer, text: charDataPlayer.name};
|
||||
}
|
||||
else
|
||||
{
|
||||
inputCharacterPlayer.value = {id: "bf", text: "Boyfriend"};
|
||||
inputCharacterPlayer.value = (charDataPlayer != null) ?
|
||||
{id: charIdPlayer, text: charDataPlayer.name} :
|
||||
{id: "bf", text: "Boyfriend"};
|
||||
}
|
||||
|
||||
var inputCharacterOpponent:Null<DropDown> = toolbox.findComponent('inputCharacterOpponent', DropDown);
|
||||
var charIdOpponent:String = currentSongMetadata.playData.characters.opponent;
|
||||
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdOpponent);
|
||||
if (charDataOpponent != null)
|
||||
if (inputCharacterOpponent != null)
|
||||
{
|
||||
inputCharacterOpponent.value = {id: charIdOpponent, text: charDataOpponent.name};
|
||||
}
|
||||
else
|
||||
{
|
||||
inputCharacterOpponent.value = {id: "dad", text: "Dad"};
|
||||
inputCharacterOpponent.value = (charDataOpponent != null) ?
|
||||
{id: charIdOpponent, text: charDataOpponent.name} :
|
||||
{id: "dad", text: "Dad"};
|
||||
}
|
||||
|
||||
var inputCharacterGirlfriend:Null<DropDown> = toolbox.findComponent('inputCharacterGirlfriend', DropDown);
|
||||
var charIdGirlfriend:String = currentSongMetadata.playData.characters.girlfriend;
|
||||
var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charIdGirlfriend);
|
||||
if (charDataGirlfriend != null)
|
||||
if (inputCharacterGirlfriend != null)
|
||||
{
|
||||
inputCharacterGirlfriend.value = {id: charIdGirlfriend, text: charDataGirlfriend.name};
|
||||
}
|
||||
else
|
||||
{
|
||||
inputCharacterGirlfriend.value = {id: "none", text: "None"};
|
||||
inputCharacterGirlfriend.value = (charDataGirlfriend != null) ?
|
||||
{id: charIdGirlfriend, text: charDataGirlfriend.name} :
|
||||
{id: "none", text: "None"};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3885,9 +3931,9 @@ class ChartEditorState extends HaxeUIState
|
|||
switch (noteData.getStrumlineIndex())
|
||||
{
|
||||
case 0: // Player
|
||||
if (hitsoundsEnabledPlayer) ChartEditorAudioHandler.playSound(Paths.sound('ui/chart-editor/playerHitsound'));
|
||||
if (hitsoundsEnabledPlayer) ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/hitNotePlayer'));
|
||||
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;
|
||||
|
||||
// Move the grid sprite to the correct position.
|
||||
if (gridTiledSprite != null)
|
||||
if (gridTiledSprite != null && gridPlayheadScrollArea != null)
|
||||
{
|
||||
if (isViewDownscroll)
|
||||
{
|
||||
|
@ -4064,7 +4110,7 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
|
||||
subStateClosed.add(fixCamera);
|
||||
subStateClosed.add(updateConductor);
|
||||
subStateClosed.add(resetConductorAfterTest);
|
||||
|
||||
FlxTransitionableState.skipNextTransIn = false;
|
||||
FlxTransitionableState.skipNextTransOut = false;
|
||||
|
@ -4097,10 +4143,9 @@ class ChartEditorState extends HaxeUIState
|
|||
add(this.component);
|
||||
}
|
||||
|
||||
function updateConductor(_:FlxSubState = null):Void
|
||||
function resetConductorAfterTest(_:FlxSubState = null):Void
|
||||
{
|
||||
var targetPos = scrollPositionInMs;
|
||||
Conductor.update(targetPos);
|
||||
moveSongToScrollPosition();
|
||||
}
|
||||
|
||||
public function postLoadInstrumental():Void
|
||||
|
@ -4153,12 +4198,13 @@ class ChartEditorState extends HaxeUIState
|
|||
*/
|
||||
function moveSongToScrollPosition():Void
|
||||
{
|
||||
// Update the songPosition in the Conductor.
|
||||
var targetPos = scrollPositionInMs;
|
||||
Conductor.update(targetPos);
|
||||
|
||||
// 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;
|
||||
|
||||
// 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
|
||||
{
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('pianoStuff/piano-${high ? '001' : '008'}'));
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/metronome${high ? '1' : '2'}'));
|
||||
}
|
||||
|
||||
function isNoteSelected(note:Null<SongNoteData>):Bool
|
||||
|
|
|
@ -72,6 +72,8 @@ class ChartEditorToolboxHandler
|
|||
{
|
||||
toolbox.showDialog(false);
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/openWindow'));
|
||||
|
||||
switch (id)
|
||||
{
|
||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
||||
|
@ -109,6 +111,8 @@ class ChartEditorToolboxHandler
|
|||
{
|
||||
toolbox.hideDialog(DialogButton.CANCEL);
|
||||
|
||||
ChartEditorAudioHandler.playSound(Paths.sound('chartingSounds/exitWindow'));
|
||||
|
||||
switch (id)
|
||||
{
|
||||
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
|
||||
{
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
@ -634,9 +650,9 @@ class ChartEditorToolboxHandler
|
|||
timeChanges[0].bpm = event.value;
|
||||
}
|
||||
|
||||
Conductor.forceBPM(event.value);
|
||||
|
||||
state.currentSongMetadata.timeChanges = timeChanges;
|
||||
|
||||
Conductor.mapTimeChanges(state.currentSongMetadata.timeChanges);
|
||||
};
|
||||
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;
|
||||
|
||||
import funkin.util.SortUtil;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.util.FlxColor;
|
||||
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
|
||||
for (songIndex in 1...songList.length)
|
||||
{
|
||||
|
|
|
@ -99,6 +99,9 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
var stickerSubState:StickerSubState;
|
||||
|
||||
static var rememberedLevelId:Null<String> = null;
|
||||
static var rememberedDifficulty:Null<String> = "normal";
|
||||
|
||||
public function new(?stickers:StickerSubState = null)
|
||||
{
|
||||
super();
|
||||
|
@ -133,6 +136,8 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
updateData();
|
||||
|
||||
rememberSelection();
|
||||
|
||||
// Explicitly define the background color.
|
||||
this.bgColor = FlxColor.BLACK;
|
||||
|
||||
|
@ -185,6 +190,7 @@ class StoryMenuState extends MusicBeatState
|
|||
leftDifficultyArrow.animation.play('idle');
|
||||
add(leftDifficultyArrow);
|
||||
|
||||
buildDifficultySprite(Constants.DEFAULT_DIFFICULTY);
|
||||
buildDifficultySprite();
|
||||
|
||||
rightDifficultyArrow = new FlxSprite(difficultySprite.x + difficultySprite.width + 10, leftDifficultyArrow.y);
|
||||
|
@ -207,6 +213,18 @@ class StoryMenuState extends MusicBeatState
|
|||
#end
|
||||
}
|
||||
|
||||
function rememberSelection():Void
|
||||
{
|
||||
if (rememberedLevelId != null)
|
||||
{
|
||||
currentLevelId = rememberedLevelId;
|
||||
}
|
||||
if (rememberedDifficulty != null)
|
||||
{
|
||||
currentDifficultyId = rememberedDifficulty;
|
||||
}
|
||||
}
|
||||
|
||||
function playMenuMusic():Void
|
||||
{
|
||||
if (FlxG.sound.music == null || !FlxG.sound.music.playing)
|
||||
|
@ -228,34 +246,35 @@ class StoryMenuState extends MusicBeatState
|
|||
isLevelUnlocked = currentLevel == null ? false : currentLevel.isUnlocked();
|
||||
}
|
||||
|
||||
function buildDifficultySprite():Void
|
||||
function buildDifficultySprite(?diff:String):Void
|
||||
{
|
||||
if (diff == null) diff = currentDifficultyId;
|
||||
remove(difficultySprite);
|
||||
difficultySprite = difficultySprites.get(currentDifficultyId);
|
||||
difficultySprite = difficultySprites.get(diff);
|
||||
if (difficultySprite == null)
|
||||
{
|
||||
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.play('idle');
|
||||
}
|
||||
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.y = leftDifficultyArrow.y - 15;
|
||||
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);
|
||||
|
||||
add(difficultySprite);
|
||||
|
@ -399,6 +418,7 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
var previousLevelId:String = currentLevelId;
|
||||
currentLevelId = levelList[currentIndex];
|
||||
rememberedLevelId = currentLevelId;
|
||||
|
||||
updateData();
|
||||
|
||||
|
@ -442,6 +462,7 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
var hasChanged:Bool = currentDifficultyId != difficultyList[currentIndex];
|
||||
currentDifficultyId = difficultyList[currentIndex];
|
||||
rememberedDifficulty = currentDifficultyId;
|
||||
|
||||
if (difficultyList.length <= 1)
|
||||
{
|
||||
|
@ -523,6 +544,7 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
PlayStatePlaylist.campaignId = currentLevel.id;
|
||||
PlayStatePlaylist.campaignTitle = currentLevel.getTitle();
|
||||
PlayStatePlaylist.campaignDifficulty = currentDifficultyId;
|
||||
|
||||
if (targetSong != null)
|
||||
{
|
||||
|
@ -538,7 +560,7 @@ class StoryMenuState extends MusicBeatState
|
|||
LoadingState.loadAndSwitchState(new PlayState(
|
||||
{
|
||||
targetSong: targetSong,
|
||||
targetDifficulty: currentDifficultyId,
|
||||
targetDifficulty: PlayStatePlaylist.campaignDifficulty,
|
||||
}), true);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -4,6 +4,9 @@ import flixel.util.FlxColor;
|
|||
import lime.app.Application;
|
||||
import funkin.data.song.SongData.SongTimeFormat;
|
||||
|
||||
/**
|
||||
* A store of unchanging, globally relevant values.
|
||||
*/
|
||||
class Constants
|
||||
{
|
||||
/**
|
||||
|
@ -118,11 +121,21 @@ class Constants
|
|||
*/
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
|
|
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;
|
||||
}
|
||||
|
||||
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.
|
||||
* @param input The array to search
|
||||
|
@ -38,6 +45,34 @@ class ArrayTools
|
|||
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.
|
||||
* @param array The array to clear.
|
||||
|
|
Loading…
Reference in a new issue