1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-12-10 22:17:05 +00:00

Compare commits

...

15 commits

Author SHA1 Message Date
Hyper_ 620ec071a7 fix: solve crash when deleting save data 2025-12-09 05:40:06 -07:00
Lasercar 7202043189 Chart Editor playtest option for Results Screen 2025-12-09 05:40:06 -07:00
charlesisfeline f0253fc46f null checkin for set health icon 2025-12-09 05:40:03 -07:00
AbnormalPoof 7cb6bb201e Re-export Girlfriend to texture atlas 2025-12-09 05:39:22 -07:00
AbnormalPoof 3d659bc0c9 Shift the flames how did this happen 2025-12-09 05:39:19 -07:00
AbnormalPoof be487ad36d Re-export the Freeplay difficulty stars using BTA 2025-12-09 05:39:10 -07:00
Cameron Taylor 658af405bf change fpScoreDisplay to private variable 2025-12-09 05:38:19 -07:00
Cameron Taylor d0183594cc change fp -> fpScoreDisplay, as a more descriptive variable name 2025-12-09 05:38:19 -07:00
MAJigsaw77 22e43f564a Remove WINAPI_GetProcessMemoryWorkingSetSize dublicate. 2025-12-09 05:38:19 -07:00
EliteMasterEric c03b70bfce Adjust WinAPI to allow setting theme on specific windows.
Co-authored-by: MAJigsaw77 <MAJigsaw77@users.noreply.github.com>
2025-12-09 05:38:19 -07:00
Cameron Taylor a0ee6ece21 early return in playVocals() to remove a level of nesting 2025-12-09 05:38:19 -07:00
Cameron Taylor 552e8e035f reduce code copypaste a tiny smidge for chart editor waveforms 2025-12-09 05:38:19 -07:00
MoonDroid 93bb397da1 Decrease blocksize compression for gfChill 2025-12-09 05:38:19 -07:00
AbnormalPoof 1a4db118ed Re-export GF's character select sprites to remove a stray line 2025-12-09 05:37:50 -07:00
AbnormalPoof cc644676b3 Re-export the Parents in the Eggnog Erect cutscene with BTA 2025-12-09 05:37:48 -07:00
16 changed files with 294 additions and 165 deletions

2
assets

@ -1 +1 @@
Subproject commit 1a961f111381eb3bfc452166c4e4b5a18b409781
Subproject commit b43892631e064771b057a8b00271dc20fab8478c

View file

