1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-07-14 15:26:24 +00:00

Merge branch 'rewrite/master' into abnormalpoof/stage-prop-flipx

This commit is contained in:
EliteMasterEric 2024-09-28 09:18:41 -04:00
commit 042dfb7442
83 changed files with 956 additions and 288 deletions

44
.github/label-actions.yml vendored Normal file
View file

@ -0,0 +1,44 @@
# Configuration for Label Actions - https://github.com/dessant/label-actions
# Automatically close issues and pull requests when the `status: duplicate` label is applied
'status: duplicate':
issues:
# Post a comment
comment: >
This issue is a duplicate. Please direct all discussion to the original issue.
# Close the issue
close: true
# Remove other status labels
unlabel:
- 'status: pending triage'
# Set a close reason
close-reason: 'not planned'
prs:
# Post a comment
comment: >
This pull request is a duplicate. Please direct all discussion to the original pull request.
# Remove other status labels
unlabel:
- 'status: pending triage'
# Close the pull request
close: true
# Set a close reason
close-reason: 'not planned'
'status: rejected':
issues:
# Close the issue
close: true
# Remove other status labels
unlabel:
- 'status: pending triage'
# Set a close reason
close-reason: 'not planned'
prs:
# Close the pull request
close: true
# Remove other status labels
unlabel:
- 'status: pending triage'
# Set a close reason
close-reason: 'not planned'

View file

@ -2,6 +2,15 @@ name: Build and Upload nightly game builds
on:
workflow_dispatch:
inputs:
build-defines:
type: string
description: Build defines to use
default: '-DGITHUB_BUILD'
save-artifact:
type: boolean
description: Save the build artifact to Github Actions (sends to itch otherwise)
default: false
push:
paths-ignore:
- '**/Dockerfile'
@ -53,13 +62,20 @@ jobs:
- name: Build game
if: ${{ matrix.target == 'windows' }}
run: |
haxelib run lime build windows -v -release -DGITHUB_BUILD
haxelib run lime build windows -v -release ${{ github.event.inputs.build-defines }}
timeout-minutes: 120
- name: Build game
if: ${{ matrix.target != 'windows' }}
run: |
haxelib run lime build ${{ matrix.target }} -v -release --times -DGITHUB_BUILD
haxelib run lime build ${{ matrix.target }} -v -release --times ${{ github.event.inputs.build-defines }}
timeout-minutes: 120
- name: Save build artifact to Github Actions
if: ${{ github.event.inputs.save-artifact }}
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.target }}
path: export/release/${{matrix.target}}/bin/
- name: Upload build artifacts
uses: ./.github/actions/upload-itch
@ -125,9 +141,15 @@ jobs:
- name: Build game
run: |
haxelib run lime build ${{ matrix.target }} -v -release --times -DGITHUB_BUILD
haxelib run lime build ${{ matrix.target }} -v -release --times ${{ github.event.inputs.build-defines }}
timeout-minutes: 120
- name: Save build artifact to Github Actions
if: ${{ github.event.inputs.save-artifact }}
uses: actions/upload-artifact@v4
with:
name: build-${{ matrix.target }}
path: export/release/${{matrix.target}}/bin/
- name: Upload build artifacts
uses: ./.github/actions/upload-itch
with:

29
.github/workflows/label-actions.yml vendored Normal file
View file

@ -0,0 +1,29 @@
# Perform actions when labels are applied to issues, discussions, or pull requests
# See .github/label-actions.yml
name: 'Label Actions'
on:
issues:
types:
- labeled
- unlabeled
pull_request_target:
types:
- labeled
- unlabeled
discussion:
types:
- labeled
- unlabeled
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/label-actions@v4

52
.github/workflows/label-issue.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: "Issue Labeler"
on:
issues:
types:
- opened
- reopened
- edited
jobs:
# When an issue is opened, perform a similarity check for potential duplicates.
# If some are found, add a label and comment listing the potential duplicate issues.
potential-duplicate:
name: Detect potential duplicate issues
runs-on: ubuntu-latest
steps:
- uses: wow-actions/potential-duplicates@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with anymatch https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
filter: ''
# Exclude keywords in title before detecting.
exclude: ''
# Label to set, when potential duplicates are detected.
label: 'potential duplicate'
# Get issues with state to compare. Supported state: 'all', 'closed', 'open'.
state: all
# If similarity is higher than this threshold([0,1]), issue will be marked as duplicate.
# Turn this up if the detection is too sensitive
threshold: 0.6
# Reactions to be add to comment when potential duplicates are detected.
# Available reactions: "-1", "+1", "confused", "laugh", "heart", "hooray", "rocket", "eyes"
# reactions: '-1'
# Comment to post when potential duplicates are detected.
comment: >
Potential duplicates: {{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
{{/issues}}
# When an issue is opened, detect if it has an empty body or incomplete issue form.
# If it does, close the issue immediately.
empty-issues:
name: Close empty issues
runs-on: ubuntu-latest
steps:
- name: Run empty issues closer action
uses: rickstaa/empty-issues-closer-action@v1
env:
github_token: ${{ secrets.GITHUB_TOKEN }}
with:
close_comment: Closing this issue because it appears to be empty. Please update the issue for it to be reopened.
open_comment: Reopening this issue because the author provided more information.

View file

@ -3,6 +3,7 @@ on:
- pull_request_target
jobs:
# Apply labels to pull requests based on which files were edited
labeler:
permissions:
contents: read
@ -13,6 +14,7 @@ jobs:
uses: actions/labeler@v5
with:
sync-labels: true
# Apply labels to pull requests based on how many lines were edited
changed-lines-count-labeler:
permissions:
contents: read

4
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "assets"]
path = assets
url = https://github.com/FunkinCrew/funkin.assets
url = https://github.com/FunkinCrew/Funkin-assets-secret
[submodule "art"]
path = art
url = https://github.com/FunkinCrew/funkin.art
url = https://github.com/FunkinCrew/Funkin-art-secret

View file

@ -96,6 +96,11 @@
"target": "windows",
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS"]
},
{
"label": "Windows / Debug (Tracy)",
"target": "windows",
"args": ["-debug", "-DFEATURE_DEBUG_TRACY", "-DFEATURE_DEBUG_FUNCTIONS"]
},
{
"label": "Linux / Debug",
"target": "linux",

View file

@ -9,19 +9,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added a new Character Select screen to switch between playable characters in Freeplay
- Modding isn't 100% there but we're working on it!
- Added Pico as a playable character! Unlock him by completing Weekend 1 (if you haven't already done that)
- The songs from Weekend 1 have moved; you must now switch to Pico in Freeplay to access them
- Added 10 new Pico remixes! Access them by selecting Pico from in the Character Select screen
- The songs from Weekend 1 have moved; you must now switch to Pico via Character Select screen in Freeplay to access them
- Added 11 new Pico remixes! Access them by selecting Pico from in the Character Select screen
- Bopeebo (Pico Mix)
- Fresh (Pico Mix)
- DadBattle (Pico Mix)
- Spookeez (Pico Mix)
- South (Pico Mix)
- Pico (Pico Mix)
- Philly Nice (Pico Mix)
- Blammed (Pico Mix)
- Blammed (Pico Mix)
- Eggnog (Pico Mix)
- Ugh (Pico Mix)
- Guns (Pico Mix)
- Added 1 new Boyfriend remix! Access it by selecting Pico from in the Character Select screen
- Added 1 new Boyfriend remix! Access it by completing Weekend 1 as Pico and then selecting Boyfriend from in the Character Select screen
- Darnell (BF Mix)
- Added 2 new Erect remixes! Access them by switching difficulty on the song
- Cocoa Erect
@ -39,6 +40,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- These display on Pico remixes, as well as when playing Weekend 1.
- Implemented support for scripted Note Kinds. You can use HScript define a different note style to display for these notes as well as custom behavior. (community feature by lemz1)
- Implemented support for Numeric and Selector options in the Options menu. (community feature by FlooferLand)
- Implemented new animations for Tankman and Pico
## Changed
- Girlfriend and Nene now perform previously unused animations when you achieve a large combo, or drop a large combo.
- The pixel character icons in the Freeplay menu now display an animation!

2
art

@ -1 +1 @@
Subproject commit bfca2ea98d11a0f4dee4a27b9390951fbc5701ea
Subproject commit 1f64f3e7403a090b164f4442d10152b2be5d3d0a

2
assets

@ -1 +1 @@
Subproject commit bc7009b4242691faa5c4552f7ca8a2f28e8cb1d2
Subproject commit 8140f8255c0db78135dbfa7b6d329f52c363f51b

View file

@ -25,7 +25,7 @@
"name": "flixel-addons",
"type": "git",
"dir": null,
"ref": "9c6fb47968e894eb36bf10e94725cd7640c49281",
"ref": "7424db4e9164ff46f224a7c47de1b732d2542dd7",
"url": "https://github.com/FunkinCrew/flixel-addons"
},
{
@ -70,14 +70,14 @@
"name": "haxeui-core",
"type": "git",
"dir": null,
"ref": "22f7c5a8ffca90d4677cffd6e570f53761709fbc",
"ref": "c9d96b168ea2a19274ad7c766ab1a34b57baa793",
"url": "https://github.com/haxeui/haxeui-core"
},
{
"name": "haxeui-flixel",
"type": "git",
"dir": null,
"ref": "28bb710d0ae5d94b5108787593052165be43b980",
"ref": "013b9d4e56bfe9a034e028a8d685f0b274cb73c4",
"url": "https://github.com/haxeui/haxeui-flixel"
},
{
@ -98,8 +98,8 @@
"name": "hxcpp",
"type": "git",
"dir": null,
"ref": "904ea40643b050a5a154c5e4c33a83fd2aec18b1",
"url": "https://github.com/HaxeFoundation/hxcpp"
"ref": "c6bac3d6c7d683f25104296b2f4c50f8c90b8349",
"url": "https://github.com/cortex-engine/hxcpp"
},
{
"name": "hxcpp-debug-server",
@ -205,4 +205,4 @@
"url": "https://github.com/fponticelli/thx.semver"
}
]
}
}

View file

@ -149,6 +149,21 @@ class Project extends HXProject {
*/
static final FEATURE_DEBUG_FUNCTIONS:FeatureFlag = "FEATURE_DEBUG_FUNCTIONS";
/**
* `-DFEATURE_DEBUG_TRACY`
* If this flag is enabled, the game will have the necessary hooks for the Tracy profiler.
* Only enable this if you're using the correct fork of Haxe to support this.
* @see https://github.com/HaxeFoundation/hxcpp/pull/1153
*/
static final FEATURE_DEBUG_TRACY:FeatureFlag = "FEATURE_DEBUG_TRACY";
/**
* `-DFEATURE_LOG_TRACE`
* If this flag is enabled, the game will print debug traces to the console.
* Disable to improve performance a bunch.
*/
static final FEATURE_LOG_TRACE:FeatureFlag = "FEATURE_LOG_TRACE";
/**
* `-DFEATURE_DISCORD_RPC`
* If this flag is enabled, the game will enable the Discord Remote Procedure Call library.
@ -453,6 +468,7 @@ class Project extends HXProject {
// Should be true on debug builds or if GITHUB_BUILD is enabled.
FEATURE_DEBUG_FUNCTIONS.apply(this, isDebug() || GITHUB_BUILD.isEnabled(this));
FEATURE_LOG_TRACE.apply(this, isDebug());
// Should default to true on workspace builds and false on release builds.
REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop());
@ -516,6 +532,16 @@ class Project extends HXProject {
// Cleaner looking compiler errors.
setHaxedef("message.reporting", "pretty");
if (FEATURE_DEBUG_TRACY.isEnabled(this)) {
setHaxedef("HXCPP_TELEMETRY"); // Enable telemetry
setHaxedef("HXCPP_TRACY"); // Enable Tracy telemetry
setHaxedef("HXCPP_TRACY_MEMORY"); // Track memory allocations
setHaxedef("HXCPP_TRACY_ON_DEMAND"); // Only collect telemetry when Tracy is open and reachable
// setHaxedef("HXCPP_TRACY_INCLUDE_CALLSTACKS"); // Inspect callstacks per zone, inflating telemetry data
setHaxedef("absolute-paths"); // Fix source locations so Tracy can see them
}
}
/**
@ -941,6 +967,8 @@ class Project extends HXProject {
var commitHash:String = process.stdout.readLine();
var commitHashSplice:String = commitHash.substr(0, 7);
process.close();
return commitHashSplice;
}
@ -955,6 +983,8 @@ class Project extends HXProject {
var branchName:String = branchProcess.stdout.readLine();
branchProcess.close();
return branchName;
}
@ -979,6 +1009,8 @@ class Project extends HXProject {
}
}
branchProcess.close();
return output.length > 0;
}

View file

@ -2,6 +2,7 @@ package;
import flixel.FlxGame;
import flixel.FlxState;
import funkin.Preferences;
import funkin.util.logging.CrashHandler;
import funkin.ui.debug.MemoryCounter;
import funkin.save.Save;
@ -22,12 +23,6 @@ class Main extends Sprite
var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom).
var initialState:Class<FlxState> = funkin.InitState; // The FlxState the game starts with.
var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions.
#if (web || CHEEMS)
var framerate:Int = 60; // How many frames per second the game should run at.
#else
// TODO: This should probably be in the options menu?
var framerate:Int = 144; // How many frames per second the game should run at.
#end
var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode.
var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets
@ -109,7 +104,7 @@ class Main extends Sprite
// George recommends binding the save before FlxGame is created.
Save.load();
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen);
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash, startFullscreen);
// FlxG.game._customSoundTray wants just the class, it calls new from
// create() in there, which gets called when it's added to stage
@ -123,8 +118,6 @@ class Main extends Sprite
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
#end
addChild(fpsCounter);
#if hxcpp_debug_server
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
#else

View file

@ -0,0 +1,102 @@
/*
* Pulled from Tracey profiler PR
* @see https://github.com/HaxeFoundation/haxe/pull/11772
*/
package cpp.vm.tracy;
#if (!HXCPP_TRACY)
#error "This class cannot be used without -D HXCPP_TRACY"
#end
enum abstract PlotFormatType(cpp.UInt8) from cpp.UInt8 to cpp.UInt8
{
var Number = 0;
var Memory = 1;
var Percentage = 2;
}
@:include('hx/TelemetryTracy.h')
extern class Native_TracyProfiler
{
/**
Mark a frame. Call this at the end of each frame loop.
**/
@:native('::__hxcpp_tracy_framemark')
public static function frameMark():Void;
/**
Print a message into Tracy's log.
**/
@:native('::__hxcpp_tracy_message')
public static function message(_msg:String, ?_color:Int = 0x000000):Void;
/**
Tracy can collect additional information about the profiled application,
which will be available in the trace description.
This can include data such as the source repository revision,
the applications environment (dev/prod), etc.
**/
@:native('::__hxcpp_tracy_message_app_info')
public static function messageAppInfo(_info:String):Void;
/**
Plot a named value to tracy. This will generate a graph in the profiler for you.
**/
@:native('::__hxcpp_tracy_plot')
public static function plot(_name:String, _val:cpp.Float32):Void;
/**
Configure how values are plotted and displayed.
**/
@:native('::__hxcpp_tracy_plot_config')
public static function plotConfig(_name:String, _format:PlotFormatType, ?_step:Bool = false, ?_fill:Bool = false, ?_color:Int = 0x000000):Void;
/**
Set a name for the current thread this function is called in. Supply an optional groupHint so threads become grouped in Tracy's UI.
**/
@:native('::__hxcpp_tracy_set_thread_name_and_group')
public static function setThreadName(_name:String, ?_groupHint:Int = 1):Void;
/**
Create a custom named scoped zone in your code.
**/
@:native('HXCPP_TRACY_ZONE')
public static function zoneScoped(_name:String):Void;
}
#if (scriptable || cppia)
class Cppia_TracyProfiler
{
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.frameMark)
public static function frameMark()
Native_TracyProfiler.frameMark();
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.message)
public static function message(_msg:String, ?_color:Int = 0x000000)
Native_TracyProfiler.message(_msg, _color);
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.messageAppInfo)
public static function messageAppInfo(_info:String)
Native_TracyProfiler.messageAppInfo(_info);
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.plot)
public static function plot(_name:String, _val:Float)
Native_TracyProfiler.plot(_name, _val);
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.plotConfig)
public static function plotConfig(_name:String, _format:PlotFormatType, ?_step:Bool = false, ?_fill:Bool = false, ?_color:Int = 0x000000)
Native_TracyProfiler.plotConfig(_name, _format, _step, _fill, _color);
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.setThreadName)
public static function setThreadName(_name:String, ?_groupHint:Int = 1)
Native_TracyProfiler.setThreadName(_name, _groupHint);
@:inheritDoc(cpp.vm.tracy.Native_TracyProfiler.zoneScoped)
public static function zoneScoped(_name:String)
Native_TracyProfiler.zoneScoped(_name);
}
typedef TracyProfiler = Cppia_TracyProfiler;
#else
typedef TracyProfiler = Native_TracyProfiler;
#end

View file

@ -1,38 +1,161 @@
package funkin;
import openfl.utils.Future;
/**
* A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions.
* Later we'll add Funkin-specific caching to this.
*/
class Assets
{
public static function getText(path:String):String
/**
* Get the file system path for an asset
* @param path The asset path to load from, relative to the assets folder
* @return The path to the asset on the file system
*/
public static function getPath(path:String):String
{
return openfl.utils.Assets.getText(path);
}
public static function getMusic(path:String):openfl.media.Sound
{
return openfl.utils.Assets.getMusic(path);
}
public static function getBitmapData(path:String):openfl.display.BitmapData
{
return openfl.utils.Assets.getBitmapData(path);
return openfl.utils.Assets.getPath(path);
}
/**
* Load bytes from an asset
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The byte contents of the file
*/
public static function getBytes(path:String):haxe.io.Bytes
{
return openfl.utils.Assets.getBytes(path);
}
/**
* Load bytes from an asset asynchronously
* @param path The asset path to load from
* @return A future which promises to return the byte contents of the file
*/
public static function loadBytes(path:String):Future<openfl.utils.ByteArray>
{
return openfl.utils.Assets.loadBytes(path);
}
/**
* Load text from an asset.
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The text contents of the file
*/
public static function getText(path:String):String
{
return openfl.utils.Assets.getText(path);
}
/**
* Load text from an asset asynchronously
* @param path The asset path to load from
* @return A future which promises to return the text contents of the file
*/
public static function loadText(path:String):Future<String>
{
return openfl.utils.Assets.loadText(path);
}
/**
* Load a Sound file from an asset
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The loaded sound
*/
public static function getSound(path:String):openfl.media.Sound
{
return openfl.utils.Assets.getSound(path);
}
/**
* Load a Sound file from an asset asynchronously
* @param path The asset path to load from
* @return A future which promises to return the loaded sound
*/
public static function loadSound(path:String):Future<openfl.media.Sound>
{
return openfl.utils.Assets.loadSound(path);
}
/**
* Load a Sound file from an asset, with optimizations specific to long-duration music
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The loaded sound
*/
public static function getMusic(path:String):openfl.media.Sound
{
return openfl.utils.Assets.getMusic(path);
}
/**
* Load a Sound file from an asset asynchronously, with optimizations specific to long-duration music
* @param path The asset path to load from
* @return A future which promises to return the loaded sound
*/
public static function loadMusic(path:String):Future<openfl.media.Sound>
{
return openfl.utils.Assets.loadMusic(path);
}
/**
* Load a Bitmap from an asset
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The loaded Bitmap image
*/
public static function getBitmapData(path:String):openfl.display.BitmapData
{
return openfl.utils.Assets.getBitmapData(path);
}
/**
* Load a Bitmap from an asset asynchronously
* @param path The asset path to load from
* @return The future which promises to return the loaded Bitmap image
*/
public static function loadBitmapData(path:String):Future<openfl.display.BitmapData>
{
return openfl.utils.Assets.loadBitmapData(path);
}
/**
* Determines whether the given asset of the given type exists.
* @param path The asset path to check
* @param type The asset type to check
* @return Whether the asset exists
*/
public static function exists(path:String, ?type:openfl.utils.AssetType):Bool
{
return openfl.utils.Assets.exists(path, type);
}
public static function list(type:openfl.utils.AssetType):Array<String>
/**
* Retrieve a list of all assets of the given type
* @param type The asset type to check
* @return A list of asset paths
*/
public static function list(?type:openfl.utils.AssetType):Array<String>
{
return openfl.utils.Assets.list(type);
}
public static function hasLibrary(name:String):Bool
{
return openfl.utils.Assets.hasLibrary(name);
}
public static function getLibrary(name:String):lime.utils.AssetLibrary
{
return openfl.utils.Assets.getLibrary(name);
}
public static function loadLibrary(name:String):Future<openfl.utils.AssetLibrary>
{
return openfl.utils.Assets.loadLibrary(name);
}
}

View file