@ -6,7 +6,7 @@
"colorprofile": "cl",
"custom": [
{
"asset": "asset/shared/images/resultScreen/results-pico/resultsPERFECT/spritemap1.png",
"asset": "asset/shared/images/resultScreen/results-pico/resultsGOOD/spritemap1.png",
"blocksize": "4x4"
},
{
@ -14,11 +14,7 @@
"blocksize": "4x4"
},
{
"asset": "asset/shared/images/resultScreen/results-pico/resultsGOOD/spritemap1.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/resultScreen/results-bf/resultsEXCELLENT/spritemap1.png",
"asset": "asset/shared/images/resultScreen/results-pico/resultsPERFECT/spritemap1.png",
"blocksize": "4x4"
},
{
@ -41,6 +37,10 @@
"asset": "assets/preload/images/alphabet.png",
"blocksize": "4x4"
},
{
"asset": "assets/preload/images/charSelect/gfChill/spritemap1.png",
"blocksize": "6x6"
},
{
"asset": "assets/shared/images/NOTE_hold_assets.png",
"blocksize": "4x4"
@ -81,6 +81,10 @@
"asset": "assets/shared/images/notes.png",
"blocksize": "4x4"
},
{
"asset": "assets/shared/images/resultScreen/results-bf/resultsEXCELLENT/spritemap1.png",
"blocksize": "4x4"
},
{
"asset": "assets/week1/image/erect/bg.png",
"blocksize": "8x8"

View file

@ -11,38 +11,32 @@ extern class WinAPI
/**
* Shows a message box with an error icon.
*
* @param handle A handle to the parent window.
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowError')
static function showError(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
static function showError(handle:cpp.RawPointer<cpp.Void>, message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Shows a message box with a warning icon.
*
* @param handle A handle to the parent window.
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowWarning')
static function showWarning(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
static function showWarning(handle:cpp.RawPointer<cpp.Void>, message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Shows a message box with an information icon.
*
* @param handle A handle to the parent window.
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowInformation')
static function showInformation(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Shows a message box with a question icon.
*
* @param message The message to display.
* @param title The title of the message box.
*/
@:native('WINAPI_ShowQuestion')
static function showQuestion(message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
static function showInformation(handle:cpp.RawPointer<cpp.Void>, message:cpp.ConstCharStar, title:cpp.ConstCharStar):Void;
/**
* Disables the "Report to Microsoft" dialog that appears when the application crashes.
@ -67,10 +61,11 @@ extern class WinAPI
/**
* Sets the dark mode for the active window.
*
* @param handle A handle to the parent window.
* @param enable Whether to enable or disable dark mode.
*/
@:native('WINAPI_SetDarkMode')
static function setDarkMode(enable:Bool):Void;
static function setDarkMode(handle:cpp.RawPointer<cpp.Void>, enable:Bool):Void;
/**
* Checks if the system is using dark mode.

View file

@ -3,34 +3,29 @@
/**
* Shows an error message box.
*
* @param handle A handle to the parent window
* @param message The error message to display
* @param title The title of the message box
*/
void WINAPI_ShowError(const char *message, const char *title);
void WINAPI_ShowError(void* handle, const char *message, const char *title);
/**
* Shows a warning message box.
*
* @param handle A handle to the parent window
* @param message The warning message to display
* @param title The title of the message box
*/
void WINAPI_ShowWarning(const char *message, const char *title);
void WINAPI_ShowWarning(void* handle, const char *message, const char *title);
/**
* Shows an information message box.
*
* @param handle A handle to the parent window
* @param message The information message to display
* @param title The title of the message box
*/
void WINAPI_ShowInformation(const char *message, const char *title);
/**
* Shows a question message box with OK/Cancel buttons.
*
* @param message The question message to display
* @param title The title of the message box
*/
void WINAPI_ShowQuestion(const char *message, const char *title);
void WINAPI_ShowInformation(void* handle, const char *message, const char *title);
/**
* Disables Windows error reporting dialogs.
@ -52,11 +47,12 @@ void WINAPI_DisableWindowsGhosting();
size_t WINAPI_GetProcessMemoryWorkingSetSize();
/**
* Sets dark mode for the active window.
* Sets dark mode on a window
* @param handle A handle to the parent window.
*
* @param enable True to enable dark mode, false to disable
*/
void WINAPI_SetDarkMode(bool enable);
void WINAPI_SetDarkMode(void* handle, bool enable);
/**
* Checks if the system is using dark mode.

View file

@ -11,25 +11,36 @@
#include <stdint.h>
#include <stdio.h>
void WINAPI_ShowError(const char *message, const char *title)
void WINAPI_ShowError(void* handle, const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONERROR);
HWND hwnd = (HWND)handle;
if (!IsWindow(hwnd))
hwnd = nullptr;
MessageBox(hwnd, message, title, MB_OK | MB_ICONERROR);
}
void WINAPI_ShowWarning(const char *message, const char *title)
void WINAPI_ShowWarning(void* handle, const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONWARNING);
HWND hwnd = (HWND)handle;
if (!IsWindow(hwnd))
hwnd = nullptr;
MessageBox(hwnd, message, title, MB_OK | MB_ICONWARNING);
}
void WINAPI_ShowInformation(const char *message, const char *title)
void WINAPI_ShowInformation(void* handle, const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OK | MB_ICONINFORMATION);
HWND hwnd = (HWND)handle;
if (!IsWindow(hwnd))
hwnd = nullptr;
MessageBox(hwnd, message, title, MB_OK | MB_ICONINFORMATION);
}
void WINAPI_ShowQuestion(const char *message, const char *title)
{
MessageBox(GetActiveWindow(), message, title, MB_OKCANCEL | MB_ICONQUESTION);
}
void WINAPI_DisableErrorReporting()
{
@ -51,16 +62,19 @@ size_t WINAPI_GetProcessMemoryWorkingSetSize()
return 0;
}
void WINAPI_SetDarkMode(bool enable)
void WINAPI_SetDarkMode(void* handle, bool enable)
{
HWND window = GetActiveWindow();
HWND hwnd = (HWND)handle;
if (!IsWindow(hwnd))
return;
int darkMode = enable ? 1 : 0;
if (DwmSetWindowAttribute(window, 20, &darkMode, sizeof(darkMode)) != S_OK)
DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode));
if (DwmSetWindowAttribute(hwnd, 20, &darkMode, sizeof(darkMode)) != S_OK)
DwmSetWindowAttribute(hwnd, 19, &darkMode, sizeof(darkMode));
UpdateWindow(window);
UpdateWindow(hwnd);
}
bool WINAPI_IsSystemDarkMode()

View file

@ -121,6 +121,11 @@ typedef PlayStateParams =
* @default `false`
*/
?botPlayMode:Bool,
/**
* Whether the results screen should show up before returning to the chart editor.
* @default `false`
*/
?playtestResults:Bool,
/**
* Whether the song should be in minimal mode.
* @default `false`
@ -362,6 +367,11 @@ class PlayState extends MusicBeatSubState
*/
public var isBotPlayMode:Bool = false;
/**
* Whether the results screen should show up before returning to the chart editor.
*/
public var isPlaytestResults:Bool = false;
/**
* Whether the player has dropped below zero health,
* and we are just waiting for an animation to play out before transitioning.
@ -723,6 +733,7 @@ class PlayState extends MusicBeatSubState
if (params.targetInstrumental != null) currentInstrumental = params.targetInstrumental;
isPracticeMode = params.practiceMode ?? false;
isBotPlayMode = params.botPlayMode ?? false;
isPlaytestResults = params.playtestResults ?? false;
isMinimalMode = params.minimalMode ?? false;
startTimestamp = params.startTimestamp ?? 0.0;
playbackRate = params.playbackRate ?? 1.0;
@ -837,6 +848,7 @@ class PlayState extends MusicBeatSubState
var pre:Float = (Conductor.instance.beatLengthMs * -5) + startTimestamp;
trace('Attempting to start at ' + pre);
trace('startTimestamp ${startTimestamp}');
Conductor.instance.update(pre);
@ -1071,7 +1083,7 @@ class PlayState extends MusicBeatSubState
opponentStrumline.clean();
// Delete all notes and reset the arrays.
regenNoteData();
regenNoteData(startTimestamp);
// Reset camera zooming
cameraBopIntensity = Constants.DEFAULT_BOP_INTENSITY;
@ -2388,7 +2400,7 @@ class PlayState extends MusicBeatSubState
}
}
regenNoteData();
regenNoteData(startTimestamp);
var event:ScriptEvent = new ScriptEvent(CREATE, false);
ScriptEventDispatcher.callEvent(currentSong, event);
@ -3598,7 +3610,21 @@ class PlayState extends MusicBeatSubState
{
if (isSubState)
{
this.close();
if (isPlaytestResults)
{
var talliesToUse:Tallies = PlayStatePlaylist.isStoryMode ? Highscore.talliesLevel : Highscore.tallies;
var clearPercentFloat = talliesToUse.totalNotes == 0 ? 0.0 : (talliesToUse.sick + talliesToUse.good) / talliesToUse.totalNotes * 100;
/*
Only move to the score screen if more than 30% of the song was successfully hit.
While that might sound like a low clear percent, consider the fact that some songs are hard,
and the user might be only playtesting one third or half the song.
*/
if (clearPercentFloat >= 30) moveToResultsScreen(false, prevScoreData);
else
this.close();
}
else
this.close();
}
else
{

View file

@ -35,6 +35,7 @@ import funkin.ui.MusicBeatSubState;
import funkin.ui.story.StoryMenuState;
import funkin.util.HapticUtil;
import funkin.graphics.ScriptedFunkinSprite;
import funkin.ui.debug.charting.ChartEditorState;
#if FEATURE_NEWGROUNDS
import funkin.api.newgrounds.Medals;
#end
@ -93,6 +94,11 @@ class ResultState extends MusicBeatSubState
var introMusicAudio:Null<FunkinSound> = null;
/**
* The music playing in the background of the state.
*/
var resultsMusic:Null<FunkinSound> = null;
var rankBg:FunkinSprite;
final cameraBG:FunkinCamera;
final cameraScroll:FunkinCamera;
@ -102,6 +108,15 @@ class ResultState extends MusicBeatSubState
var busy:Bool = false;
public var isChartingMode(get, never):Bool;
function get_isChartingMode():Bool
{
if (PlayState.instance != null) return PlayState.instance.isChartingMode;
else
return false;
}
public function new(params:ResultsStateParams)
{
super();
@ -487,22 +502,34 @@ class ResultState extends MusicBeatSubState
// Play the intro music.
introMusicAudio = FunkinSound.load(introMusic, 1.0, false, true, true, () -> {
introMusicAudio = null;
if (!isChartingMode) // Don't override the music and cause problems on the chart editor
FunkinSound.playMusic(getMusicPath(playerCharacter, rank),
{
startingVolume: 1.0,
overrideExisting: true,
restartTrack: true
});
else // Play the results music as a looped sound instead (that we cancel before closing and returning to the chart editor)
{
resultsMusic = FunkinSound.load(Paths.music(getMusicPath(playerCharacter, rank) + '/' + getMusicPath(playerCharacter, rank)), 1.0, true, false,
true);
false; // Why is this necessary for this to work?
}
});
}
else
{
FunkinSound.playMusic(getMusicPath(playerCharacter, rank),
if (!isChartingMode) FunkinSound.playMusic(getMusicPath(playerCharacter, rank),
{
startingVolume: 1.0,
overrideExisting: true,
restartTrack: true
});
else
{
resultsMusic = FunkinSound.load(Paths.music(getMusicPath(playerCharacter, rank) + '/' + getMusicPath(playerCharacter, rank)), 1.0, true, false, true);
}
}
});
@ -780,6 +807,7 @@ class ResultState extends MusicBeatSubState
if (_parentState is funkin.ui.debug.results.ResultsDebugSubState)
{
if (introMusicAudio != null)
{
introMusicAudio.stop();
introMusicAudio.destroy();
@ -793,16 +821,18 @@ class ResultState extends MusicBeatSubState
introMusicAudio.onComplete = null;
FlxTween.tween(introMusicAudio, {volume: 0}, 0.8,
{
onComplete: _ -> {
if (introMusicAudio != null)
onComplete: _ -> {
if (introMusicAudio != null)
{
introMusicAudio.stop();
introMusicAudio.destroy();
introMusicAudio = null;
introMusicAudio.stop();
introMusicAudio.destroy();
introMusicAudio = null;
}
}
}
});
});
FlxTween.tween(introMusicAudio, {pitch: 3}, 0.1,
{
onComplete: _ -> {
@ -893,6 +923,16 @@ class ResultState extends MusicBeatSubState
trace('THE RANK IS Higher.....');
shouldTween = true;
if (isChartingMode)
{
PlayState.instance?.close();
FlxTimer.globalManager.clear();
FlxTween.globalManager.clear();
if (introMusicAudio != null) introMusicAudio.stop();
if (resultsMusic != null) resultsMusic.stop();
this.close();
return;
}
targetState = FreeplayState.build(
{
{
@ -910,6 +950,16 @@ class ResultState extends MusicBeatSubState
}
else
{
if (isChartingMode)
{
PlayState.instance?.close();
FlxTimer.globalManager.clear();
FlxTween.globalManager.clear();
if (introMusicAudio != null) introMusicAudio.stop();
if (resultsMusic != null) resultsMusic.stop();
this.close();
return;
}
shouldTween = false;
shouldUseSubstate = true;
targetStateFactory = () -> new StickerSubState(

View file

@ -55,11 +55,17 @@ class SetHealthIconSongEvent extends SongEvent
switch (data?.value?.char ?? 0)
{
case 0:
trace('Applying Player health icon via song event: ${healthIconData.id}');
PlayState.instance.iconP1.configure(healthIconData);
if (PlayState.instance.iconP1 != null)
{
trace('Applying Player health icon via song event: ${healthIconData.id}');
PlayState.instance.iconP1.configure(healthIconData);
}
case 1:
trace('Applying Opponent health icon via song event: ${healthIconData.id}');
PlayState.instance.iconP2.configure(healthIconData);
if (PlayState.instance.iconP2 != null)
{
trace('Applying Opponent health icon via song event: ${healthIconData.id}');
PlayState.instance.iconP2.configure(healthIconData);
}
default:
trace(' WARNING '.bold().bg_yellow() + ' Unknown character index: ' + data.value.char);
}

View file

@ -67,7 +67,7 @@ class Save implements ConsoleClass
@:nullSafety(Off)
public function new(?data:RawSaveData)
{
this.data = data ?? Save.getDefaultData();
this.data = data ??= Save.getDefaultData();
// Build macro will inject SaveProperty initialization here automatically
// Make sure the verison number is up to date before we flush.

View file

@ -603,6 +603,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
*/
var playtestBotPlayMode:Bool = false;
/**
* If true, after playtesting a chart the results screen will show your score.
*/
var playtestShowResults:Bool = false;
/**
* Enables or disables the "debugger" popup that appears when you run into a flixel error.
*/
@ -6260,6 +6265,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
targetVariation: selectedVariation,
practiceMode: playtestPracticeMode,
botPlayMode: playtestBotPlayMode,
playtestResults: playtestShowResults,
minimalMode: minimal,
startTimestamp: startTimestamp,
playbackRate: playbackRate,

View file

@ -188,71 +188,71 @@ class ChartEditorAudioHandler
if (state.audioVocalTrackGroup == null) state.audioVocalTrackGroup = new VoicesGroup();
if (vocalTrack != null)
// early return
if (vocalTrack == null) return false;
switch (charType)
{
switch (charType)
{
case BF:
state.audioVocalTrackGroup.addPlayerVoice(vocalTrack);
case BF:
state.audioVocalTrackGroup.addPlayerVoice(vocalTrack);
var waveformData:Null<WaveformData> = vocalTrack.waveformData;
var waveformData:Null<WaveformData> = vocalTrack.waveformData;
if (waveformData != null)
{
var duration:Float = Conductor.instance.getStepTimeInMs(16) * 0.001;
var waveformSprite:WaveformSprite = new WaveformSprite(waveformData, VERTICAL, FlxColor.WHITE);
waveformSprite.x = 840;
waveformSprite.y = Math.max(state.gridTiledSprite?.y ?? 0.0, ChartEditorState.GRID_INITIAL_Y_POS - ChartEditorState.GRID_TOP_PAD);
waveformSprite.height = (ChartEditorState.GRID_SIZE) * 16;
waveformSprite.width = (ChartEditorState.GRID_SIZE) * 2;
waveformSprite.time = 0;
waveformSprite.duration = duration;
state.audioWaveforms.add(waveformSprite);
}
else
{
trace(' WARNING '.bold().bg_yellow() + ' Failed to parse waveform data for vocal track.');
}
if (waveformData != null)
{
var waveformSprite:WaveformSprite = initWaveformSprite(waveformData, state);
waveformSprite.x = 840;
state.audioWaveforms.add(waveformSprite);
}
else
{
trace(' WARNING '.bold().bg_yellow() + ' Failed to parse waveform data for vocal track.');
}
state.audioVocalTrackGroup.playerVoicesOffset = state.currentVocalOffsetPlayer;
return true;
case DAD:
state.audioVocalTrackGroup.addOpponentVoice(vocalTrack);
state.audioVocalTrackGroup.playerVoicesOffset = state.currentVocalOffsetPlayer;
return true;
case DAD:
state.audioVocalTrackGroup.addOpponentVoice(vocalTrack);
var waveformData:Null<WaveformData> = vocalTrack.waveformData;
var waveformData:Null<WaveformData> = vocalTrack.waveformData;
if (waveformData != null)
{
var duration:Float = Conductor.instance.getStepTimeInMs(16) * 0.001;
var waveformSprite:WaveformSprite = new WaveformSprite(waveformData, VERTICAL, FlxColor.WHITE);
waveformSprite.x = 360;
waveformSprite.y = Math.max(state.gridTiledSprite?.y ?? 0.0, ChartEditorState.GRID_INITIAL_Y_POS - ChartEditorState.GRID_TOP_PAD);
waveformSprite.height = (ChartEditorState.GRID_SIZE) * 16;
waveformSprite.width = (ChartEditorState.GRID_SIZE) * 2;
waveformSprite.time = 0;
waveformSprite.duration = duration;
state.audioWaveforms.add(waveformSprite);
}
else
{
trace(' WARNING '.bold().bg_yellow() + ' Failed to parse waveform data for vocal track.');
}
if (waveformData != null)
{
var waveformSprite:WaveformSprite = initWaveformSprite(waveformData, state);
waveformSprite.x = 360;
state.audioWaveforms.add(waveformSprite);
}
else
{
trace(' WARNING '.bold().bg_yellow() + ' Failed to parse waveform data for vocal track.');
}
state.audioVocalTrackGroup.opponentVoicesOffset = state.currentVocalOffsetOpponent;
state.audioVocalTrackGroup.opponentVoicesOffset = state.currentVocalOffsetOpponent;
return true;
case OTHER:
state.audioVocalTrackGroup.add(vocalTrack);
// TODO: Add offset for other characters.
return true;
default:
// Do nothing.
}
return true;
case OTHER:
state.audioVocalTrackGroup.add(vocalTrack);
// TODO: Add offset for other characters.
return true;
default:
// Do nothing.
}
return false;
}
// initializes a waveform sprite with buncho non-charType specific things
static function initWaveformSprite(waveformData:WaveformData, state:ChartEditorState):WaveformSprite
{
var waveformSprite:WaveformSprite = new WaveformSprite(waveformData, VERTICAL, FlxColor.WHITE);
waveformSprite.y = Math.max(state.gridTiledSprite?.y ?? 0.0, ChartEditorState.GRID_INITIAL_Y_POS - ChartEditorState.GRID_TOP_PAD);
waveformSprite.height = (ChartEditorState.GRID_SIZE) * 16;
waveformSprite.width = (ChartEditorState.GRID_SIZE) * 2;
waveformSprite.time = 0;
waveformSprite.duration = Conductor.instance.getStepTimeInMs(16) * 0.001;
return waveformSprite;
}
public static function stopExistingVocals(state:ChartEditorState):Void
{
state.audioVocalTrackGroup.clear();

View file

@ -280,6 +280,16 @@ class ChartEditorToolboxHandler
state.playtestBotPlayMode = checkboxBotPlay.selected;
};
var checkboxShowResults:Null<CheckBox> = toolbox.findComponent('playtestShowResultsCheckbox', CheckBox);
if (checkboxShowResults == null)
throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestShowResultsCheckbox component.';
checkboxShowResults.selected = state.playtestShowResults;
checkboxShowResults.onClick = _ -> {
state.playtestShowResults = checkboxShowResults.selected;
};
var checkboxSongScripts:Null<CheckBox> = toolbox.findComponent('playtestSongScriptsCheckbox', CheckBox);
if (checkboxSongScripts == null)

View file

@ -7,8 +7,8 @@ import flixel.util.FlxTimer;
@:nullSafety
class FreeplayFlames extends FlxSpriteGroup
{
var flameX(default, set):Float = (FlxG.width - 363) - funkin.ui.FullScreenScaleMode.gameNotchSize.x;
var flameY(default, set):Float = 103;
var flameX(default, set):Float = (FlxG.width - 367) - funkin.ui.FullScreenScaleMode.gameNotchSize.x;
var flameY(default, set):Float = 91;
var flameSpreadX(default, set):Float = 29;
var flameSpreadY(default, set):Float = 6;

View file

@ -157,7 +157,7 @@ class FreeplayState extends MusicBeatSubState
*/
var currentVariation:String = Constants.DEFAULT_VARIATION;
public var fp:FreeplayScore;
var fpScoreDisplay:FreeplayScore;
var txtCompletion:AtlasText;
var lerpCompletion:Float = 0;
@ -330,7 +330,7 @@ class FreeplayState extends MusicBeatSubState
// We build a bunch of sprites BEFORE create() so we can guarantee they aren't null later on.
albumRoll = new AlbumRoll();
fp = new FreeplayScore(FlxG.width - (FullScreenScaleMode.gameNotchSize.x + 353), 60, 7, 100, styleData);
fpScoreDisplay = new FreeplayScore(FlxG.width - (FullScreenScaleMode.gameNotchSize.x + 353), 60, 7, 100, styleData);
rankCamera = new FunkinCamera('rankCamera', 0, 0, FlxG.width, FlxG.height);
funnyCam = new FunkinCamera('freeplayFunny', 0, 0, FlxG.width, FlxG.height);
grpCapsules = new FlxTypedGroup<SongMenuItem>();
@ -629,8 +629,8 @@ class FreeplayState extends MusicBeatSubState
tmr.time = FlxG.random.float(20, 60);
}, 0);
fp.visible = false;
add(fp);
fpScoreDisplay.visible = false;
add(fpScoreDisplay);
var clearBoxSprite:FlxSprite = new FlxSprite(FlxG.width - (FullScreenScaleMode.gameNotchSize.x + 115), 65).loadGraphic(Paths.image('freeplay/clearBox'));
clearBoxSprite.visible = false;
@ -687,13 +687,13 @@ class FreeplayState extends MusicBeatSubState
}
};
exitMovers.set([fp, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
exitMovers.set([fpScoreDisplay, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
{
x: FlxG.width,
speed: 0.3
});
exitMoversCharSel.set([fp, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
exitMoversCharSel.set([fpScoreDisplay, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
{
y: -270,
speed: 0.8,
@ -781,8 +781,8 @@ class FreeplayState extends MusicBeatSubState
freeplayTxtBg.visible = true;
if (freeplayArrow != null) freeplayArrow.visible = true;
ostName.visible = true;
fp.visible = true;
fp.updateScore(0);
fpScoreDisplay.visible = true;
fpScoreDisplay.updateScore(0);
clearBoxSprite.visible = true;
txtCompletion.visible = true;
@ -995,7 +995,6 @@ class FreeplayState extends MusicBeatSubState
if (tempSong == null) continue;
var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem);
funnyMenu.initPosition(FlxG.width, 0);
funnyMenu.initData(tempSong, styleData, i + 1);
funnyMenu.onConfirm = function() {
@ -1702,7 +1701,7 @@ class FreeplayState extends MusicBeatSubState
lerpCompletion = intendedCompletion;
}
fp.updateScore(Std.int(lerpScore));
fpScoreDisplay.updateScore(Std.int(lerpScore));
// sets the text of the completion percentage. Perhaps eventually we may want to generalize this,
// but for now we can just clamp the values between 0 and 100.

View file

@ -95,21 +95,12 @@ class WindowUtil
*/
public static final windowExit:FlxTypedSignal<Int->Void> = new FlxTypedSignal<Int->Void>();
/**
* Has `initWindowEvents()` been called already?
* This is to prevent multiple instances of the same function.
*/
private static var _initializedWindowEvents:Bool = false;
/**
* Wires up FlxSignals that happen based on window activity.
* For example, we can run a callback when the window is closed.
*/
public static function initWindowEvents():Void
{
if (_initializedWindowEvents) return; // Fix that annoying
// onUpdate is called every frame just before rendering.
// onExit is called when the game window is closed.
openfl.Lib.current.stage.application.onExit.add(function(exitCode:Int) {
windowExit.dispatch(exitCode);
@ -144,7 +135,6 @@ class WindowUtil
}
});
#end
_initializedWindowEvents = true;
}
/**
@ -164,7 +154,11 @@ class WindowUtil
public static function showError(name:String, desc:String):Void
{
#if (windows && cpp)
funkin.external.windows.WinAPI.showError(desc, name);
final handleVal:Float = lime.app.Application.current.window.nativeHandle;
final handlePtr:cpp.RawPointer<cpp.Void> = untyped __cpp__('(void*)(uintptr_t){0}', handleVal);
funkin.external.windows.WinAPI.showError(handlePtr, desc, name);
#else
lime.app.Application.current.window.alert(desc, name);
#end
@ -178,7 +172,11 @@ class WindowUtil
public static function showWarning(name:String, desc:String):Void
{
#if (windows && cpp)
funkin.external.windows.WinAPI.showWarning(desc, name);
final handleVal:Float = lime.app.Application.current.window.nativeHandle;
final handlePtr:cpp.RawPointer<cpp.Void> = untyped __cpp__('(void*)(uintptr_t){0}', handleVal);
funkin.external.windows.WinAPI.showWarning(handlePtr, desc, name);
#else
lime.app.Application.current.window.alert(desc, name);
#end
@ -192,23 +190,30 @@ class WindowUtil
public static function showInformation(name:String, desc:String):Void
{
#if (windows && cpp)
funkin.external.windows.WinAPI.showInformation(desc, name);
final handleVal:Float = lime.app.Application.current.window.nativeHandle;
final handlePtr:cpp.RawPointer<cpp.Void> = untyped __cpp__('(void*)(uintptr_t){0}', handleVal);
funkin.external.windows.WinAPI.showInformation(handlePtr, desc, name);
#else
lime.app.Application.current.window.alert(desc, name);
#end
}
/**
* Shows a question dialog with a question icon and OK/Cancel buttons.
* @param name The title of the dialog window.
* @param desc The question message to display.
* Sets the dark mode appearance for the specified window.
*
* @param window The window instance to modify.
* @param value Whether to enable (`true`) or disable (`false`) dark mode.
*/
public static function showQuestion(name:String, desc:String):Void
public static function setDarkMode(window:lime.ui.Window, value:Bool):Void
{
#if (windows && cpp)
funkin.external.windows.WinAPI.showQuestion(desc, name);
#else
lime.app.Application.current.window.alert(desc, name);
final handleVal:Float = window.nativeHandle;
final handlePtr:cpp.RawPointer<cpp.Void> = untyped __cpp__('(void*)(uintptr_t){0}', handleVal);
funkin.external.windows.WinAPI.setDarkMode(handlePtr, value);
#end
}

View file

@ -13,6 +13,10 @@ import hxgamemode.GamemodeClient;
class ApplicationIcon extends lime.graphics.Image {}
#end
#if (windows && cpp)
using funkin.util.WindowUtil;
#end
@:access(lime.app.Application)
@:access(lime.system.System)
@:access(openfl.display.Stage)
@ -21,8 +25,24 @@ class ApplicationIcon extends lime.graphics.Image {}
class ApplicationMain
{
#if !macro
public static function main()
#if (windows && cpp)
public static var systemDarkMode:Bool = false;
#end
public static function main():Void
{
#if (windows && cpp)
// Disable the Windows "ghosting" effect that dims unresponsive windows.
funkin.external.windows.WinAPI.disableWindowsGhosting();
// Disable Windows error reporting (avoids sending bug reports to Microsoft).
funkin.external.windows.WinAPI.disableErrorReporting();
// Whether the system is currently using dark mode.
systemDarkMode = funkin.external.windows.WinAPI.isSystemDarkMode();
#end
lime.system.System.__registerEntryPoint("::APP_FILE::", create);
#if (js && html5)
@ -36,6 +56,10 @@ class ApplicationMain
public static function create(config):Void
{
#if linux
GamemodeClient.request_start();
#end
final appMeta:Map<String, String> = [];
appMeta.set("build", "::meta.buildNumber::");
@ -52,12 +76,20 @@ class ApplicationMain
var app = new openfl.display.Application(appMeta);
#if linux
#if ((windows && cpp) || linux)
app.onCreateWindow.add(function(window:lime.ui.Window):Void
{
#if (windows && cpp)
if (systemDarkMode)
{
window.setDarkMode(systemDarkMode);
}
#end
#if linux
window.setIcon(new ApplicationIcon());
#end
});
GamemodeClient.request_start();
#end
#if !disable_preloader_assets
@ -128,20 +160,6 @@ class ApplicationMain
app.window.frameRate = ::WIN_FPS::;
#end
#if (windows && cpp)
// Disable the Windows "ghosting" effect that dims unresponsive windows.
funkin.external.windows.WinAPI.disableWindowsGhosting();
// Disable Windows error reporting (avoids sending bug reports to Microsoft).
funkin.external.windows.WinAPI.disableErrorReporting();
// Enable dark mode if the system theme is set to dark.
if (funkin.external.windows.WinAPI.isSystemDarkMode())
{
funkin.external.windows.WinAPI.setDarkMode(true);
}
#end
var preloader = getPreloader();
app.preloader.onProgress.add (function(loaded, total)
{