@ -275,6 +275,13 @@ class Conductor
return Save.instance.options.audioVisualOffset;
}
public var combinedOffset(get, never):Float;
function get_combinedOffset():Float
{
return instrumentalOffset + audioVisualOffset + inputOffset;
}
/**
* The number of beats in a measure. May be fractional depending on the time signature.
*/
@ -397,9 +404,12 @@ class Conductor
*/
public function update(?songPos:Float, applyOffsets:Bool = true, forceDispatch:Bool = false)
{
var currentTime:Float = (FlxG.sound.music != null) ? FlxG.sound.music.time : 0.0;
var currentLength:Float = (FlxG.sound.music != null) ? FlxG.sound.music.length : 0.0;
if (songPos == null)
{
songPos = (FlxG.sound.music != null) ? FlxG.sound.music.time : 0.0;
songPos = currentTime;
}
// Take into account instrumental and file format song offsets.
@ -409,8 +419,17 @@ class Conductor
var oldBeat:Float = this.currentBeat;
var oldStep:Float = this.currentStep;
// If the song is playing, limit the song position to the length of the song or beginning of the song.
if (FlxG.sound.music != null && FlxG.sound.music.playing)
{
this.songPosition = Math.min(currentLength, Math.max(0, songPos));
}
else
{
this.songPosition = songPos;
}
// Set the song position we are at (for purposes of calculating note positions, etc).
this.songPosition = songPos;
currentTimeChange = timeChanges[0];
if (this.songPosition > 0.0)
@ -430,7 +449,8 @@ class Conductor
else if (currentTimeChange != null && this.songPosition > 0.0)
{
// roundDecimal prevents representing 8 as 7.9999999
this.currentStepTime = FlxMath.roundDecimal((currentTimeChange.beatTime * Constants.STEPS_PER_BEAT) + (this.songPosition - currentTimeChange.timeStamp) / stepLengthMs, 6);
this.currentStepTime = FlxMath.roundDecimal((currentTimeChange.beatTime * Constants.STEPS_PER_BEAT)
+ (this.songPosition - currentTimeChange.timeStamp) / stepLengthMs, 6);
this.currentBeatTime = currentStepTime / Constants.STEPS_PER_BEAT;
this.currentMeasureTime = currentStepTime / stepsPerMeasure;
this.currentStep = Math.floor(currentStepTime);

View file

@ -2,7 +2,6 @@ package funkin;
import flixel.graphics.frames.FlxAtlasFrames;
import openfl.utils.AssetType;
import openfl.utils.Assets as OpenFlAssets;
/**
* A core class which handles determining asset paths.
@ -44,11 +43,11 @@ class Paths
if (currentLevel != null)
{
var levelPath:String = getLibraryPathForce(file, currentLevel);
if (OpenFlAssets.exists(levelPath, type)) return levelPath;
if (Assets.exists(levelPath, type)) return levelPath;
}
var levelPath:String = getLibraryPathForce(file, 'shared');
if (OpenFlAssets.exists(levelPath, type)) return levelPath;
if (Assets.exists(levelPath, type)) return levelPath;
return getPreloadPath(file);
}

View file

@ -7,6 +7,35 @@ import funkin.save.Save;
*/
class Preferences
{
/**
* FPS
* @default `60`
*/
public static var framerate(get, set):Int;
static function get_framerate():Int
{
#if web
return 60;
#else
return Save?.instance?.options?.framerate ?? 60;
#end
}
static function set_framerate(value:Int):Int
{
#if web
return 60;
#else
var save:Save = Save.instance;
save.options.framerate = value;
save.flush();
FlxG.updateFramerate = value;
FlxG.drawFramerate = value;
return value;
#end
}
/**
* Whether some particularly fowl language is displayed.
* @default `true`

View file

@ -550,11 +550,12 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
/**
* Stop all sounds in the pool and allow them to be recycled.
*/
public static function stopAllAudio(musicToo:Bool = false):Void
public static function stopAllAudio(musicToo:Bool = false, persistToo:Bool = false):Void
{
for (sound in pool)
{
if (sound == null) continue;
if (!persistToo && sound.persist) continue;
if (!musicToo && sound == FlxG.sound.music) continue;
sound.destroy();
}

View file

@ -264,12 +264,32 @@ class PlayerCharSelectData
*/
@:optional
public var position:Null<Int>;
/**
* The GF name to assign for this character.
*/
@:optional
public var gf:PlayerCharSelectGFData;
}
typedef PlayerCharSelectGFData =
{
@:optional
public var assetPath:String;
@:optional
public var animInfoPath:String;
@:optional
@:default(false)
public var visualizer:Bool;
}
typedef PlayerResultsData =
{
var music:PlayerResultsMusicData;
var perfectGold:Array<PlayerResultsAnimationData>;
var perfect:Array<PlayerResultsAnimationData>;
var excellent:Array<PlayerResultsAnimationData>;
var great:Array<PlayerResultsAnimationData>;

View file

@ -0,0 +1,40 @@
package funkin.effects;
import flixel.addons.effects.FlxTrail;
import funkin.play.stage.Bopper;
import flixel.FlxSprite;
import flixel.system.FlxAssets;
/**
* An offshoot of FlxTrail, but accomodates the way Funkin
* does offsets for characters. example, fixes Spirits trail
*/
class FunkTrail extends FlxTrail
{
/**
* Creates a new FunkTrail effect for a specific FlxSprite.
*
* @param Target The FlxSprite the trail is attached to.
* @param Graphic The image to use for the trailsprites. Optional, uses the sprite's graphic if null.
* @param Length The amount of trailsprites to create.
* @param Delay How often to update the trail. 0 updates every frame.
* @param Alpha The alpha value for the very first trailsprite.
* @param Diff How much lower the alpha of the next trailsprite is.
*/
public function new(Target:FlxSprite, ?Graphic:FlxGraphicAsset, Length:Int = 10, Delay:Int = 3, Alpha:Float = 0.4, Diff:Float = 0.05)
{
super(Target, Graphic, Length, Delay, Alpha, Diff);
}
override public function update(elapsed:Float):Void
{
if (target is Bopper)
{
var targ:Bopper = cast target;
@:privateAccess effectOffset.set((targ.animOffsets[0] - targ.globalOffsets[0]) * targ.scale.x,
(targ.animOffsets[1] - targ.globalOffsets[1]) * targ.scale.y);
}
super.update(elapsed);
}
}

View file

@ -7,7 +7,6 @@ import flxanimate.frames.FlxAnimateFrames;
import flixel.graphics.frames.FlxFrame;
import flixel.system.FlxAssets.FlxGraphicAsset;
import openfl.display.BitmapData;
import openfl.utils.Assets;
import flixel.math.FlxPoint;
import flxanimate.animate.FlxKeyFrame;
@ -184,7 +183,7 @@ class FlxAtlasSprite extends FlxAnimate
// Move to the first frame of the animation.
// goToFrameLabel(id);
trace('Playing animation $id');
// trace('Playing animation $id');
if ((id == null || id == "") || this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null))
{
this.anim.play(id, restart, false, startFrame);

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class AdjustColorShader extends FlxRuntimeShader
{
@ -52,4 +50,9 @@ class AdjustColorShader extends FlxRuntimeShader
return this.contrast;
}
public override function toString():String
{
return 'AdjustColorShader(${this.hue}, ${this.saturation}, ${this.brightness}, ${this.contrast})';
}
}

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
import openfl.display.BitmapData;
import openfl.display.ShaderInput;

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
/**
* Note... not actually gaussian!

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class Grayscale extends FlxRuntimeShader
{

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class HSVShader extends FlxRuntimeShader
{

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import openfl.utils.Assets;
import funkin.Paths;
import flixel.math.FlxPoint;
class MosaicEffect extends FlxRuntimeShader

View file

@ -2,7 +2,6 @@ package funkin.graphics.shaders;
import openfl.display.BitmapData;
import openfl.display.BlendMode;
import openfl.utils.Assets;
class RuntimeCustomBlendShader extends RuntimePostEffectShader
{

View file

@ -5,7 +5,6 @@ import openfl.display.BitmapData;
import openfl.display.ShaderParameter;
import openfl.display.ShaderParameterType;
import flixel.util.FlxColor;
import openfl.utils.Assets;
typedef Light =
{

View file

@ -0,0 +1,34 @@
package funkin.graphics.video;
#if hxCodec
import hxcodec.flixel.FlxVideoSprite;
/**
* Not to be confused with FlxVideo, this is a hxcodec based video class
* We override it simply to correct/control our volume easier.
*/
class FunkinVideoSprite extends FlxVideoSprite
{
public var volume(default, set):Float = 1;
public function new(x:Float = 0, y:Float = 0)
{
super(x, y);
set_volume(1);
}
override public function update(elapsed:Float):Void
{
super.update(elapsed);
set_volume(volume);
}
function set_volume(value:Float):Float
{
volume = value;
bitmap.volume = Std.int((FlxG.sound.muted ? 0 : 1) * (FlxG.sound.logToLinear(FlxG.sound.volume) * 100) * volume);
return volume;
}
}
#end

View file

@ -3,6 +3,7 @@ package;
#if !macro
// Only import these when we aren't in a macro.
import funkin.util.Constants;
import funkin.Assets;
import funkin.Paths;
import funkin.Preferences;
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.

View file

@ -1,7 +1,6 @@
package funkin.input;
import haxe.ui.backend.flixel.CursorHelper;
import openfl.utils.Assets;
import lime.app.Future;
import openfl.display.BitmapData;

View file

@ -228,6 +228,8 @@ class PolymodHandler
static function buildImports():Void
{
// Add default imports for common classes.
Polymod.addDefaultImport(funkin.Assets);
Polymod.addDefaultImport(funkin.Paths);
// Add import aliases for certain classes.
// NOTE: Scripted classes are automatically aliased to their parent class.
@ -258,7 +260,7 @@ class PolymodHandler
Polymod.blacklistImport('cpp.Lib');
// `Unserializer`
// Unserializerr.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages
// Unserializer.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages
Polymod.blacklistImport('Unserializer');
// `lime.system.CFFI`

View file

@ -125,7 +125,6 @@ class ModuleHandler
for (key => value in moduleCache)
{
ScriptEventDispatcher.callEvent(value, event);
moduleCache.remove(key);
}
moduleCache.clear();

View file

@ -11,7 +11,6 @@ import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
import flixel.util.FlxTimer;
import funkin.util.EaseUtil;
import funkin.audio.FunkinSound;
import openfl.utils.Assets;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle;

View file

@ -15,7 +15,6 @@ import funkin.ui.freeplay.FreeplayState;
import funkin.ui.MusicBeatSubState;
import funkin.ui.story.StoryMenuState;
import funkin.util.MathUtil;
import openfl.utils.Assets;
import funkin.effects.RetroCameraFade;
import flixel.math.FlxPoint;

View file

@ -687,7 +687,11 @@ class PlayState extends MusicBeatSubState
}
Conductor.instance.mapTimeChanges(currentChart.timeChanges);
Conductor.instance.update((Conductor.instance.beatLengthMs * -5) + startTimestamp);
var pre:Float = (Conductor.instance.beatLengthMs * -5) + startTimestamp;
trace('Attempting to start at ' + pre);
Conductor.instance.update(pre);
// The song is now loaded. We can continue to initialize the play state.
initCameras();
@ -857,7 +861,7 @@ class PlayState extends MusicBeatSubState
// Reset music properly.
if (FlxG.sound.music != null)
{
FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
FlxG.sound.music.time = startTimestamp - Conductor.instance.combinedOffset;
FlxG.sound.music.pitch = playbackRate;
FlxG.sound.music.pause();
}
@ -874,7 +878,7 @@ class PlayState extends MusicBeatSubState
}
}
vocals.pause();
vocals.time = 0;
vocals.time = 0 - Conductor.instance.combinedOffset;
if (FlxG.sound.music != null) FlxG.sound.music.volume = 1;
vocals.volume = 1;
@ -915,7 +919,11 @@ class PlayState extends MusicBeatSubState
{
// Do NOT apply offsets at this point, because they already got applied the previous frame!
Conductor.instance.update(Conductor.instance.songPosition + elapsed * 1000, false);
if (Conductor.instance.songPosition >= (startTimestamp)) startSong();
if (Conductor.instance.songPosition >= (startTimestamp + Conductor.instance.combinedOffset))
{
trace("started song at " + Conductor.instance.songPosition);
startSong();
}
}
}
else
@ -1391,15 +1399,18 @@ class PlayState extends MusicBeatSubState
// activeNotes.sort(SortUtil.byStrumtime, FlxSort.DESCENDING);
}
if (!startingSong
&& FlxG.sound.music != null
&& (Math.abs(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 100
|| Math.abs(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset)) > 100))
if (FlxG.sound.music != null)
{
trace("VOCALS NEED RESYNC");
if (vocals != null) trace(vocals.checkSyncError(Conductor.instance.songPosition + Conductor.instance.instrumentalOffset));
trace(FlxG.sound.music.time - (Conductor.instance.songPosition + Conductor.instance.instrumentalOffset));
resyncVocals();
var correctSync:Float = Math.min(FlxG.sound.music.length, Math.max(0, Conductor.instance.songPosition - Conductor.instance.combinedOffset));
if (!startingSong && (Math.abs(FlxG.sound.music.time - correctSync) > 5 || Math.abs(vocals.checkSyncError(correctSync)) > 5))
{
trace("VOCALS NEED RESYNC");
if (vocals != null) trace(vocals.checkSyncError(correctSync));
trace(FlxG.sound.music.time);
trace(correctSync);
resyncVocals();
}
}
// Only bop camera if zoom level is below 135%
@ -1612,7 +1623,7 @@ class PlayState extends MusicBeatSubState
if (girlfriend != null)
{
girlfriend.characterType = CharacterType.GF;
// Don't need to do anything.
}
else if (currentCharacterData.girlfriend != '')
{
@ -1630,8 +1641,6 @@ class PlayState extends MusicBeatSubState
if (dad != null)
{
dad.characterType = CharacterType.DAD;
//
// OPPONENT HEALTH ICON
//
@ -1650,8 +1659,6 @@ class PlayState extends MusicBeatSubState
if (boyfriend != null)
{
boyfriend.characterType = CharacterType.BF;
//
// PLAYER HEALTH ICON
//
@ -1956,7 +1963,7 @@ class PlayState extends MusicBeatSubState
};
// A negative instrumental offset means the song skips the first few milliseconds of the track.
// This just gets added into the startTimestamp behavior so we don't need to do anything extra.
FlxG.sound.music.play(true, startTimestamp - Conductor.instance.instrumentalOffset);
FlxG.sound.music.play(true, Math.max(0, startTimestamp - Conductor.instance.combinedOffset));
FlxG.sound.music.pitch = playbackRate;
// Prevent the volume from being wrong.
@ -1968,6 +1975,9 @@ class PlayState extends MusicBeatSubState
vocals.play();
vocals.volume = 1.0;
vocals.pitch = playbackRate;
vocals.time = FlxG.sound.music.time;
trace('${FlxG.sound.music.time}');
trace('${vocals.time}');
resyncVocals();
#if FEATURE_DISCORD_RPC
@ -1977,7 +1987,7 @@ class PlayState extends MusicBeatSubState
if (startTimestamp > 0)
{
// FlxG.sound.music.time = startTimestamp - Conductor.instance.instrumentalOffset;
// FlxG.sound.music.time = startTimestamp - Conductor.instance.combinedOffset;
handleSkippedNotes();
}
@ -1994,7 +2004,8 @@ class PlayState extends MusicBeatSubState
// Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
if (!(FlxG.sound.music?.playing ?? false)) return;
var timeToPlayAt:Float = Conductor.instance.songPosition - Conductor.instance.instrumentalOffset;
var timeToPlayAt:Float = Math.min(FlxG.sound.music.length, Math.max(0, Conductor.instance.songPosition - Conductor.instance.combinedOffset));
trace('Resyncing vocals to ${timeToPlayAt}');
FlxG.sound.music.pause();
vocals.pause();
@ -2374,9 +2385,9 @@ class PlayState extends MusicBeatSubState
if (targetNote == null) continue;
// Judge and hit the note.
trace('Hit note! ${targetNote.noteData}');
// trace('Hit note! ${targetNote.noteData}');
goodNoteHit(targetNote, input);
trace('Score: ${songScore}');
// trace('Score: ${songScore}');
notesInDirection.remove(targetNote);
@ -3178,7 +3189,7 @@ class PlayState extends MusicBeatSubState
/**
* Resets the camera's zoom level and focus point.
*/
public function resetCamera(?resetZoom:Bool = true, ?cancelTweens:Bool = true):Void
public function resetCamera(?resetZoom:Bool = true, ?cancelTweens:Bool = true, ?snap:Bool = true):Void
{
// Cancel camera tweens if any are active.
if (cancelTweens)
@ -3195,7 +3206,7 @@ class PlayState extends MusicBeatSubState
}
// Snap the camera to the follow point immediately.
FlxG.camera.focusOn(cameraFollowPoint.getPosition());
if (snap) FlxG.camera.focusOn(cameraFollowPoint.getPosition());
}
/**

View file

@ -472,9 +472,12 @@ class ResultState extends MusicBeatSubState
{
ease: FlxEase.quartOut,
onUpdate: _ -> {
clearPercentLerp = Math.round(clearPercentLerp);
clearPercentCounter.curNumber = Math.round(clearPercentCounter.curNumber);
// Only play the tick sound if the number increased.
if (clearPercentLerp != clearPercentCounter.curNumber)
{
trace('$clearPercentLerp and ${clearPercentCounter.curNumber}');
clearPercentLerp = clearPercentCounter.curNumber;
FunkinSound.playOnce(Paths.sound('scrollMenu'));
}

View file

@ -11,7 +11,7 @@ import funkin.play.character.ScriptedCharacter.ScriptedSparrowCharacter;
import funkin.util.assets.DataAssets;
import funkin.util.VersionUtil;
import haxe.Json;
import openfl.utils.Assets;
import flixel.graphics.frames.FlxFrame;
class CharacterDataParser
{
@ -281,41 +281,78 @@ class CharacterDataParser
}
/**
* TODO: Hardcode this.
* Returns the idle frame of a character.
*/
public static function getCharPixelIconAsset(char:String):String
public static function getCharPixelIconAsset(char:String):FlxFrame
{
var icon:String = char;
var charPath:String = "freeplay/icons/";
switch (icon)
// FunkinCrew please dont skin me alive for copying pixelated icon and changing it a tiny bit
switch (char)
{
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf":
icon = "bf";
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf" | "bf-dark":
charPath += "bfpixel";
case "monster-christmas":
icon = "monster";
charPath += "monsterpixel";
case "mom" | "mom-car":
icon = "mommy";
charPath += "mommypixel";
case "pico-blazin" | "pico-playable" | "pico-speaker":
icon = "pico";
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
icon = "gf";
charPath += "picopixel";
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen" | "gf-dark":
charPath += "gfpixel";
case "dad":
icon = "daddy";
charPath += "dadpixel";
case "darnell-blazin":
icon = "darnell";
charPath += "darnellpixel";
case "senpai-angry":
icon = "senpai";
charPath += "senpaipixel";
case "spooky-dark":
icon = "spooky";
charPath += "spookypixel";
case "tankman-atlas":
icon = "tankman";
charPath += "tankmanpixel";
case "pico-christmas" | "pico-dark":
charPath += "picopixel";
default:
charPath += '${char}pixel';
}
var path = Paths.image("freeplay/icons/" + icon + "pixel");
if (Assets.exists(path)) return path;
if (!Assets.exists(Paths.image(charPath)))
{
trace('[WARN] Character ${char} has no freeplay icon.');
return null;
}
// TODO: Hardcode some additional behavior or a fallback.
return null;
var isAnimated = Assets.exists(Paths.file('images/$charPath.xml'));
var frame:FlxFrame = null;
if (isAnimated)
{
var frames = Paths.getSparrowAtlas(charPath);
var idleFrame:FlxFrame = frames.frames.find(function(frame:FlxFrame):Bool {
return frame.name.startsWith('idle');
});
if (idleFrame == null)
{
trace('[WARN] Character ${char} has no idle in their freeplay icon.');
return null;
}
// so, haxe.ui.backend.AssetsImpl uses the parent width and height, which makes the image go crazy when rendered
// so this is a work around so that it uses the actual width and height
var imageGraphic = flixel.graphics.FlxGraphic.fromFrame(idleFrame);
var imageFrame = flixel.graphics.frames.FlxImageFrame.fromImage(imageGraphic);
frame = imageFrame.frame;
}
else
{
var imageFrame = flixel.graphics.frames.FlxImageFrame.fromImage(Paths.image(charPath));
frame = imageFrame.frame;
}
return frame;
}
/**

View file

@ -5,7 +5,6 @@ import flixel.FlxSprite;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import funkin.play.character.CharacterData.CharacterDataParser;
import openfl.utils.Assets;
import funkin.graphics.FunkinSprite;
import funkin.util.MathUtil;

View file

@ -8,7 +8,6 @@ import funkin.graphics.FunkinSprite;
import funkin.play.PlayState;
import funkin.util.TimerUtil;
import funkin.util.EaseUtil;
import openfl.utils.Assets;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle;
@ -95,7 +94,6 @@ class PopUpStuff extends FlxTypedGroup<FunkinSprite>
if (numScore == null) continue;
numScore.x = (FlxG.width * 0.507) - (36 * daLoop) - 65;
trace('numScore($daLoop) = ${numScore.x}');
numScore.y = (FlxG.camera.height * 0.44);
numScore.x += offsets[0];

View file

@ -11,7 +11,7 @@ import flixel.util.FlxTimer;
import funkin.graphics.video.FlxVideo;
#end
#if hxCodec
import hxcodec.flixel.FlxVideoSprite;
import funkin.graphics.video.FunkinVideoSprite;
#end
/**
@ -26,7 +26,7 @@ class VideoCutscene
static var vid:FlxVideo;
#end
#if hxCodec
static var vid:FlxVideoSprite;
static var vid:FunkinVideoSprite;
#end
/**
@ -138,7 +138,7 @@ class VideoCutscene
static function playVideoNative(filePath:String):Void
{
// Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0);
vid = new FunkinVideoSprite(0, 0);
if (vid != null)
{

View file

@ -127,7 +127,8 @@ class FocusCameraSongEvent extends SongEvent
switch (ease)
{
case 'CLASSIC': // Old-school. No ease. Just set follow point.
PlayState.instance.resetCamera(false, true);
PlayState.instance.resetCamera(false, false, false);
PlayState.instance.cancelCameraFollowTween();
PlayState.instance.cameraFollowPoint.setPosition(targetX, targetY);
case 'INSTANT': // Instant ease. Duration is automatically 0.
PlayState.instance.tweenCameraToPosition(targetX, targetY, 0);

View file

@ -85,7 +85,8 @@ class StrumlineNote extends FlxSprite
noteStyle.applyStrumlineFrames(this);
noteStyle.applyStrumlineAnimations(this, this.direction);
this.setGraphicSize(Std.int(Strumline.STRUMLINE_SIZE * noteStyle.getStrumlineScale()));
var scale = noteStyle.getStrumlineScale();
this.scale.set(scale, scale);
this.updateHitbox();
noteStyle.applyStrumlineOffsets(this);

View file

@ -87,6 +87,7 @@ class SustainTrail extends FlxSprite
public var bottomClip:Float = 0.9;
public var isPixel:Bool;
public var noteStyleOffsets:Array<Float>;
var graphicWidth:Float = 0;
var graphicHeight:Float = 0;
@ -107,6 +108,7 @@ class SustainTrail extends FlxSprite
this.noteDirection = noteDirection;
setupHoldNoteGraphic(noteStyle);
noteStyleOffsets = noteStyle.getHoldNoteOffsets();
indices = new DrawData<Int>(12, true, TRIANGLE_VERTEX_INDICES);
@ -137,7 +139,6 @@ class SustainTrail extends FlxSprite
zoom = 1.0;
zoom *= noteStyle.fetchHoldNoteScale();
zoom *= 0.7;
// CALCULATE SIZE
graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2
@ -202,7 +203,7 @@ class SustainTrail extends FlxSprite
{
width = graphicWidth;
height = graphicHeight;
offset.set(0, 0);
offset.set(noteStyleOffsets[0], noteStyleOffsets[1]);
origin.set(width * 0.5, height * 0.5);
}

View file

@ -93,7 +93,8 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
buildNoteAnimations(target);
// Set the scale.
target.setGraphicSize(Strumline.STRUMLINE_SIZE * getNoteScale());
var scale = getNoteScale();
target.scale.set(scale, scale);
target.updateHitbox();
}
@ -102,7 +103,11 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
function buildNoteFrames(force:Bool = false):Null<FlxAtlasFrames>
{
var noteAssetPath = getNoteAssetPath();
if (noteAssetPath == null) return null;
if (noteAssetPath == null)
{
FlxG.log.warn('Note asset path not found: ${id}');
return null;
}
if (!FunkinSprite.isTextureCached(Paths.image(noteAssetPath)))
{
@ -224,6 +229,13 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return data?.scale ?? 1.0;
}
public function getHoldNoteOffsets():Array<Float>
{
var data = _data?.assets?.holdNote;
if (data == null && fallback != null) return fallback.getHoldNoteOffsets();
return data?.offsets ?? [0.0, 0.0];
}
public function applyStrumlineFrames(target:StrumlineNote):Void
{
// TODO: Add support for multi-Sparrow.
@ -304,9 +316,16 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return thx.Arrays.filterNull(result);
}
public function getStrumlineOffsets():Array<Float>
{
var data = _data?.assets?.noteStrumline;
if (data == null && fallback != null) return fallback.getStrumlineOffsets();
return data?.offsets ?? [0.0, 0.0];
}
public function applyStrumlineOffsets(target:StrumlineNote):Void
{
var offsets = _data?.assets?.noteStrumline?.offsets ?? [0.0, 0.0];
var offsets = getStrumlineOffsets();
target.x += offsets[0];
target.y += offsets[1];
}
@ -575,7 +594,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
var result = _data.assets.judgementBad?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false;
case "GO":
case "shit":
var result = _data.assets.judgementShit?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false;

View file

@ -16,7 +16,6 @@ import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
import funkin.modding.events.ScriptEvent;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.util.SortUtil;
import openfl.utils.Assets;
/**
* This is a data structure managing information about the current song.

View file

@ -467,6 +467,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
#end
}
// Set the characters type
character.characterType = charType;
// Add the character to the scene.
this.add(character);

View file

@ -17,7 +17,7 @@ import thx.semver.Version;
@:nullSafety
class Save
{
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.5";
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.4";
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x";
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
@ -34,19 +34,19 @@ class Save
{
if (_instance == null)
{
_instance = new Save(FlxG.save.data);
return _instance = load();
}
return _instance;
}
var data:RawSaveData;
public static function load():Void
public static function load():Save
{
trace("[SAVE] Loading save...");
// Bind save data.
loadFromSlot(1);
return loadFromSlot(1);
}
/**
@ -65,7 +65,9 @@ class Save
public static function getDefault():RawSaveData
{
return {
version: Save.SAVE_DATA_VERSION,
// Version number is an abstract(Array) internally.
// This means it copies by reference, so merging save data overides the version number lol.
version: thx.Dynamics.clone(Save.SAVE_DATA_VERSION),
volume: 1.0,
mute: false,
@ -89,6 +91,7 @@ class Save
options:
{
// Reasonable defaults.
framerate: 60,
naughtyness: true,
downscroll: false,
flashingLights: true,
@ -433,7 +436,9 @@ class Save
{
if (!data.unlocks.charactersSeen.contains(character))
{
trace('Character seen: ' + character);
data.unlocks.charactersSeen.push(character);
trace('New characters seen list: ' + data.unlocks.charactersSeen);
flush();
}
}
@ -832,7 +837,7 @@ class Save
* If you set slot to `2`, it will load an independe
* @param slot
*/
static function loadFromSlot(slot:Int):Void
static function loadFromSlot(slot:Int):Save
{
trace("[SAVE] Loading save from slot " + slot + "...");
@ -850,12 +855,14 @@ class Save
trace('[SAVE] Found legacy save data, converting...');
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
FlxG.save.mergeData(gameSave.data, true);
return gameSave;
}
else
{
trace('[SAVE] No legacy save data found.');
var gameSave = new Save();
FlxG.save.mergeData(gameSave.data, true);
return gameSave;
}
}
else
@ -863,6 +870,8 @@ class Save
trace('[SAVE] Found existing save data.');
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
FlxG.save.mergeData(gameSave.data, true);
return gameSave;
}
}
@ -1133,6 +1142,12 @@ typedef SaveScoreTallyData =
*/
typedef SaveDataOptions =
{
/**
* FPS
* @default `60`
*/
var framerate:Int;
/**
* Whether some particularly fowl language is displayed.
* @default `true`

View file

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.4] - 2024-09-12
Note to self: Only update to 2.1.0 when migration is needed.
### Added
- `unlocks.charactersSeen:Array<String>` to `Save`
- `unlocks.oldChar:Bool` to `Save`
## [2.0.5] - 2024-05-21
### Fixed
- Resolved an issue where HTML5 wouldn't store the semantic version properly, causing the game to fail to load the save.

View file

@ -37,17 +37,25 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler
return _conductorInUse = value;
}
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
{
super();
this.bgColor = bgColor;
}
var controls(get, never):Controls;
inline function get_controls():Controls
return PlayerSettings.player1.controls;
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
{
super();
this.bgColor = bgColor;
initCallbacks();
}
function initCallbacks()
{
subStateOpened.add(onOpenSubStateComplete);
subStateClosed.add(onCloseSubStateComplete);
}
override function create():Void
{
super.create();

View file

@ -11,6 +11,7 @@ import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass;
import flixel.math.FlxMath;
import funkin.modding.events.ScriptEvent;
import funkin.vis.dsp.SpectralAnalyzer;
import funkin.data.freeplay.player.PlayerRegistry;
class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
{
@ -27,7 +28,8 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
var analyzer:SpectralAnalyzer;
var curGF:GFChar = GF;
var currentGFPath:Null<String>;
var enableVisualizer:Bool = false;
public function new()
{
@ -97,7 +99,7 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
function drawFFT()
{
if (curGF == NENE)
if (enableVisualizer)
{
var levels = analyzer.getLevels();
var frame = anim.curSymbol.timeline.get("VIZ_bars").get(anim.curFrame);
@ -172,28 +174,33 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
*/
public function switchGF(bf:String):Void
{
var prevGF:GFChar = curGF;
switch (bf)
{
case "pico":
curGF = NENE;
case "bf":
curGF = GF;
default:
curGF = GF;
}
var previousGFPath = currentGFPath;
var bfObj = PlayerRegistry.instance.fetchEntry(bf);
var gfData = bfObj?.getCharSelectData()?.gf;
currentGFPath = gfData?.assetPath != null ? Paths.animateAtlas(gfData?.assetPath) : null;
// We don't need to update any anims if we didn't change GF
if (prevGF != curGF)
trace('currentGFPath(${currentGFPath})');
if (currentGFPath == null)
{
loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill"));
this.visible = false;
return;
}
else if (previousGFPath != currentGFPath)
{
this.visible = true;
loadAtlas(currentGFPath);
animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt"));
animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt"));
enableVisualizer = gfData?.visualizer ?? false;
var animInfoPath = Paths.file('images/${gfData?.animInfoPath}');
animInInfo = FramesJSFLParser.parse(animInfoPath + '/In.txt');
animOutInfo = FramesJSFLParser.parse(animInfoPath + '/Out.txt');
}
playAnimation("idle", true, false, false);
// addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false));
updateHitbox();
}
@ -213,9 +220,3 @@ enum FadeStatus
FADE_OUT;
FADE_IN;
}
enum abstract GFChar(String) from String to String
{
var GF = "gf";
var NENE = "nene";
}

View file

@ -47,7 +47,6 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass
//
if (getCurrentAnimation() == "idle")
{
trace('Player beat hit');
playAnimation("idle", true, false, false);
}
};

View file

@ -71,6 +71,7 @@ class CharSelectSubState extends MusicBeatSubState
var availableChars:Map<Int, String> = new Map<Int, String>();
var pressedSelect:Bool = false;
var selectTimer:FlxTimer = new FlxTimer();
var allowInput:Bool = false;
var selectSound:FunkinSound;
var unlockSound:FunkinSound;
@ -430,6 +431,8 @@ class CharSelectSubState extends MusicBeatSubState
overrideExisting: true,
restartTrack: true,
onLoad: function() {
allowInput = true;
@:privateAccess
gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1);
#if desktop
@ -573,6 +576,8 @@ class CharSelectSubState extends MusicBeatSubState
overrideExisting: true,
restartTrack: true,
onLoad: function() {
allowInput = true;
@:privateAccess
gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1);
#if desktop
@ -642,6 +647,7 @@ class CharSelectSubState extends MusicBeatSubState
function goToFreeplay():Void
{
allowInput = false;
autoFollow = false;
FlxTween.tween(cursor, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
@ -695,7 +701,7 @@ class CharSelectSubState extends MusicBeatSubState
syncAudio(elapsed);
if (!pressedSelect)
if (allowInput && !pressedSelect)
{
if (controls.UI_UP) holdTmrUp += elapsed;
if (controls.UI_UP_R)
@ -786,10 +792,9 @@ class CharSelectSubState extends MusicBeatSubState
&& availableChars.exists(getCurrentSelected())
&& Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()]))
{
gfChill.visible = true;
curChar = availableChars.get(getCurrentSelected());
if (!pressedSelect && controls.ACCEPT)
if (allowInput && !pressedSelect && controls.ACCEPT)
{
cursorConfirmed.visible = true;
cursorConfirmed.x = cursor.x - 2;
@ -817,7 +822,7 @@ class CharSelectSubState extends MusicBeatSubState
});
}
if (pressedSelect && controls.BACK)
if (allowInput && pressedSelect && controls.BACK)
{
cursorConfirmed.visible = false;
grpCursors.visible = true;
@ -847,7 +852,7 @@ class CharSelectSubState extends MusicBeatSubState
gfChill.visible = false;
if (controls.ACCEPT)
if (allowInput && controls.ACCEPT)
{
cursorDenied.visible = true;
cursorDenied.x = cursor.x - 2;

View file

@ -4,7 +4,7 @@ package funkin.ui.charSelect;
import funkin.graphics.video.FlxVideo;
#end
#if hxCodec
import hxcodec.flixel.FlxVideoSprite;
import funkin.graphics.video.FunkinVideoSprite;
#end
import funkin.ui.MusicBeatSubState;
import funkin.audio.FunkinSound;
@ -72,12 +72,12 @@ class IntroSubState extends MusicBeatSubState
#end
#if hxCodec
var vid:FlxVideoSprite;
var vid:FunkinVideoSprite;
function playVideoNative(filePath:String):Void
{
// Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0);
vid = new FunkinVideoSprite(0, 0);
vid.scrollFactor.set();

View file

@ -24,7 +24,6 @@ import haxe.ui.core.Screen;
import haxe.ui.events.UIEvent;
import haxe.ui.RuntimeComponentBuilder;
import lime.utils.Assets as LimeAssets;
import openfl.Assets;
import openfl.events.Event;
import openfl.events.IOErrorEvent;
import openfl.geom.Rectangle;

View file

@ -700,7 +700,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function get_isCursorOverHaxeUI():Bool
{
return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
}
/**
@ -3840,7 +3840,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Handle scroll anchor
if (scrollAnchorScreenPos != null)
{
var currentScreenPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
var currentScreenPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewX);
var distance = currentScreenPos - scrollAnchorScreenPos;
var verticalDistance = distance.y;
@ -4121,8 +4121,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
var overlapsRenderedEvents:Bool = FlxG.mouse.overlaps(renderedEvents);
// Cursor position relative to the grid.
var cursorX:Float = FlxG.mouse.screenX - gridTiledSprite.x;
var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y;
var cursorX:Float = FlxG.mouse.viewX - gridTiledSprite.x;
var cursorY:Float = FlxG.mouse.viewY - gridTiledSprite.y;
var overlapsSelectionBorder:Bool = overlapsGrid
&& ((cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2)
@ -4137,7 +4137,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{
if (scrollAnchorScreenPos == null)
{
scrollAnchorScreenPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
scrollAnchorScreenPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
selectionBoxStartPos = null;
}
else
@ -4159,11 +4159,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
else if (notePreview != null && FlxG.mouse.overlaps(notePreview) && !isCursorOverHaxeUI)
{
// Clicked note preview
notePreviewScrollAreaStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
notePreviewScrollAreaStartPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
}
else if (!isCursorOverHaxeUI && (!overlapsGrid || overlapsSelectionBorder))
{
selectionBoxStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
selectionBoxStartPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
// Drawing selection box.
targetCursorMode = Crosshair;
}
@ -4188,7 +4188,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{
// Clicked on the playhead scroll area.
// Move the playhead to the cursor position.
this.playheadPositionInPixels = FlxG.mouse.screenY - (GRID_INITIAL_Y_POS);
this.playheadPositionInPixels = FlxG.mouse.viewY - (GRID_INITIAL_Y_POS);
moveSongToScrollPosition();
// Cursor should be a grabby hand.
@ -4251,8 +4251,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
else
{
// Minimum of 0.
return 0;
// Minimum of -1.
return -1;
}
});
@ -4313,27 +4313,27 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Clicking and dragging.
// Scroll the screen if the mouse is above or below the grid.
if (FlxG.mouse.screenY < MENU_BAR_HEIGHT)
if (FlxG.mouse.viewY < MENU_BAR_HEIGHT)
{
// Scroll up.
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.screenY;
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
scrollPositionInPixels -= diff * 0.5; // Too fast!
moveSongToScrollPosition();
}
else if (FlxG.mouse.screenY > (playbarHeadLayout?.y ?? 0.0))
else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
{
// Scroll down.
var diff:Float = FlxG.mouse.screenY - (playbarHeadLayout?.y ?? 0.0);
var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
scrollPositionInPixels += diff * 0.5; // Too fast!
moveSongToScrollPosition();
}
// Render the selection box.
var selectionRect:FlxRect = new FlxRect();
selectionRect.x = Math.min(FlxG.mouse.screenX, selectionBoxStartPos.x);
selectionRect.y = Math.min(FlxG.mouse.screenY, selectionBoxStartPos.y);
selectionRect.width = Math.abs(FlxG.mouse.screenX - selectionBoxStartPos.x);
selectionRect.height = Math.abs(FlxG.mouse.screenY - selectionBoxStartPos.y);
selectionRect.x = Math.min(FlxG.mouse.viewX, selectionBoxStartPos.x);
selectionRect.y = Math.min(FlxG.mouse.viewY, selectionBoxStartPos.y);
selectionRect.width = Math.abs(FlxG.mouse.viewX - selectionBoxStartPos.x);
selectionRect.height = Math.abs(FlxG.mouse.viewY - selectionBoxStartPos.y);
setSelectionBoxBounds(selectionRect);
targetCursorMode = Crosshair;
@ -4461,8 +4461,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Player is clicking and holding on note preview to scrub around.
targetCursorMode = Grabbing;
var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.screenY, (notePreview?.y ?? 0.0),
(notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0), 0, songLengthInPixels);
var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.viewY, (notePreview?.y ?? 0.0), (notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0),
0, songLengthInPixels);
scrollPositionInPixels = clickedPosInPixels;
moveSongToScrollPosition();
@ -4520,17 +4520,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
targetCursorMode = Grabbing;
// Scroll the screen if the mouse is above or below the grid.
if (FlxG.mouse.screenY < MENU_BAR_HEIGHT)
if (FlxG.mouse.viewY < MENU_BAR_HEIGHT)
{
// Scroll up.
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.screenY;
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
scrollPositionInPixels -= diff * 0.5; // Too fast!
moveSongToScrollPosition();
}
else if (FlxG.mouse.screenY > (playbarHeadLayout?.y ?? 0.0))
else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
{
// Scroll down.
var diff:Float = FlxG.mouse.screenY - (playbarHeadLayout?.y ?? 0.0);
var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
scrollPositionInPixels += diff * 0.5; // Too fast!
moveSongToScrollPosition();
}
@ -4811,11 +4811,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Show the context menu connected to the note.
if (useSingleNoteContextMenu)
{
this.openNoteContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedNote.noteData);
this.openNoteContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY, highlightedNote.noteData);
}
else
{
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY);
this.openSelectionContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY);
}
}
else
@ -4835,11 +4835,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|| (isHighlightedEventSelected && currentEventSelection.length == 1);
if (useSingleEventContextMenu)
{
this.openEventContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedEvent.eventData);
this.openEventContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY, highlightedEvent.eventData);
}
else
{
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY);
this.openSelectionContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY);
}
}
else
@ -4860,11 +4860,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Show the context menu connected to the note.
if (useSingleNoteContextMenu)
{
this.openHoldNoteContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedHoldNote.noteData);
this.openHoldNoteContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY, highlightedHoldNote.noteData);
}
else
{
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY);
this.openSelectionContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY);
}
}
else
@ -5139,10 +5139,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
var songPos:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
var songPosMilliseconds:String = Std.string(Math.floor(Math.abs(songPos) % 1000)).lpad('0', 2).substr(0, 2);
var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2);
var songPosMinutes:String = Std.string(Math.floor((Math.abs(songPos) / 1000) / 60)).lpad('0', 2);
if (songPos < 0) songPosMinutes = '-' + songPosMinutes;
var songPosString:String = '${songPosMinutes}:${songPosSeconds}';
var songPosString:String = '${songPosMinutes}:${songPosSeconds}:${songPosMilliseconds}';
if (playbarSongPos.value != songPosString) playbarSongPos.value = songPosString;
@ -5614,7 +5615,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
else
{
trace('Ignoring keybinds for View menu items because we are in live input mode (${currentLiveInputStyle}).');
// trace('Ignoring keybinds for View menu items because we are in live input mode (${currentLiveInputStyle}).');
}
}

View file

@ -3,7 +3,6 @@ package funkin.ui.debug.charting.components;
import funkin.data.event.SongEventRegistry;
import flixel.graphics.frames.FlxAtlasFrames;
import openfl.display.BitmapData;
import openfl.utils.Assets;
import flixel.FlxObject;
import flixel.FlxBasic;
import flixel.FlxSprite;

View file

@ -95,7 +95,7 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
}
var LIMIT = 6;
charButton.icon = CharacterDataParser.getCharPixelIconAsset(charId);
charButton.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(charId));
charButton.text = charData.name.length > LIMIT ? '${charData.name.substr(0, LIMIT)}.' : '${charData.name}';
charButton.onClick = _ -> {

View file

@ -13,7 +13,6 @@ import funkin.audio.waveform.WaveformSprite;
import flixel.util.FlxColor;
import haxe.io.Bytes;
import haxe.io.Path;
import openfl.utils.Assets;
/**
* Functions for loading audio for the chart editor.

View file

@ -221,7 +221,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.opponent);
if (charDataOpponent != null)
{
buttonCharacterOpponent.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.opponent);
buttonCharacterOpponent.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.opponent));
buttonCharacterOpponent.text = charDataOpponent.name.length > LIMIT ? '${charDataOpponent.name.substr(0, LIMIT)}.' : '${charDataOpponent.name}';
}
else
@ -233,7 +233,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.girlfriend);
if (charDataGirlfriend != null)
{
buttonCharacterGirlfriend.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.girlfriend);
buttonCharacterGirlfriend.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.girlfriend));
buttonCharacterGirlfriend.text = charDataGirlfriend.name.length > LIMIT ? '${charDataGirlfriend.name.substr(0, LIMIT)}.' : '${charDataGirlfriend.name}';
}
else
@ -245,7 +245,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.player);
if (charDataPlayer != null)
{
buttonCharacterPlayer.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.player);
buttonCharacterPlayer.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.player));
buttonCharacterPlayer.text = charDataPlayer.name.length > LIMIT ? '${charDataPlayer.name.substr(0, LIMIT)}.' : '${charDataPlayer.name}';
}
else

View file

@ -16,6 +16,7 @@ import funkin.ui.debug.latency.CoolStatsGraph;
import openfl.events.KeyboardEvent;
import funkin.input.PreciseInputManager;
import funkin.play.notes.Strumline;
import funkin.ui.mainmenu.MainMenuState;
import funkin.play.notes.notestyle.NoteStyle;
import funkin.data.notestyle.NoteStyleData;
import funkin.data.notestyle.NoteStyleRegistry;
@ -188,6 +189,12 @@ class LatencyState extends MusicBeatSubState
}
override public function close():Void
{
cleanup();
super.close();
}
function cleanup():Void
{
PreciseInputManager.instance.onInputPressed.remove(preciseInputPressed);
PreciseInputManager.instance.onInputReleased.remove(preciseInputReleased);
@ -200,7 +207,6 @@ class LatencyState extends MusicBeatSubState
FlxG.state.persistentDraw = prevPersistentDraw;
FlxG.state.persistentUpdate = prevPersistentUpdate;
super.close();
}
function regenNoteData()
@ -311,7 +317,9 @@ class LatencyState extends MusicBeatSubState
if (controls.BACK)
{
close();
// close();
cleanup();
FlxG.switchState(() -> new MainMenuState());
}
super.update(elapsed);

View file

@ -11,7 +11,6 @@ import funkin.data.freeplay.album.AlbumRegistry;
import funkin.util.assets.FlxAnimationUtil;
import funkin.graphics.FunkinSprite;
import funkin.util.SortUtil;
import openfl.utils.Assets;
/**
* The graphic for the album roll in the FreeplayState.

View file

@ -51,7 +51,6 @@ import funkin.ui.transition.LoadingState;
import funkin.ui.transition.StickerSubState;
import funkin.util.MathUtil;
import funkin.util.SortUtil;
import lime.utils.Assets;
import openfl.display.BlendMode;
import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.song.SongData.SongMusicData;
@ -586,13 +585,13 @@ class FreeplayState extends MusicBeatSubState
}
};
exitMovers.set([fp, txtCompletion, fnfHighscoreSpr, txtCompletion, clearBoxSprite],
exitMovers.set([fp, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
{
x: FlxG.width,
speed: 0.3
});
exitMoversCharSel.set([fp, txtCompletion, fnfHighscoreSpr, txtCompletion, clearBoxSprite],
exitMoversCharSel.set([fp, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
{
y: -270,
speed: 0.8,
@ -1376,7 +1375,7 @@ class FreeplayState extends MusicBeatSubState
#if FEATURE_DEBUG_FUNCTIONS
if (FlxG.keys.justPressed.P)
{
FlxG.switchState(FreeplayState.build(
FlxG.switchState(() -> FreeplayState.build(
{
{
character: currentCharacterId == "pico" ? Constants.DEFAULT_CHARACTER : "pico",
@ -1777,12 +1776,13 @@ class FreeplayState extends MusicBeatSubState
FlxG.log.warn('WARN: could not find song with id (${daSong.songId})');
return;
}
var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty) ?? '';
var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty, currentCharacter) ?? '';
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION
&& targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty;
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.songId, suffixedDifficulty);
trace(songScore);
intendedScore = songScore?.score ?? 0;
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
rememberedDifficulty = suffixedDifficulty;
@ -1859,7 +1859,7 @@ class FreeplayState extends MusicBeatSubState
albumRoll.setDifficultyStars(daSong?.difficultyRating);
}
// Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String)
// Clears the cache of songs to free up memory, they'll have to be loaded in later tho
function clearDaCache(actualSongTho:String):Void
{
for (song in songs)

View file

@ -18,7 +18,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup;

View file

@ -18,7 +18,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup;
@ -177,20 +176,21 @@ class BoyfriendCard extends BackingCard
}
var beatFreq:Int = 1;
var beatFreqList:Array<Int> = [1,2,4,8];
var beatFreqList:Array<Int> = [1, 2, 4, 8];
public override function beatHit():Void {
public override function beatHit():Void
{
// increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms.....
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)];
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm / 140)];
if(Conductor.instance.currentBeat % beatFreq != 0) return;
if (Conductor.instance.currentBeat % beatFreq != 0) return;
FlxTween.cancelTweensOf(glow);
FlxTween.cancelTweensOf(glowDark);
glow.alpha = 0.8;
FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut});
FlxTween.tween(glow, {alpha: 0}, 16 / 24, {ease: FlxEase.quartOut});
glowDark.alpha = 0;
FlxTween.tween(glowDark, {alpha: 0.6}, 18/24, {ease: FlxEase.quartOut});
FlxTween.tween(glowDark, {alpha: 0.6}, 18 / 24, {ease: FlxEase.quartOut});
}
public override function introDone():Void

View file

@ -20,7 +20,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup;
import funkin.graphics.shaders.AdjustColorShader;

View file

@ -20,7 +20,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup;
import funkin.graphics.shaders.AdjustColorShader;
@ -233,20 +232,21 @@ class PicoCard extends BackingCard
}
var beatFreq:Int = 1;
var beatFreqList:Array<Int> = [1,2,4,8];
var beatFreqList:Array<Int> = [1, 2, 4, 8];
public override function beatHit():Void {
public override function beatHit():Void
{
// increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms.....
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)];
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm / 140)];
if(Conductor.instance.currentBeat % beatFreq != 0) return;
if (Conductor.instance.currentBeat % beatFreq != 0) return;
FlxTween.cancelTweensOf(glow);
FlxTween.cancelTweensOf(glowDark);
glow.alpha = 1;
FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut});
FlxTween.tween(glow, {alpha: 0}, 16 / 24, {ease: FlxEase.quartOut});
glowDark.alpha = 0;
FlxTween.tween(glowDark, {alpha: 1}, 18/24, {ease: FlxEase.quartOut});
FlxTween.tween(glowDark, {alpha: 1}, 18 / 24, {ease: FlxEase.quartOut});
}
public override function introDone():Void

View file

@ -113,7 +113,9 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
switch (rank)
{
case PERFECT | PERFECT_GOLD:
case PERFECT_GOLD:
return _data.results.perfectGold;
case PERFECT:
return _data.results.perfect;
case EXCELLENT:
return _data.results.excellent;

View file

@ -123,7 +123,7 @@ class MainMenuState extends MusicBeatState
}));
});
#if CAN_OPEN_LINKS
#if FEATURE_OPEN_URL
// In order to prevent popup blockers from triggering,
// we need to open the link as an immediate result of a keypress event,
// so we can't wait for the flicker animation to complete.
@ -234,7 +234,7 @@ class MainMenuState extends MusicBeatState
camFollow.setPosition(selected.getGraphicMidpoint().x, selected.getGraphicMidpoint().y);
}
#if CAN_OPEN_LINKS
#if FEATURE_OPEN_URL
function selectDonate()
{
WindowUtil.openURL(Constants.URL_ITCH);
@ -344,6 +344,8 @@ class MainMenuState extends MusicBeatState
}
}
Conductor.instance.update();
// Open the debug menu, defaults to ` / ~
// This includes stuff like the Chart Editor, so it should be present on all builds.
if (controls.DEBUG_MENU)
@ -356,7 +358,7 @@ class MainMenuState extends MusicBeatState
#if FEATURE_DEBUG_FUNCTIONS
// Ctrl+Alt+Shift+P = Character Unlock screen
// Ctrl+Alt+Shift+W = Meet requirements for Pico Unlock
// Ctrl+Alt+Shift+L = Revoke requirements for Pico Unlock
// Ctrl+Alt+Shift+M = Revoke requirements for Pico Unlock
// Ctrl+Alt+Shift+R = Score/Rank conflict test
// Ctrl+Alt+Shift+N = Mark all characters as not seen
// Ctrl+Alt+Shift+E = Dump save data
@ -369,7 +371,7 @@ class MainMenuState extends MusicBeatState
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W)
{
FunkinSound.playOnce(Paths.sound('confirmMenu'));
// Give the user a score of 1 point on Weekend 1 story mode.
// Give the user a score of 1 point on Weekend 1 story mode (Easy difficulty).
// This makes the level count as cleared and displays the songs in Freeplay.
funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
{
@ -389,27 +391,30 @@ class MainMenuState extends MusicBeatState
});
}
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.L)
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.M)
{
FunkinSound.playOnce(Paths.sound('confirmMenu'));
// Give the user a score of 0 points on Weekend 1 story mode.
// Give the user a score of 0 points on Weekend 1 story mode (all difficulties).
// This makes the level count as uncleared and no longer displays the songs in Freeplay.
funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
{
score: 1,
tallies:
{
sick: 0,
good: 0,
bad: 0,
shit: 0,
missed: 0,
combo: 0,
maxCombo: 0,
totalNotesHit: 0,
totalNotes: 0,
}
});
for (diff in ['easy', 'normal', 'hard'])
{
funkin.save.Save.instance.setLevelScore('weekend1', diff,
{
score: 0,
tallies:
{
sick: 0,
good: 0,
bad: 0,
shit: 0,
missed: 0,
combo: 0,
maxCombo: 0,
totalNotesHit: 0,
totalNotes: 0,
}
});
}
}
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R)

View file

@ -6,7 +6,6 @@ import flixel.system.FlxAssets;
import flixel.tweens.FlxEase;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.utils.Assets;
import funkin.util.MathUtil;
/**
@ -80,10 +79,12 @@ class FunkinSoundTray extends FlxSoundTray
y = MathUtil.coolLerp(y, lerpYPos, 0.1);
alpha = MathUtil.coolLerp(alpha, alphaTarget, 0.25);
var shouldHide = (FlxG.sound.muted == false && FlxG.sound.volume > 0);
// Animate sound tray thing
if (_timer > 0)
{
_timer -= (MS / 1000);
if (shouldHide) _timer -= (MS / 1000);
alphaTarget = 1;
}
else if (y >= -height)
@ -122,7 +123,7 @@ class FunkinSoundTray extends FlxSoundTray
active = true;
var globalVolume:Int = Math.round(FlxG.sound.logToLinear(FlxG.sound.volume) * 10);
if (FlxG.sound.muted)
if (FlxG.sound.muted || FlxG.sound.volume == 0)
{
globalVolume = 0;
}

View file

@ -1,5 +1,6 @@
package funkin.ui.options;
import funkin.ui.transition.LoadingState;
import funkin.ui.debug.latency.LatencyState;
import flixel.FlxSprite;
import flixel.FlxSubState;
@ -185,7 +186,11 @@ class OptionsMenu extends Page
createItem("PREFERENCES", function() switchPage(Preferences));
createItem("CONTROLS", function() switchPage(Controls));
createItem("INPUT OFFSETS", function() {
#if web
LoadingState.transitionToState(() -> new LatencyState());
#else
FlxG.state.openSubState(new LatencyState());
#end
});
#if newgrounds

View file

@ -77,6 +77,10 @@ class PreferencesMenu extends Page
createPrefItemCheckbox('Unlocked Framerate', 'Enable to unlock the framerate', function(value:Bool):Void {
Preferences.unlockedFramerate = value;
}, Preferences.unlockedFramerate);
#else
createPrefItemNumber('FPS', 'The maximum framerate that the game targets', function(value:Float) {
Preferences.framerate = Std.int(value);
}, null, Preferences.framerate, 30, 300, 5, 0);
#end
}
@ -87,7 +91,6 @@ class PreferencesMenu extends Page
// Indent the selected item.
items.forEach(function(daItem:TextMenuItem) {
var thyOffset:Int = 0;
// Initializing thy text width (if thou text present)
var thyTextWidth:Int = 0;
if (Std.isOfType(daItem, EnumPreferenceItem)) thyTextWidth = cast(daItem, EnumPreferenceItem).lefthandText.getWidth();

View file

@ -23,7 +23,6 @@ import funkin.ui.MusicBeatState;
import funkin.ui.transition.LoadingState;
import funkin.ui.transition.StickerSubState;
import funkin.util.MathUtil;
import openfl.utils.Assets;
class StoryMenuState extends MusicBeatState
{

View file

@ -4,7 +4,7 @@ package funkin.ui.title;
import funkin.graphics.video.FlxVideo;
#end
#if hxCodec
import hxcodec.flixel.FlxVideoSprite;
import funkin.graphics.video.FunkinVideoSprite;
#end
import funkin.ui.MusicBeatState;
@ -62,12 +62,12 @@ class AttractState extends MusicBeatState
#end
#if hxCodec
var vid:FlxVideoSprite;
var vid:FunkinVideoSprite;
function playVideoNative(filePath:String):Void
{
// Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0);
vid = new FunkinVideoSprite(0, 0);
if (vid != null)
{

View file

@ -273,11 +273,6 @@ class TitleState extends MusicBeatState
}
#end
if (Save.instance.charactersSeen.contains("pico"))
{
Save.instance.charactersSeen.remove("pico");
Save.instance.oldChar = false;
}
Conductor.instance.update();
/* if (FlxG.onMobile)

View file

@ -3,24 +3,23 @@ package funkin.ui.transition;
import flixel.FlxSprite;
import flixel.math.FlxMath;
import flixel.tweens.FlxEase;
import funkin.graphics.FunkinSprite;
import flixel.tweens.FlxTween;
import flixel.util.FlxTimer;
import flixel.util.typeLimit.NextState;
import funkin.graphics.FunkinSprite;
import funkin.graphics.shaders.ScreenWipeShader;
import funkin.play.PlayState;
import funkin.play.PlayStatePlaylist;
import funkin.play.song.Song.SongDifficulty;
import funkin.ui.MusicBeatState;
import haxe.io.Path;
import funkin.graphics.FunkinSprite;
import lime.app.Future;
import lime.app.Promise;
import lime.utils.AssetLibrary;
import lime.utils.AssetManifest;
import lime.utils.Assets as LimeAssets;
import openfl.filters.ShaderFilter;
import openfl.utils.Assets;
import flixel.util.typeLimit.NextState;
import openfl.utils.Assets as OpenFLAssets;
class LoadingState extends MusicBeatSubState
{
@ -98,7 +97,7 @@ class LoadingState extends MusicBeatSubState
function checkLoadSong(path:String):Void
{
if (!Assets.cache.hasSound(path))
if (!OpenFLAssets.cache.hasSound(path))
{
var library = Assets.getLibrary('songs');
var symbolPath = path.split(':').pop();
@ -277,7 +276,7 @@ class LoadingState extends MusicBeatSubState
#if NO_PRELOAD_ALL
static function isSoundLoaded(path:String):Bool
{
return Assets.cache.hasSound(path);
return OpenFLAssets.cache.hasSound(path);
}
static function isLibraryLoaded(library:String):Bool
@ -452,6 +451,11 @@ class LoadingState extends MusicBeatSubState
return promise.future;
}
public static function transitionToState(state:NextState, stopMusic:Bool = false):Void
{
FlxG.switchState(() -> new LoadingState(state, stopMusic));
}
}
class MultiCallback

View file

@ -2,7 +2,6 @@ package funkin.ui.transition;
import flixel.FlxSprite;
import haxe.Json;
import lime.utils.Assets;
import funkin.graphics.FunkinSprite;
// import flxtyped group
import funkin.ui.MusicBeatSubState;
@ -56,7 +55,7 @@ class StickerSubState extends MusicBeatSubState
// make sure that ONLY plays mp3/ogg files
// if there's no mp3/ogg file, then it regenerates/reloads the random folder
var assetsInList = openfl.utils.Assets.list();
var assetsInList = Assets.list();
var soundFilterFunc = function(a:String) {
return a.startsWith('assets/shared/sounds/stickersounds/');
@ -84,7 +83,7 @@ class StickerSubState extends MusicBeatSubState
var filterFunc = function(a:String) {
return a.startsWith('assets/shared/sounds/stickersounds/' + soundSelection + '/');
};
var assetsInList3 = openfl.utils.Assets.list();
var assetsInList3 = Assets.list();
sounds = assetsInList3.filter(filterFunc);
for (i in 0...sounds.length)
{

View file

@ -481,10 +481,6 @@ class Constants
public static final JUDGEMENT_BAD_COMBO_BREAK:Bool = true;
public static final JUDGEMENT_SHIT_COMBO_BREAK:Bool = true;
// % Sick
public static final RANK_PERFECT_PLAT_THRESHOLD:Float = 1.0; // % Sick
public static final RANK_PERFECT_GOLD_THRESHOLD:Float = 0.85; // % Sick
// % Hit
public static final RANK_PERFECT_THRESHOLD:Float = 1.00;
public static final RANK_EXCELLENT_THRESHOLD:Float = 0.90;

View file

@ -22,7 +22,7 @@ class WindowUtil
*/
public static function openURL(targetUrl:String):Void
{
#if CAN_OPEN_LINKS
#if FEATURE_OPEN_URL
#if linux
Sys.command('/usr/bin/xdg-open $targetUrl &');
#else
@ -40,7 +40,7 @@ class WindowUtil
*/
public static function openFolder(targetPath:String):Void
{
#if CAN_OPEN_LINKS
#if FEATURE_OPEN_URL
#if windows
Sys.command('explorer', [targetPath.replace('/', '\\')]);
#elseif mac
@ -59,7 +59,7 @@ class WindowUtil
*/
public static function openSelectFile(targetPath:String):Void
{
#if CAN_OPEN_LINKS
#if FEATURE_OPEN_URL
#if windows
Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]);
#elseif mac
@ -91,6 +91,14 @@ class WindowUtil
windowExit.dispatch(exitCode);
});
#if FEATURE_DEBUG_TRACY
// Apply a marker to indicate frame end for the Tracy profiler.
// Do this only if Tracy is configured to prevent lag.
openfl.Lib.current.stage.addEventListener(openfl.events.Event.EXIT_FRAME, (e:openfl.events.Event) -> {
cpp.vm.tracy.TracyProfiler.frameMark();
});
#end
openfl.Lib.current.stage.addEventListener(openfl.events.KeyboardEvent.KEY_DOWN, (e:openfl.events.KeyboardEvent) -> {
for (key in PlayerSettings.player1.controls.getKeysForAction(WINDOW_FULLSCREEN))
{

View file

@ -6,7 +6,7 @@ class AnsiTrace
// but adds nice cute ANSI things
public static function trace(v:Dynamic, ?info:haxe.PosInfos)
{
#if TREMOVE
#if NO_FEATURE_LOG_TRACE
return;
#end
var str = formatOutput(v, info);

View file

@ -23,6 +23,8 @@ class GitCommit
var commitHash:String = process.stdout.readLine();
var commitHashSplice:String = commitHash.substr(0, 7);
process.close();
trace('Git Commit ID: ${commitHashSplice}');
// Generates a string expression
@ -52,6 +54,7 @@ class GitCommit
}
var branchName:String = branchProcess.stdout.readLine();
branchProcess.close();
trace('Git Branch Name: ${branchName}');
// Generates a string expression
@ -84,6 +87,7 @@ class GitCommit
try
{
output = branchProcess.stdout.readLine();
branchProcess.close();
}
catch (e)
{