From 838d5ff1a89f7f71c598d107347d871c310b3d0c Mon Sep 17 00:00:00 2001 From: String Date: Fri, 27 Feb 2026 05:02:44 -0600 Subject: [PATCH 1/3] Completely remove FlixelGDX module --- android/build.gradle | 4 +- build.gradle | 8 + flixelgdx/android/build.gradle | 48 - .../android/FlixelAndroidLauncher.java | 28 - .../android/alert/FlixelAndroidAlerter.java | 44 - flixelgdx/build.gradle | 18 - flixelgdx/core/build.gradle | 58 - .../me/stringdotjar/flixelgdx/Flixel.java | 538 -------- .../stringdotjar/flixelgdx/FlixelBasic.java | 90 -- .../me/stringdotjar/flixelgdx/FlixelGame.java | 726 ---------- .../stringdotjar/flixelgdx/FlixelObject.java | 125 -- .../stringdotjar/flixelgdx/FlixelSprite.java | 505 ------- .../flixelgdx/backend/Alerter.java | 10 - .../flixelgdx/display/FlixelCamera.java | 1186 ----------------- .../flixelgdx/display/FlixelState.java | 199 --- .../flixelgdx/display/FlixelSubState.java | 53 - .../flixelgdx/group/FlixelGroup.java | 117 -- .../flixelgdx/group/FlixelGroupable.java | 10 - .../flixelgdx/group/FlixelSpriteGroup.java | 742 ----------- .../flixelgdx/input/FlixelKey.java | 11 - .../flixelgdx/logging/FlixelLogLevel.java | 25 - .../flixelgdx/logging/FlixelLogMode.java | 20 - .../flixelgdx/logging/FlixelLogger.java | 226 ---- .../flixelgdx/signal/FlixelSignal.java | 95 -- .../flixelgdx/signal/FlixelSignalData.java | 48 - .../flixelgdx/text/FlixelFontRegistry.java | 212 --- .../flixelgdx/text/FlixelText.java | 1133 ---------------- .../flixelgdx/tween/FlixelEase.java | 220 --- .../flixelgdx/tween/FlixelTween.java | 296 ---- .../flixelgdx/tween/FlixelTweenManager.java | 65 - .../tween/settings/FlixelTweenSettings.java | 246 ---- .../tween/settings/FlixelTweenType.java | 16 - .../flixelgdx/tween/type/FlixelNumTween.java | 68 - .../tween/type/FlixelPropertyTween.java | 88 -- .../flixelgdx/tween/type/FlixelVarTween.java | 157 --- .../flixelgdx/util/FlixelConstants.java | 32 - .../flixelgdx/util/FlixelGitUtil.java | 80 -- .../flixelgdx/util/FlixelMathUtil.java | 21 - .../flixelgdx/util/FlixelPathsUtil.java | 75 -- .../flixelgdx/util/FlixelReflectUtil.java | 74 - .../flixelgdx/util/FlixelRuntimeUtil.java | 197 --- flixelgdx/core/src/main/java/module-info.java | 23 - flixelgdx/lwjgl3/build.gradle | 18 - .../backend/lwjgl3/FlixelLwjgl3Launcher.java | 56 - .../lwjgl3/alert/FlixelLwjgl3Alerter.java | 41 - lwjgl3/build.gradle | 4 +- polyverse/build.gradle | 4 +- settings.gradle | 11 +- 48 files changed, 26 insertions(+), 8045 deletions(-) delete mode 100644 flixelgdx/android/build.gradle delete mode 100644 flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/FlixelAndroidLauncher.java delete mode 100644 flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/alert/FlixelAndroidAlerter.java delete mode 100644 flixelgdx/build.gradle delete mode 100644 flixelgdx/core/build.gradle delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelBasic.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelGame.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelObject.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelSprite.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/backend/Alerter.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelCamera.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelState.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelSubState.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroup.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroupable.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelSpriteGroup.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogLevel.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogMode.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignal.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelFontRegistry.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelText.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelEase.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTween.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTweenManager.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenSettings.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenType.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelNumTween.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelPropertyTween.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelVarTween.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelMathUtil.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelPathsUtil.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java delete mode 100644 flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java delete mode 100644 flixelgdx/core/src/main/java/module-info.java delete mode 100644 flixelgdx/lwjgl3/build.gradle delete mode 100644 flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/FlixelLwjgl3Launcher.java delete mode 100644 flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/alert/FlixelLwjgl3Alerter.java diff --git a/android/build.gradle b/android/build.gradle index f819286..ae66ecb 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -58,7 +58,9 @@ configurations { natives } dependencies { coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' implementation project(':polyverse') - implementation project(':flixelgdx:android') + implementation("com.github.stringdotjar:flixelgdx-android:master-SNAPSHOT") { + changing = true + } } configurations { diff --git a/build.gradle b/build.gradle index e22e156..92ae681 100644 --- a/build.gradle +++ b/build.gradle @@ -69,4 +69,12 @@ subprojects { } } +// Always fetch latest master-SNAPSHOT for flixelgdx from JitPack (if not found locally). +configurations.all { + resolutionStrategy { + cacheChangingModulesFor 0, 'seconds' + cacheDynamicVersionsFor 0, 'seconds' + } +} + eclipse.project.name = 'Polyverse' + '-parent' diff --git a/flixelgdx/android/build.gradle b/flixelgdx/android/build.gradle deleted file mode 100644 index e2c423e..0000000 --- a/flixelgdx/android/build.gradle +++ /dev/null @@ -1,48 +0,0 @@ -eclipse.project.name = appName + '-flixelgdx-android' - -apply plugin: 'com.android.library' - -android { - namespace "me.stringdotjar.flixelgdx" - compileSdk 36 - - defaultConfig { - multiDexEnabled = true - minSdkVersion 34 - targetSdkVersion 35 - } - compileOptions { - coreLibraryDesugaringEnabled = true - sourceCompatibility JavaVersion.VERSION_17 - targetCompatibility JavaVersion.VERSION_17 - } -} - -repositories { - // needed for AAPT2, may be needed for other tools - google() -} - -configurations { - coreLibraryDesugaring -} - -dependencies { - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5' - - api project(":flixelgdx:core") - api "androidx.multidex:multidex:2.0.1" - api "games.rednblack.miniaudio:gdx-miniaudio-platform:$miniaudioVersion:natives-armeabi-v7a" - api "games.rednblack.miniaudio:gdx-miniaudio-platform:$miniaudioVersion:natives-arm64-v8a" - api "games.rednblack.miniaudio:gdx-miniaudio-platform:$miniaudioVersion:natives-x86" - api "games.rednblack.miniaudio:gdx-miniaudio-platform:$miniaudioVersion:natives-x86_64" - api "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-arm64-v8a" - api "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-armeabi-v7a" - api "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86" - api "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-x86_64" - api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-arm64-v8a" - api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-armeabi-v7a" - api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86" - api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-x86_64" - api "com.badlogicgames.gdx:gdx-backend-android:$gdxVersion" -} diff --git a/flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/FlixelAndroidLauncher.java b/flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/FlixelAndroidLauncher.java deleted file mode 100644 index f440895..0000000 --- a/flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/FlixelAndroidLauncher.java +++ /dev/null @@ -1,28 +0,0 @@ -package me.stringdotjar.flixelgdx.backend.android; - -import android.app.Activity; -import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; -import me.stringdotjar.flixelgdx.Flixel; -import me.stringdotjar.flixelgdx.FlixelGame; -import me.stringdotjar.flixelgdx.backend.android.alert.FlixelAndroidAlerter; - -/** - * Launches the Android version of the FlixelGDX game. - */ -public class FlixelAndroidLauncher { - - /** - * Launches the Android version of the game with the given game instance. - * - *

This should be called from the onCreate method of the Android launcher class, and the - * game instance should be created in the same general area. - * - * @param game The game instance to launch. This should already be initialized with the desired configuration values. - */ - public static void launch(FlixelGame game, Activity activity) { - Flixel.initialize(game, new FlixelAndroidAlerter(activity)); - - AndroidApplicationConfiguration configuration = new AndroidApplicationConfiguration(); - configuration.useImmersiveMode = true; - } -} diff --git a/flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/alert/FlixelAndroidAlerter.java b/flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/alert/FlixelAndroidAlerter.java deleted file mode 100644 index e7ace9a..0000000 --- a/flixelgdx/android/src/main/java/me/stringdotjar/flixelgdx/backend/android/alert/FlixelAndroidAlerter.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.stringdotjar.flixelgdx.backend.android.alert; - -import android.app.Activity; -import android.app.AlertDialog; -import me.stringdotjar.flixelgdx.backend.Alerter; - -public class FlixelAndroidAlerter implements Alerter { - - private final Activity activity; - - public FlixelAndroidAlerter(Activity activity) { - this.activity = activity; - } - - @Override - public void showInfoAlert(String title, String message) { - showAlert(title, message, android.R.drawable.ic_dialog_info); - } - - @Override - public void showWarningAlert(String title, String message) { - showAlert(title, message, android.R.drawable.ic_dialog_alert); - } - - @Override - public void showErrorAlert(String title, String message) { - showAlert(title, message, android.R.drawable.stat_notify_error); - } - - private void showAlert(final String title, final String message, final int iconResId) { - activity.runOnUiThread(new Runnable() { - @Override - public void run() { - new AlertDialog.Builder(activity) - .setTitle(title) - .setMessage(message) - .setIcon(iconResId) - .setPositiveButton("OK", null) - .setCancelable(true) - .show(); - } - }); - } -} diff --git a/flixelgdx/build.gradle b/flixelgdx/build.gradle deleted file mode 100644 index 40479df..0000000 --- a/flixelgdx/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -subprojects { - - group = 'me.stringdotjar.flixelgdx' - version = '1.0.0' - - // Force Java 17 for the entire framework. - if (!project.path.contains('android')) { - plugins.withType(JavaPlugin).tap { - configureEach { - java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } - } - } - } - } -} diff --git a/flixelgdx/core/build.gradle b/flixelgdx/core/build.gradle deleted file mode 100644 index 42fa47f..0000000 --- a/flixelgdx/core/build.gradle +++ /dev/null @@ -1,58 +0,0 @@ -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -eclipse.project.name = appName + '-flixelgdx-core' - -apply plugin: "java-library" - -// Embed version from build so Flixel.getVersion() can read it at runtime (Gradle does not set JAR manifest like Maven). -def generateFlixelVersion = tasks.register('generateFlixelVersion', WriteProperties) { - outputFile = layout.buildDirectory.file("generated/version/version.properties") - property("version", project.version) -} -processResources.from(generateFlixelVersion) { - into "me/stringdotjar/flixelgdx" -} - -java { - sourceCompatibility = 17 - targetCompatibility = 17 -} - -// Put dependencies on module path so module-info.java can resolve requires (automatic modules). -tasks.named('compileJava') { - doFirst { - options.compilerArgs = [ - '--module-path', classpath.asPath, - ] - classpath = files() - } -} - -// Require Eclipse (and IDEs that use Eclipse project model) to put dependencies on the module path, -// making "requires gdx" etc. resolve. Without this, only Gradle's compileJava sees them as modules. -eclipse.classpath.file { - whenMerged { - entries.findAll { it instanceof org.gradle.plugins.ide.eclipse.model.Library }.each { - it.entryAttributes['module'] = 'true' - } - } -} - -// Avoid JPMS split-package error: gdx and gdx-jnigen-loader both expose com.badlogic.gdx.utils. -configurations.all { - exclude group: 'com.badlogicgames.gdx', module: 'gdx-jnigen-loader' -} - -dependencies { - api "com.badlogicgames.gdx:gdx:$gdxVersion" - api "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" - api "com.github.tommyettinger:anim8-gdx:$anim8Version" - api "com.github.tommyettinger:libgdx-utils:$utilsVersion" - api "games.rednblack.miniaudio:miniaudio:$miniaudioVersion" - - implementation "org.fusesource.jansi:jansi:$jansiVersion" - implementation "org.jetbrains:annotations:26.1.0" - - if (enableGraalNative == 'true') { - implementation "io.github.berstanio:gdx-svmhelper-annotations:$graalHelperVersion" - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java deleted file mode 100644 index 24f3790..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/Flixel.java +++ /dev/null @@ -1,538 +0,0 @@ -package me.stringdotjar.flixelgdx; - -import com.badlogic.gdx.Application.ApplicationType; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.assets.AssetManager; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.scenes.scene2d.Stage; -import games.rednblack.miniaudio.MAGroup; -import games.rednblack.miniaudio.MASound; -import games.rednblack.miniaudio.MiniAudio; -import games.rednblack.miniaudio.loader.MASoundLoader; -import me.stringdotjar.flixelgdx.util.FlixelPathsUtil; -import me.stringdotjar.flixelgdx.backend.Alerter; -import me.stringdotjar.flixelgdx.display.FlixelCamera; -import me.stringdotjar.flixelgdx.display.FlixelState; -import me.stringdotjar.flixelgdx.logging.FlixelLogMode; -import me.stringdotjar.flixelgdx.logging.FlixelLogger; -import me.stringdotjar.flixelgdx.signal.FlixelSignal; -import me.stringdotjar.flixelgdx.signal.FlixelSignalData.MusicPlayedSignalData; -import me.stringdotjar.flixelgdx.signal.FlixelSignalData.UpdateSignalData; -import me.stringdotjar.flixelgdx.signal.FlixelSignalData.StateSwitchSignalData; -import me.stringdotjar.flixelgdx.signal.FlixelSignalData.SoundPlayedSignalData; -import org.jetbrains.annotations.NotNull; - -import java.io.InputStream; -import java.util.Properties; - -/** - * Global manager and utility class for Flixel. - * - *

This is where you want to do the main things, like switching screens, playing sounds/music, etc. - */ -public final class Flixel { - - /** The current {@code FlixelState} being displayed. */ - private static FlixelState state; - - /** The main audio object used to create, */ - private static MiniAudio engine; - - /** The global asset manager used to obtain preloaded assets. */ - private static AssetManager assetManager; - - /** The audio group for all sound effects, including the current music. */ - private static MAGroup soundsGroup; - - /** The sound for playing music throughout the game. */ - private static MASound music; - - /** The current master volume that is set. */ - private static float masterVolume = 1; - - /** The static instance used to access the core elements of the game. */ - private static FlixelGame game; - - /** The system to use for displaying alert notifications to the user. */ - private static Alerter alerter; - - /** Has the global manager been initialized yet? */ - private static boolean initialized = false; - - /** The default logger used by {@link #info}, {@link #warn}, and {@link #error}. */ - private static FlixelLogger defaultLogger; - - /** - * Initializes the global manager. - * - *

This can only be called once. If attempted to be executed again, the game will throw an - * exception. - * - * @param gameInstance The instance of the game to use. - * @param alertSystem The system to use for displaying alert notifications to the user. - * @throws IllegalStateException If Flixel has already been initialized. - */ - public static void initialize(@NotNull FlixelGame gameInstance, @NotNull Alerter alertSystem) { - if (initialized) { - throw new IllegalStateException("Flixel has already been initialized!"); - } - game = gameInstance; - alerter = alertSystem; - - assetManager = new AssetManager(); - - // Set up the game's global audio system. - engine = new MiniAudio(); - soundsGroup = engine.createGroup(); - assetManager.setLoader(MASound.class, new MASoundLoader(engine, assetManager.getFileHandleResolver())); - - defaultLogger = new FlixelLogger(FlixelLogMode.SIMPLE); - initialized = true; - } - - /** - * Sets the current screen to the provided screen. - * - * @param newState The new {@code FlixelState} to set as the current screen. - */ - public static void switchState(FlixelState newState) { - Signals.preStateSwitch.dispatch(new StateSwitchSignalData(newState)); - if (!initialized) { - throw new IllegalStateException("Flixel has not been initialized yet!"); - } - if (newState == null) { - throw new IllegalArgumentException("New state cannot be null!"); - } - if (state != null) { - state.hide(); - state.dispose(); - } - game.resetCameras(); - state = newState; - state.create(); - Signals.postStateSwitch.dispatch(new StateSwitchSignalData(newState)); - } - - /** - * Plays a new sound effect. - * - *

When you want to play a sound externally, outside the assets folder, you can use a {@link - * FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * Flixel.playSound(FlixelPathsUtil.external("your/path/here").path());
-   * }
- * - * @param path The path to load the sound from. Note that if you're loading an external sound - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @return The new sound instance. - */ - public static MASound playSound(String path) { - return playSound(path, 1, false, null, false); - } - - /** - * Plays a new sound effect. - * - *

When you want to play a sound externally, outside the assets folder, you can use a {@link - * FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * Flixel.playSound(FlixelPathsUtil.external("your/path/here").path(), 1);
-   * }
- * - * @param path The path to load the sound from. Note that if you're loading an external sound - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new sound with. - * @return The new sound instance. - */ - public static MASound playSound(String path, float volume) { - return playSound(path, volume, false, null, false); - } - - /** - * Plays a new sound effect. - * - *

When you want to play a sound externally, outside the assets folder, you can use a {@link - * FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * Flixel.playSound(FlixelPathsUtil.external("your/path/here").path(), 1, false);
-   * }
- * - * @param path The path to load the sound from. Note that if you're loading an external sound - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new sound with. - * @param looping Should the new sound loop indefinitely? - * @return The new sound instance. - */ - public static MASound playSound(String path, float volume, boolean looping) { - return playSound(path, volume, looping, null, false); - } - - /** - * Plays a new sound effect. - * - *

When you want to play a sound externally, outside the assets folder, you can use a {@link - * FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * // If null is passed down for the group, then the default sound group will be used.
-   * Flixel.playSound(FlixelPathsUtil.external("your/path/here").path(), 1, false, null);
-   * }
- * - * @param path The path to load the sound from. Note that if you're loading an external sound - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new sound with. - * @param looping Should the new sound loop indefinitely? - * @param group The sound group to add the new sound to. If {@code null} is passed down, then the - * default sound group will be used. - * @return The new sound instance. - */ - public static MASound playSound(String path, float volume, boolean looping, MAGroup group) { - return playSound(path, volume, looping, group, false); - } - - /** - * Plays a new sound effect. - * - *

When you want to play a sound externally, outside the assets folder, you can use a {@link - * FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * // If null is passed down for the group, then the default sound group will be used.
-   * // For the boolean attribute "external", you only should make it true for mobile builds,
-   * // otherwise just simply leave it be or make it "false" for other platforms like desktop.
-   * Flixel.playSound(FlixelPathsUtil.external("your/path/here").path(), 1, false, null, true);
-   * }
- * - * @param path The path to load the sound from. Note that if you're loading an external sound - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new sound with. - * @param looping Should the new sound loop indefinitely? - * @param group The sound group to add the new sound to. If {@code null} is passed down, then the - * default sound group will be used. - * @param external Should this sound be loaded externally? (This is only for mobile platforms!) - * @return The new sound instance. - */ - public static MASound playSound(@NotNull String path, float volume, boolean looping, MAGroup group, boolean external) { - String resolvedPath = external ? path : FlixelPathsUtil.resolveAudioPath(path); - MASound sound = engine.createSound(resolvedPath, (short) 0, (group != null) ? group : soundsGroup, external); - Signals.preSoundPlayed.dispatch(new SoundPlayedSignalData(sound)); - sound.setVolume(volume); - sound.setLooping(looping); - sound.play(); - Signals.postSoundPlayed.dispatch(new SoundPlayedSignalData(sound)); - return sound; - } - - /** - * Sets the current music playing for the entire game. - * - *

When you want to play music located externally, outside the assets folder, you can use a - * {@link FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * Flixel.playMusic(FlixelPathsUtil.external("your/path/here").path());
-   * }
- * - * @param path The path to load the music from. Note that if you're loading an external sound file - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - */ - public static MASound playMusic(String path) { - return playMusic(path, 1, true, false); - } - - /** - * Sets the current music playing for the entire game. - * - *

When you want to play music located externally, outside the assets folder, you can use a - * {@link FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * Flixel.playMusic(FlixelPathsUtil.external("your/path/here").path(), 1);
-   * }
- * - * @param path The path to load the music from. Note that if you're loading an external sound file - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new music with. - */ - public static MASound playMusic(String path, float volume) { - return playMusic(path, volume, true, false); - } - - /** - * Sets the current music playing for the entire game. - * - *

When you want to play music located externally, outside the assets folder, you can use a - * {@link FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * Flixel.playMusic(FlixelPathsUtil.external("your/path/here").path(), 1, false);
-   * }
- * - * @param path The path to load the music from. Note that if you're loading an external sound file - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new music with. - * @param looping Should the new music loop indefinitely? - */ - public static MASound playMusic(String path, float volume, boolean looping) { - return playMusic(path, volume, looping, false); - } - - /** - * Sets the current music playing for the entire game. - * - *

When you want to play music located externally, outside the assets folder, you can use a - * {@link FileHandle} like so: - * - *

{@code
-   * // Notice how it uses the FlixelPathsUtil class provided by Flixel'.
-   * // For the boolean attribute "external", you only should make it true for mobile builds,
-   * // otherwise just simply leave it be or make it "false" for other platforms like desktop.
-   * Flixel.playMusic(FlixelPathsUtil.external("your/path/here").path(), 1, false, true);
-   * }
- * - * @param path The path to load the music from. Note that if you're loading an external sound file - * outside the game's assets, you should use {@link FileHandle}; otherwise, just pass down a - * regular string (without {@code assets/} at the beginning). - * @param volume The volume to play the new music with. - * @param looping Should the new music loop indefinitely? - * @param external Should this music be loaded externally? (This is only for mobile platforms!) - */ - public static MASound playMusic(String path, float volume, boolean looping, boolean external) { - Signals.preMusicPlayed.dispatch(new MusicPlayedSignalData(music)); - if (music != null) { - music.stop(); - } - String resolvedPath = external ? path : FlixelPathsUtil.resolveAudioPath(path); - music = engine.createSound(resolvedPath, (short) 0, soundsGroup, external); - music.setVolume(volume); - music.setLooping(looping); - music.play(); - Signals.postMusicPlayed.dispatch(new MusicPlayedSignalData(music)); - return music; - } - - /** - * Sets the game master/global volume, which is automatically applied to all current sounds. - * - * @param volume The new master volume to set. - */ - public static void setMasterVolume(float volume) { - engine.setMasterVolume(!(volume > 1.0) ? volume : 1.0f); - masterVolume = volume; - } - - /** - * Shows an info alert notification to the user. - * - * @param title The title of the alert. - * @param message The message of the alert. - */ - public static void showInfoAlert(String title, String message) { - alerter.showInfoAlert(title, message); - } - - /** - * Shows a warning alert notification to the user. - * - * @param title The title of the alert. - * @param message The message of the alert. - */ - public static void showWarningAlert(String title, String message) { - alerter.showWarningAlert(title, message); - } - - /** - * Shows an error alert notification to the user. - * - * @param title The title of the alert. - * @param message The message of the alert. - */ - public static void showErrorAlert(String title, String message) { - alerter.showErrorAlert(title, message); - } - - public static boolean keyPressed(int key) { - return Gdx.input.isKeyPressed(key); - } - - public static boolean keyJustPressed(int key) { - return Gdx.input.isKeyJustPressed(key); - } - - public static void info(Object message) { - info(defaultLogger.getDefaultTag(), message); - } - - public static void info(String tag, Object message) { - defaultLogger.info(tag, message); - } - - public static void warn(Object message) { - warn(defaultLogger.getDefaultTag(), message); - } - - public static void warn(String tag, Object message) { - defaultLogger.warn(tag, message); - } - - public static void error(String message) { - error(defaultLogger.getDefaultTag(), message, null); - } - - public static void error(String tag, Object message) { - error(tag, message, null); - } - - public static void error(String tag, Object message, Throwable throwable) { - defaultLogger.error(tag, message, throwable); - } - - public static void setLogger(@NotNull FlixelLogger logger) { - defaultLogger = logger; - } - - public static void setDefaultLogTag(@NotNull String tag) { - defaultLogger.setDefaultTag(tag); - } - - public static Alerter getAlerter() { - return alerter; - } - - public static FlixelLogger getLogger() { - return defaultLogger; - } - - public static FlixelLogMode getLogMode() { - return defaultLogger.getLogMode(); - } - - public static FlixelGame getGame() { - return game; - } - - public static Stage getStage() { - return game.stage; - } - - public static FlixelState getState() { - return state; - } - - public static MASound getMusic() { - return music; - } - - public static Vector2 getWindowSize() { - return game.viewSize; - } - - public static int getWindowWidth() { - return (int) game.viewSize.x; - } - - public static int getWindowHeight() { - return (int) game.viewSize.y; - } - - public static MiniAudio getAudioEngine() { - return engine; - } - - public static float getMasterVolume() { - return masterVolume; - } - - public static AssetManager getAssetManager() { - return assetManager; - } - - public static MAGroup getSoundsGroup() { - return soundsGroup; - } - - public static float getElapsed() { - return Gdx.graphics.getDeltaTime(); - } - - public static FlixelCamera getCamera() { - return game.getCamera(); - } - - public static boolean isFullscreen() { - return Gdx.graphics.isFullscreen(); - } - - public static ApplicationType getPlatform() { - return Gdx.app.getType(); - } - - public static String getVersion() { - try (InputStream in = Flixel.class.getResourceAsStream("version.properties")) { - if (in != null) { - Properties p = new Properties(); - p.load(in); - String v = p.getProperty("version"); - if (v != null && !v.isEmpty()) return v; - } - } catch (Exception ignored) {} - return "Unknown"; - } - - public static void setLogMode(@NotNull FlixelLogMode mode) { - defaultLogger.setLogMode(mode); - } - - /** - * Contains all the global events that get dispatched when something happens in the game. - * - *

This includes anything from the screen being switched, the game updating every frame, and - * just about everything you can think of. - * - *

IMPORTANT DETAIL!: Anything with the {@code pre} and {@code post} prefixes always mean the - * same thing. If a signal has {@code pre}, then the signal gets ran BEFORE any functionality is - * executed, and {@code post} means AFTER all functionality was executed. - */ - public static final class Signals { - - public static final FlixelSignal preUpdate = new FlixelSignal<>(); - public static final FlixelSignal postUpdate = new FlixelSignal<>(); - public static final FlixelSignal preDraw = new FlixelSignal<>(); - public static final FlixelSignal postDraw = new FlixelSignal<>(); - public static final FlixelSignal preStateSwitch = new FlixelSignal<>(); - public static final FlixelSignal postStateSwitch = new FlixelSignal<>(); - public static final FlixelSignal preGameClose = new FlixelSignal<>(); - public static final FlixelSignal postGameClose = new FlixelSignal<>(); - public static final FlixelSignal windowFocused = new FlixelSignal<>(); - public static final FlixelSignal windowUnfocused = new FlixelSignal<>(); - public static final FlixelSignal windowMinimized = new FlixelSignal<>(); - public static final FlixelSignal preSoundPlayed = new FlixelSignal<>(); - public static final FlixelSignal postSoundPlayed = new FlixelSignal<>(); - public static final FlixelSignal preMusicPlayed = new FlixelSignal<>(); - public static final FlixelSignal postMusicPlayed = new FlixelSignal<>(); - - private Signals() {} - } - - private Flixel() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelBasic.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelBasic.java deleted file mode 100644 index dc411d3..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelBasic.java +++ /dev/null @@ -1,90 +0,0 @@ -package me.stringdotjar.flixelgdx; - -import com.badlogic.gdx.graphics.g2d.Batch; - -import me.stringdotjar.flixelgdx.display.FlixelCamera; - -/** - * This is the most generic Flixel object. Both {@link FlixelObject} and {@link FlixelCamera} - * extend this class. It has no size, position, or graphical data, only lifecycle flags and - * a unique ID. - * - * @see FlxBasic (HaxeFlixel) - */ -public class FlixelBasic { - - private static int idEnumerator = 0; - - /** A unique ID starting from 0 and increasing by 1 for each subsequent {@code FlixelBasic} created. */ - public final int ID; - - /** Controls whether {@link #update(float)} is automatically called. */ - public boolean active = true; - - /** - * Whether this object is alive. {@link #kill()} and {@link #revive()} both flip this - * switch (along with {@link #exists}). - */ - public boolean alive = true; - - /** Controls whether {@link #update(float)} and {@link #draw(Batch)} are automatically called. */ - public boolean exists = true; - - /** Controls whether {@link #draw(Batch)} is automatically called. */ - public boolean visible = true; - - public FlixelBasic() { - this.ID = idEnumerator++; - } - - /** - * Updates the logic of {@code this} FlixelBasic. - * - *

Override this function to update your object's position and appearance. - * This is where most game rules and behavioral code will go. - * - * @param elapsed Seconds elapsed since the last frame. - */ - public void update(float elapsed) {} - - /** - * Override this function to control how the object is drawn. Doing so is rarely necessary - * but can be very useful. - * - * @param batch The batch used for rendering. - */ - public void draw(Batch batch) {} - - /** - * Cleans up this object so it can be garbage-collected. A destroyed {@code FlixelBasic} - * should not be used anymore. Use {@link #kill()} if you only want to disable it - * temporarily and {@link #revive()} it later. - */ - public void destroy() { - exists = false; - active = false; - } - - /** - * Flags this object as nonexistent and dead. Default behavior sets both {@link #alive} - * and {@link #exists} to {@code false}. Use {@link #revive()} to bring it back. - */ - public void kill() { - alive = false; - exists = false; - } - - /** - * Brings this object back to life by setting {@link #alive} and {@link #exists} to - * {@code true}. - */ - public void revive() { - alive = true; - exists = true; - } - - @Override - public String toString() { - return getClass().getSimpleName() + "(ID=" + ID + ")"; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelGame.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelGame.java deleted file mode 100644 index 1330fac..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelGame.java +++ /dev/null @@ -1,726 +0,0 @@ -package me.stringdotjar.flixelgdx; - -import com.badlogic.gdx.Application; -import com.badlogic.gdx.ApplicationListener; -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Pixmap; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.SpriteBatch; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.scenes.scene2d.Stage; -import com.badlogic.gdx.utils.ScreenUtils; -import com.badlogic.gdx.utils.SnapshotArray; - -import me.stringdotjar.flixelgdx.display.FlixelCamera; -import me.stringdotjar.flixelgdx.display.FlixelState; -import me.stringdotjar.flixelgdx.logging.FlixelLogger; -import me.stringdotjar.flixelgdx.text.FlixelFontRegistry; -import me.stringdotjar.flixelgdx.tween.FlixelTween; -import me.stringdotjar.flixelgdx.util.FlixelRuntimeUtil; -import org.fusesource.jansi.AnsiConsole; - -import static me.stringdotjar.flixelgdx.signal.FlixelSignalData.UpdateSignalData; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Arrays; -import java.util.concurrent.ConcurrentLinkedQueue; - -/** - * The game object used for containing the main loop and core elements of the Flixel game. - * - *

To actually use this properly, you need to create a subclass of this and override - * the methods you want to change. - * - *

It is recommended for using this in the following way: - * - *

{@code
- * // Create a new subclass of FlixelGame.
- * // Remember that you can override any methods to add extra functionality
- * // to the game's behavior.
- * public class MyGame extends FlixelGame {
- *   public MyGame(String title, int width, int height, FlixelState initialState) {
- *     super(title, width, height, initialState);
- *   }
- * }
- * }
- * - * Then, in a platform-specific launcher, you can create a new instance of your game and run it: - * - *
{@code
- * // Example of how to create a new game instance and run it using the LWJGL3 launcher.
- * public class Lwjgl3Launcher {
- *   public static void main(String[] args) {
- *     if (StartupHelper.startNewJvmIfRequired()) { // This handles macOS support and helps on Windows.
- *       return;
- *     }
- *
- *     MyGame game = new MyGame(
- *       "My Game",
- *       800,
- *       600,
- *       new InitialState() // The initial state the game enters when it starts!
- *     );
- *
- *     FlixelLwjgl3Launcher.launch(game);
- *   }
- * }
- * }
- */
-public abstract class FlixelGame implements ApplicationListener {
-
-  /** The title displayed on the game's window. */
-  protected String title;
-
-  /** The size of the game's starting window position and its first camera. */
-  protected Vector2 viewSize;
-
-  /** The current window size stored in a vector object. */
-  protected Vector2 windowSize;
-
-  /** The entry point screen the game starts in (which becomes null after the game is done setting up!). */
-  protected FlixelState initialScreen;
-
-  /** The framerate of how fast the game should update and render. */
-  private int framerate;
-
-  /** Should the game use VSync to limit the framerate to the monitor's refresh rate? */
-  private boolean vsync;
-
-  /** Should the game start in fullscreen mode? */
-  protected boolean fullscreen;
-
-  /** Is the game's window currently focused? */
-  private boolean isFocused = true;
-
-  /** Is the game's window currently minimized? */
-  private boolean isMinimized = false;
-
-  /** The main stage used for rendering all screens and sprites on screen. */
-  protected Stage stage;
-
-  /** The main sprite batch used for rendering all sprites on screen. */
-  protected SpriteBatch batch;
-
-  /** The 1x1 texture used to draw the background color of the current screen. */
-  protected Texture bgTexture;
-
-  /** Where all the global cameras are stored. */
-  protected SnapshotArray cameras;
-
-  /** The queue of logs to be written to the log file. */
-  private final ConcurrentLinkedQueue logQueue = new ConcurrentLinkedQueue<>();
-
-  /** The maximum number of log files FlixelGDX can store. */
-  protected int maxLogFiles = 10;
-
-  /** Can the game store logs to a text file and store them in a folder of the game's working directory? */
-  private boolean canStoreLogs = true;
-
-  /** Is the game currently closing? */
-  private boolean isClosing = false;
-
-  /** Has the game successfully shut down? */
-  private boolean isClosed = false;
-
-  /** Reusable signal data for preUpdate dispatch (avoids per-frame allocation). */
-  private final UpdateSignalData preUpdateData = new UpdateSignalData();
-
-  /** Reusable signal data for postUpdate dispatch (avoids per-frame allocation). */
-  private final UpdateSignalData postUpdateData = new UpdateSignalData();
-
-  /** Signals the log writer thread to drain the queue and exit. */
-  private volatile boolean logWriterShutdownRequested = false;
-
-  /** Lock for coordinating between log producers and the log writer thread via wait/notify. */
-  private final Object logQueueLock = new Object();
-
-  /** Reference to the log writer thread so we can wait for it to finish during dispose. */
-  private Thread logThread;
-
-  /**
-   * Creates a new game instance with the details specified.
-   *
-   * @param title The title of the game's window.
-   * @param initialScreen The initial screen to load when the game starts.
-   */
-  public FlixelGame(String title, FlixelState initialScreen) {
-    this(title, 640, 360, initialScreen, 60, true, false);
-  }
-
-  /**
-   * Creates a new game instance with the details specified.
-   *
-   * @param title The title of the game's window.
-   * @param width The starting width of the game's window and how wide the camera should be.
-   * @param height The starting height of the game's window and how tall the camera should be.
-   * @param initialScreen The initial screen to load when the game starts.
-   */
-  public FlixelGame(String title, int width, int height, FlixelState initialScreen) {
-    this(title, width, height, initialScreen, 60, true, false);
-  }
-
-  /**
-   * Creates a new game instance with the details specified.
-   *
-   * @param title The title of the game's window.
-   * @param width The starting width of the game's window and how wide the camera should be.
-   * @param height The starting height of the game's window and how tall the camera should be.
-   * @param initialScreen The initial screen to load when the game starts.
-   * @param framerate The framerate of how fast the game should update and render.
-   */
-  public FlixelGame(String title, int width, int height, FlixelState initialScreen, int framerate) {
-    this(title, width, height, initialScreen, framerate, true, false);
-  }
-
-  /**
-   * Creates a new game instance with the details specified.
-   *
-   * @param title The title of the game's window.
-   * @param width The starting width of the game's window and how wide the camera should be.
-   * @param height The starting height of the game's window and how tall the camera should be.
-   * @param initialScreen The initial screen to load when the game starts.
-   * @param framerate The framerate of how fast the game should update and render.
-   * @param vsync Should the game use VSync to limit the framerate to the monitor's refresh rate?
-   */
-  public FlixelGame(String title, int width, int height, FlixelState initialScreen, int framerate, boolean vsync) {
-    this(title, width, height, initialScreen, framerate, vsync, false);
-  }
-
-  /**
-   * Creates a new game instance with the details specified.
-   *
-   * @param title The title of the game's window.
-   * @param width The starting width of the game's window and how wide the camera should be.
-   * @param height The starting height of the game's window and how tall the camera should be.
-   * @param initialScreen The initial screen to load when the game starts.
-   * @param framerate The framerate of how fast the game should update and render.
-   * @param vsync Should the game use VSync to limit the framerate to the monitor's refresh rate?
-   * @param fullscreen Should the game start in fullscreen mode?
-   */
-  public FlixelGame(String title, int width, int height, FlixelState initialScreen, int framerate, boolean vsync, boolean fullscreen) {
-    this.title = title;
-    this.viewSize = new Vector2(width, height);
-    this.windowSize = new Vector2(width, height);
-    this.initialScreen = initialScreen;
-    this.framerate = framerate;
-    this.vsync = vsync;
-    this.fullscreen = fullscreen;
-  }
-
-  @Override
-  public void create() {
-    configureCrashHandler(); // This should ALWAYS be called first no matter what!
-
-    // Install Jansi's ANSI-aware output stream so that ANSI color codes render correctly in
-    // terminals that don't natively support them (e.g. the Windows console). Skipped in the
-    // IDE because the IDE console already handles ANSI codes without Jansi's help.
-    if (FlixelRuntimeUtil.isRunningFromJar()) {
-      AnsiConsole.systemInstall();
-    }
-
-    batch = new SpriteBatch();
-    cameras = new SnapshotArray<>(FlixelCamera[]::new);
-    cameras.add(new FlixelCamera((int) viewSize.x, (int) viewSize.y));
-    stage = new Stage(getCamera().getViewport(), batch);
-
-    // Set up the background color for the game.
-    Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
-    pixmap.setColor(Color.WHITE);
-    pixmap.fill();
-    bgTexture = new Texture(pixmap);
-    pixmap.dispose();
-
-    // Set up the log thread to write logs to a file.
-    setupLogWriterThread();
-
-    Flixel.switchState(initialScreen);
-    initialScreen = null;
-  }
-
-  @Override
-  public void resize(int width, int height) {
-    FlixelCamera[] camerasArray = cameras.begin();
-    for (int i = 0; i < cameras.size; i++) {
-      FlixelCamera camera = camerasArray[i];
-      if (camera != null) {
-        camera.update(width, height, true);
-      }
-    }
-    cameras.end();
-  }
-
-  /**
-   * Updates the logic of the game's loop.
-   *
-   * @param elapsed The amount of time that occurred in the last frame.
-   */
-  public void update(float elapsed) {
-    preUpdateData.set(elapsed);
-    Flixel.Signals.preUpdate.dispatch(preUpdateData);
-
-    stage.act(elapsed);
-    FlixelTween.getGlobalManager().update(elapsed);
-
-    // Walk the state/substate chain. Each state in the chain is updated only
-    // if it is the active (innermost) state, or if its persistentUpdate flag is true.
-    FlixelState current = Flixel.getState();
-    while (current != null) {
-      FlixelState sub = current.getSubState();
-      boolean hasSubState = (sub != null);
-
-      if (!hasSubState || current.persistentUpdate) {
-        current.update(elapsed);
-
-        SnapshotArray members = current.getMembers();
-        FlixelBasic[] items = members.begin();
-        for (int j = 0; j < members.size; j++) {
-          FlixelBasic item = items[j];
-          if (item != null) {
-            item.update(elapsed);
-          }
-        }
-        members.end();
-      }
-
-      current = sub;
-    }
-
-    // Update all cameras.
-    FlixelCamera[] cams = cameras.begin();
-    for (int i = 0; i < cameras.size; i++) {
-      FlixelCamera camera = cams[i];
-      if (camera == null) {
-        continue;
-      }
-      camera.update(elapsed);
-    }
-    cameras.end();
-
-    postUpdateData.set(elapsed);
-    Flixel.Signals.postUpdate.dispatch(postUpdateData);
-  }
-
-  /**
-   * Updates the graphics and display of the game.
-   */
-  public void draw() {
-    Flixel.Signals.preDraw.dispatch();
-
-    ScreenUtils.clear(Color.BLACK); // Clear the screen to refresh the screen.
-    FlixelState state = Flixel.getState();
-
-    // Loop through all cameras and draw the state/substate chain onto each camera.
-    FlixelCamera[] cams = cameras.begin();
-    for (int i = 0; i < cameras.size; i++) {
-      FlixelCamera camera = cams[i];
-      if (camera == null) {
-        continue;
-      }
-
-      batch.setProjectionMatrix(camera.getCamera().combined);
-      batch.begin();
-
-      // Walk the state/substate chain. Each state is drawn only if it is the
-      // active (innermost) state, or if its persistentDraw flag is true.
-      FlixelState current = state;
-      while (current != null) {
-        FlixelState sub = current.getSubState();
-        boolean hasSubState = (sub != null);
-
-        if (!hasSubState || current.persistentDraw) {
-          batch.setColor(current.getBgColor());
-          batch.draw(bgTexture, camera.x, camera.y, camera.getWorldWidth(), camera.getWorldHeight());
-          batch.setColor(Color.WHITE);
-
-          SnapshotArray members = current.getMembers();
-          FlixelBasic[] mbrs = members.begin();
-          for (int j = 0; j < members.size; j++) {
-            FlixelBasic member = mbrs[j];
-            if (member == null) {
-              continue;
-            }
-            if (member instanceof FlixelObject m) {
-              if (camera.getCamera().frustum.boundsInFrustum(m.getX(), m.getY(), 0, m.getWidth(), m.getHeight(), 0)) {
-                m.draw(batch);
-              }
-            }
-          }
-          members.end();
-        }
-
-        current = sub;
-      }
-
-      batch.end();
-    }
-    cameras.end();
-
-    // Call user draw hooks for each state in the chain.
-    FlixelState drawHook = state;
-    while (drawHook != null) {
-      FlixelState sub = drawHook.getSubState();
-      boolean hasSubState = (sub != null);
-
-      if (!hasSubState || drawHook.persistentDraw) {
-        drawHook.draw(batch);
-      }
-
-      drawHook = sub;
-    }
-
-    stage.draw();
-
-    Flixel.Signals.postDraw.dispatch();
-  }
-
-  @Override
-  public final void render() {
-    float delta = Gdx.graphics.getDeltaTime();
-
-    windowSize.x = Gdx.graphics.getWidth();
-    windowSize.y = Gdx.graphics.getHeight();
-
-    fullscreen = Gdx.graphics.isFullscreen();
-
-    update(delta);
-    draw();
-  }
-
-  @Override
-  public void pause() {}
-
-  @Override
-  public void resume() {}
-
-  /** Called when the user regains focus on the game's window. */
-  public void onWindowFocused() {
-    isFocused = true;
-    Flixel.Signals.windowFocused.dispatch();
-  }
-
-  /** Called when the user loses focus on the game's window, while also not being minimized. */
-  public void onWindowUnfocused() {
-    if (isMinimized) {
-      return;
-    }
-    isFocused = false;
-    Flixel.Signals.windowUnfocused.dispatch();
-  }
-
-  /**
-   * Called when the user minimizes the game's window.
-   *
-   * @param iconified Whether the window is iconified (minimized) or not. This parameter is provided
-   * for compatibility with the window listener in the LWJGL3 (desktop) launcher.
-   */
-  public void onWindowMinimized(boolean iconified) {
-    isMinimized = iconified;
-    if (!isMinimized) {
-      return;
-    }
-    isFocused = false;
-    Flixel.Signals.windowMinimized.dispatch();
-  }
-
-  /**
-   * Sets fullscreen mode for the game's window.
-   *
-   * @param enabled If the game's window should be in fullscreen mode.
-   */
-  public void setFullscreen(boolean enabled) {
-    if (enabled) {
-      Gdx.graphics.setFullscreenMode(Gdx.graphics.getDisplayMode());
-    } else {
-      Gdx.graphics.setWindowedMode((int) viewSize.x, (int) viewSize.y);
-    }
-  }
-
-  /** Toggles fullscreen mode on or off, depending on the current state. */
-  public void toggleFullscreen() {
-    setFullscreen(!Flixel.isFullscreen());
-  }
-
-  /**
-   * Gets called when the game is closing to perform custom cleanup
-   * after core resources are disposed and before the log thread shuts down, so any
-   * logs written here (e.g. via {@link Flixel#info}) are persisted to the log file.
-   */
-  protected void close() {}
-
-  @Override
-  public final void dispose() {
-    if (isClosing) {
-      return;
-    }
-    isClosing = true;
-
-    Flixel.Signals.preGameClose.dispatch();
-
-    Flixel.getState().hide();
-    Flixel.getState().dispose();
-    stage.dispose();
-    batch.dispose();
-    bgTexture.dispose();
-
-    if (Flixel.getMusic() != null) {
-      Flixel.getMusic().dispose();
-    }
-    Flixel.getSoundsGroup().dispose();
-    Flixel.getAudioEngine().dispose();
-
-    FlixelFontRegistry.dispose();
-
-    if (AnsiConsole.isInstalled()) {
-      AnsiConsole.systemUninstall();
-    }
-
-    close();
-
-    Flixel.Signals.postGameClose.dispatch();
-
-    // Signal the log thread to drain the queue and exit. Must happen AFTER all dispose-time
-    // logs are added, so they get written before the thread exits.
-    synchronized (logQueueLock) {
-      logWriterShutdownRequested = true;
-      logQueueLock.notify();
-    }
-
-    // Wait for the log thread to flush all remaining logs to disk before marking closed.
-    if (logThread != null && logThread.isAlive()) {
-      try {
-        logThread.join(5000);
-      } catch (InterruptedException e) {
-        Thread.currentThread().interrupt();
-      }
-    }
-
-    isClosed = true;
-  }
-
-  /**
-   * Configures Flixel's crash handler to safely catch uncaught exceptions and gracefully close the game.
-   */
-  protected void configureCrashHandler() {
-    Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
-      String logs = FlixelRuntimeUtil.getFullExceptionMessage(throwable);
-      String msg = "There was an uncaught exception on thread \"" + thread.getName() + "\"!\n" + logs;
-      Flixel.error(msg);
-      Flixel.showErrorAlert("Uncaught Exception", msg);
-      dispose();
-      // Only use Gdx.app.exit() on non-iOS platforms to avoid App Store guideline violations!
-      if (Gdx.app.getType() != Application.ApplicationType.iOS) {
-        Gdx.app.exit();
-      }
-    });
-  }
-
-  /**
-   * Sets up the log writer thread to write logs to a file.
-   */
-  protected void setupLogWriterThread() {
-    String path = FlixelRuntimeUtil.getWorkingDirectory();
-    if (path == null) {
-      return;
-    }
-    String logsFolder = path.substring(0, path.lastIndexOf('/')) + "/logs/";
-
-    if (canStoreLogs) {
-      LocalDateTime now = LocalDateTime.now();
-      DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd_HH-mm-ss");
-      String date = now.format(formatter);
-
-      // Create/get the path to the logs folder, which is inside the game's working directory.
-      Gdx.files.absolute(logsFolder).mkdirs();
-
-      // Check if the logs folder has too many log files, and if so, delete the oldest ones.
-      // We prune when count >= maxLogFiles so that after creating this run's log we have at most maxLogFiles.
-      FileHandle[] logFiles = Gdx.files.absolute(logsFolder).list();
-      if (logFiles != null && logFiles.length >= maxLogFiles) {
-        // Sort by name so we delete oldest first (flixel-yyyy-MM-dd_HH-mm-ss.log is lexicographically ordered).
-        Arrays.sort(logFiles, (a, b) -> a.name().compareTo(b.name()));
-        int toDelete = logFiles.length - maxLogFiles + 1;
-        for (int i = 0; i < toDelete; i++) {
-          logFiles[i].delete();
-        }
-      }
-
-      FileHandle logFile = Gdx.files.absolute(logsFolder + "/flixel-" + date + ".log");
-
-      // Wire default logger (used by Flixel.info/warn/error) to also write to file.
-      FlixelLogger defaultLogger = Flixel.getLogger();
-      if (defaultLogger != null) {
-        defaultLogger.setLogFileLocation(logFile);
-        defaultLogger.setFileLineConsumer(this::enqueueLog);
-      }
-
-      final FileHandle logFileForThread = logFile;
-      logThread = new Thread(() -> {
-        try {
-          // Keep running until shutdown is requested AND the queue is fully drained.
-          // This ensures logs added during dispose() (e.g. "Disposing...") are written.
-          while (true) {
-            String log = logQueue.poll();
-            if (log != null) {
-              logFileForThread.writeString(log + "\n", true);
-            } else {
-              synchronized (logQueueLock) {
-                if (logWriterShutdownRequested) {
-                  break;
-                }
-                try {
-                  logQueueLock.wait();
-                } catch (InterruptedException e) {
-                  Thread.currentThread().interrupt();
-                }
-              }
-            }
-          }
-        } catch (Exception e) {}
-      });
-      logThread.setName("FlixelGDX Log Thread");
-      logThread.setDaemon(true);
-      logThread.start();
-    } else {
-      FileHandle[] logFiles = Gdx.files.absolute(logsFolder).list();
-      if (logFiles != null) {
-        for (FileHandle logFile : logFiles) {
-          logFile.delete();
-        }
-      }
-    }
-  }
-
-  /**
-   * Adds a log entry to the queue and notifies the log writer thread. Prefer this over
-   * {@link #getLogQueue()} when adding logs so the writer wakes immediately instead of polling.
-   */
-  public void enqueueLog(String log) {
-    logQueue.add(log);
-    synchronized (logQueueLock) {
-      logQueueLock.notify();
-    }
-  }
-
-  /**
-   * Gets the first camera that is part of the list. If the list is {@code null} or empty, then a new list (with a
-   * default camera accordingly) is created.
-   *
-   * @return The first camera in the list.
-   */
-  public FlixelCamera getCamera() {
-    Vector2 windowSize = Flixel.getWindowSize();
-    if (cameras == null) {
-      cameras = new SnapshotArray<>(FlixelCamera[]::new);
-    }
-    if (cameras.isEmpty()) {
-      cameras.add(new FlixelCamera((int) windowSize.x, (int) windowSize.y));
-      stage.setViewport(cameras.first().getViewport());
-    }
-    return cameras.first();
-  }
-
-  /**
-   * Resets the camera list to contain a single default camera with the current window size as its viewport.
-   */
-  public void resetCameras() {
-    FlixelCamera camera = new FlixelCamera((int) viewSize.x, (int) viewSize.y);
-    camera.update((int) windowSize.x, (int) windowSize.y, true);
-    cameras.clear();
-    cameras.add(camera);
-    stage.setViewport(camera.getViewport());
-  }
-
-  public String getTitle() {
-    return title;
-  }
-
-  public Vector2 getViewSize() {
-    return viewSize;
-  }
-
-  public Vector2 getWindowSize() {
-    return windowSize;
-  }
-
-  public int getWindowWidth() {
-    return (int) windowSize.x;
-  }
-
-  public  int getWindowHeight() {
-    return (int) windowSize.y;
-  }
-
-  public boolean isFocused() {
-    return isFocused;
-  }
-
-  public Stage getStage() {
-    return stage;
-  }
-
-  public boolean canStoreLogs() {
-    return canStoreLogs;
-  }
-
-  public void setCanStoreLogs(boolean canStoreLogs) {
-    this.canStoreLogs = canStoreLogs;
-  }
-
-  public ConcurrentLinkedQueue getLogQueue() {
-    return logQueue;
-  }
-
-  public SnapshotArray getCameras() {
-    return cameras;
-  }
-
-  public SpriteBatch getBatch() {
-    return batch;
-  }
-
-  public Texture getBgTexture() {
-    return bgTexture;
-  }
-
-  public boolean isMinimized() {
-    return isMinimized;
-  }
-
-  public boolean isClosing() {
-    return isClosing;
-  }
-
-  public boolean isClosed() {
-    return isClosed;
-  }
-
-  public int getFramerate() {
-    return framerate;
-  }
-
-  public void setFramerate(int framerate) {
-    this.framerate = framerate;
-    Gdx.graphics.setForegroundFPS(framerate);
-  }
-
-  public boolean isVsync() {
-    return vsync;
-  }
-
-  public void setVsync(boolean vsync) {
-    this.vsync = vsync;
-    Gdx.graphics.setVSync(vsync);
-  }
-
-  public boolean isFullscreen() {
-    return fullscreen;
-  }
-
-  public void setWindowSize(Vector2 newSize) {
-    viewSize = newSize;
-    Gdx.graphics.setWindowedMode((int) newSize.x, (int) newSize.y);
-  }
-}
diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelObject.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelObject.java
deleted file mode 100644
index 77a62f2..0000000
--- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelObject.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package me.stringdotjar.flixelgdx;
-
-/**
- * The base class for all visual/spatial objects in Flixel. Extends {@link FlixelBasic} with
- * position ({@link #x}, {@link #y}), dimensions ({@link #width}, {@link #height}), and
- * rotation ({@link #angle}).
- *
- * 

Most games interact with this through {@link me.stringdotjar.flixelgdx.FlixelSprite}, - * which adds graphical capabilities on top of this spatial foundation. - * - * @see FlxObject (HaxeFlixel) - */ -public class FlixelObject extends FlixelBasic { - - /** X position of the upper left corner of this object in world space. */ - protected float x = 0f; - - /** Y position of the upper left corner of this object in world space. */ - protected float y = 0f; - - /** The width of this object's hitbox. */ - protected float width = 0f; - - /** The height of this object's hitbox. */ - protected float height = 0f; - - /** The angle (in degrees) of this object. Used for visual rotation in sprites. */ - protected float angle = 0f; - - public FlixelObject() { - super(); - } - - public FlixelObject(float x, float y) { - super(); - this.x = x; - this.y = y; - } - - public FlixelObject(float x, float y, float width, float height) { - super(); - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - public float getX() { - return x; - } - - public void setX(float x) { - this.x = x; - } - - public float getY() { - return y; - } - - public void setY(float y) { - this.y = y; - } - - public float getWidth() { - return width; - } - - public void setWidth(float width) { - this.width = width; - } - - public float getHeight() { - return height; - } - - public void setHeight(float height) { - this.height = height; - } - - public float getAngle() { - return angle; - } - - public void setAngle(float angle) { - this.angle = angle; - } - - /** - * Helper function to set the coordinates of this object. - * - * @param x The new x position. - * @param y The new y position. - */ - public void setPosition(float x, float y) { - this.x = x; - this.y = y; - } - - /** - * Shortcut for setting both {@link #width} and {@link #height}. - * - * @param width The new width. - * @param height The new height. - */ - public void setSize(float width, float height) { - this.width = width; - this.height = height; - } - - /** Adds {@code dx} to the current X position. */ - public void changeX(float dx) {x += dx;} - - /** Adds {@code dy} to the current Y position. */ - public void changeY(float dy) {y += dy;} - - /** Adds {@code dr} degrees to the current rotation angle. */ - public void changeRotation(float dr) {angle += dr;} - - @Override - public String toString() { - return getClass().getSimpleName() + "(ID=" + ID - + ", x=" + x + ", y=" + y - + ", w=" + width + ", h=" + height + ")"; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelSprite.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelSprite.java deleted file mode 100644 index 1aef8bb..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/FlixelSprite.java +++ /dev/null @@ -1,505 +0,0 @@ -package me.stringdotjar.flixelgdx; - -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Animation; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.graphics.g2d.TextureAtlas.AtlasRegion; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.math.Rectangle; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.Pool; -import com.badlogic.gdx.utils.XmlReader; - -import me.stringdotjar.flixelgdx.display.FlixelCamera; - -import com.badlogic.gdx.utils.ObjectMap; - -import java.util.Comparator; - -/** - * The core building block of all Flixel games. Extends {@link FlixelObject} with graphical - * capabilities including texture rendering, animation, scaling, rotation, tinting, and flipping. - * - *

It is common to extend {@code FlixelSprite} for your own game's needs; for example a - * {@code SpaceShip} class may extend {@code FlixelSprite} but add additional game-specific fields. - * - * @see FlxSprite (HaxeFlixel) - */ -public class FlixelSprite extends FlixelObject implements Pool.Poolable { - - /** The texture image that {@code this} sprite uses. */ - protected Texture texture; - - /** The hitbox used for collision detection and angling. */ - protected Rectangle hitbox; - - /** The cameras that {@code this} sprite is projected onto. */ - protected FlixelCamera[] cameras; - - /** The atlas regions used in this sprite (used for animations). */ - protected Array atlasRegions; - - /** A map that animations are stored and registered in. */ - protected final ObjectMap> animations; - - /** The current frame that {@code this} sprite is on in its animation (if one is playing). */ - protected TextureAtlas.AtlasRegion currentFrame; - - /** Used for updating {@code this} sprite's current animation. */ - protected float stateTime = 0; - - /** The name of the current animation playing. */ - private String currentAnim = ""; - - /** Is {@code this} sprites current animation looping indefinitely? */ - private boolean looping = true; - - /** - * Where all the image frames are stored. This is also where the main image is stored when using - * {@link #loadGraphic(FileHandle)}. - */ - protected TextureRegion[][] frames; - - /** The currently active texture region rendered when no animation is playing. */ - protected TextureRegion currentRegion; - - /** Horizontal scale factor. {@code 1} = normal size. */ - protected float scaleX = 1f; - - /** Vertical scale factor. {@code 1} = normal size. */ - protected float scaleY = 1f; - - /** X component of the rotation/scale origin point. */ - protected float originX = 0f; - - /** Y component of the rotation/scale origin point. */ - protected float originY = 0f; - - /** The color tint applied when drawing this sprite. */ - protected final Color color = new Color(Color.WHITE); - - /** Whether this sprite is flipped horizontally. */ - protected boolean flipX = false; - - /** Whether this sprite is flipped vertically. */ - protected boolean flipY = false; - - public FlixelSprite() { - super(); - animations = new ObjectMap<>(); - cameras = new FlixelCamera[]{Flixel.getCamera()}; - } - - /** - * Updates {@code this} sprite. - * - * @param delta The amount of time that has passed since the last frame update. - */ - @Override - public void update(float delta) { - Animation anim = animations.get(currentAnim); - if (anim != null) { - stateTime += delta; - currentFrame = (TextureAtlas.AtlasRegion) anim.getKeyFrame(stateTime, looping); - currentRegion = currentFrame; - } - } - - /** - * Load's a texture and automatically resizes the size of {@code this} sprite. - * - * @param path The directory of the {@code .png} to load onto {@code this} sprite. - * @return {@code this} sprite for chaining. - */ - public FlixelSprite loadGraphic(FileHandle path) { - Texture texture = new Texture(path); - return loadGraphic(texture, texture.getWidth(), texture.getHeight()); - } - - /** - * Load's a texture and automatically resizes the size of {@code this} sprite. - * - * @param path The directory of the {@code .png} to load onto {@code this} sprite. - * @param frameWidth How wide the sprite should be. - * @return {@code this} sprite for chaining. - */ - public FlixelSprite loadGraphic(FileHandle path, int frameWidth) { - Texture texture = new Texture(path); - return loadGraphic(texture, frameWidth, texture.getHeight()); - } - - /** - * Load's a texture and automatically resizes the size of {@code this} sprite. - * - * @param path The directory of the {@code .png} to load onto {@code this} sprite. - * @param frameWidth How wide the sprite should be. - * @param frameHeight How tall the sprite should be. - * @return {@code this} sprite for chaining. - */ - public FlixelSprite loadGraphic(FileHandle path, int frameWidth, int frameHeight) { - return loadGraphic(new Texture(path), frameWidth, frameHeight); - } - - public FlixelSprite loadGraphic(Texture texture, int frameWidth, int frameHeight) { - frames = TextureRegion.split(texture, frameWidth, frameHeight); - currentRegion = frames[0][0]; - setSize(frameWidth, frameHeight); - setOriginCenter(); - return this; - } - - /** - * Loads an {@code .xml} spritesheet with {@code SubTexture} data inside of it. - * - * @param texture The path to the {@code .png} texture file for slicing and extracting the - * different frames from. - * @param xmlFile The path to the {@code .xml} file which contains the data for each subtexture of - * the sparrow atlas. - * @return {@code this} sprite for chaining. - */ - public FlixelSprite loadSparrowFrames(FileHandle texture, FileHandle xmlFile) { - return loadSparrowFrames(new Texture(texture), new XmlReader().parse(xmlFile)); - } - - /** - * Loads an {@code .xml} spritesheet with {@code SubTexture} data inside of it. - * - * @param texture The {@code .png} texture file for slicing and extracting the different frames - * from. - * @param xmlFile The {@link XmlReader.Element} data which contains the data for each subtexture - * of the sparrow atlas. - * @return {@code this} sprite for chaining. - */ - public FlixelSprite loadSparrowFrames(Texture texture, XmlReader.Element xmlFile) { - atlasRegions = new Array<>(AtlasRegion[]::new); - - for (XmlReader.Element subTexture : xmlFile.getChildrenByName("SubTexture")) { - String name = subTexture.getAttribute("name"); - int x = subTexture.getInt("x"); - int y = subTexture.getInt("y"); - int width = subTexture.getInt("width"); - int height = subTexture.getInt("height"); - - this.texture = texture; - TextureAtlas.AtlasRegion region = new TextureAtlas.AtlasRegion(texture, x, y, width, height); - region.name = name; - - if (subTexture.hasAttribute("frameX")) { - region.offsetX = Math.abs(subTexture.getInt("frameX")); - region.offsetY = Math.abs(subTexture.getInt("frameY")); - region.originalWidth = subTexture.getInt("frameWidth"); - region.originalHeight = subTexture.getInt("frameHeight"); - } else { - region.offsetX = 0; - region.offsetY = 0; - region.originalWidth = width; - region.originalHeight = height; - } - - atlasRegions.add(region); - } - - if (atlasRegions.size > 0) { - currentFrame = atlasRegions.first(); - currentRegion = currentFrame; - setSize(currentFrame.getRegionWidth(), currentFrame.getRegionHeight()); - } - - currentRegion = atlasRegions.first(); - setSize(currentRegion.getRegionWidth(), currentRegion.getRegionHeight()); - return this; - } - - /** - * Adds an animation by looking for sub textures that start with the prefix passed down. - * - * @param name The name of the animation (e.g., "confirm"). - * @param prefix The prefix in the {@code .xml} file (e.g., "left confirm"). - * @param frameRate How fast the animation should play in frames-per-second. Standard is 24. - * @param loop Should the new animation loop indefinitely? - */ - public void addAnimationByPrefix(String name, String prefix, int frameRate, boolean loop) { - Array frames = new Array<>(); - - for (TextureAtlas.AtlasRegion region : atlasRegions) { - if (region.name.startsWith(prefix)) { - frames.add(region); - } - } - - if (frames.size > 0) { - // Ensure frames are sorted alphabetically (e.g., confirm0000, confirm0001). - frames.sort(Comparator.comparing(o -> o.name)); - - animations.put( - name, - new Animation<>(1f / frameRate, frames, loop ? Animation.PlayMode.LOOP : Animation.PlayMode.NORMAL) - ); - } - } - - /** - * Adds a new animation to the animations list, if it doesn't exist already. - * - * @param name The name of the animation. This is what you'll use every time you use {@code - * playAnimation()}. - * @param frameIndices An array of integers used for animation frame indices. - * @param frameDuration How long each frame lasts for in seconds. - */ - public void addAnimation(String name, int[] frameIndices, float frameDuration) { - Array animFrames = new Array<>(); - - // Convert 1D indices (0, 1, 2...) to 2D grid coordinates. - int cols = frames[0].length; - for (int index : frameIndices) { - int row = index / cols; - int col = index % cols; - animFrames.add(frames[row][col]); - } - - animations.put(name, new Animation<>(frameDuration, animFrames)); - } - - /** - * Plays an animation {@code this} sprite has, with looping enabled by default. - * - * @param name The name of the animation to play. - */ - public void playAnimation(String name) { - playAnimation(name, true); - } - - /** - * Plays an animation {@code this} sprite has, if it exists. - * - * @param name The name of the animation to play. - * @param loop Should this animation loop indefinitely? - */ - public void playAnimation(String name, boolean loop) { - playAnimation(name, loop, true); - } - - /** - * Plays an animation {@code this} sprite has, if it exists. - * - * @param name The name of the animation to play. - * @param loop Should this animation loop indefinitely? - * @param forceRestart Should the animation automatically restart regardless if its playing? - */ - public void playAnimation(String name, boolean loop, boolean forceRestart) { - if (currentAnim.equals(name) && !forceRestart) { - return; - } - if (isAnimationFinished() || forceRestart) { - this.currentAnim = name; - this.looping = loop; - this.stateTime = 0; - } - } - - @Override - public void draw(Batch batch) { - if (currentFrame != null) { - float oX = currentFrame.originalWidth / 2f; - float oY = currentFrame.originalHeight / 2f; - - float drawX = x + currentFrame.offsetX; - float drawY = y + (currentFrame.originalHeight - currentFrame.getRegionHeight() - currentFrame.offsetY); - - batch.setColor(color); - batch.draw( - currentFrame, - drawX, - drawY, - oX - currentFrame.offsetX, - oY - (currentFrame.originalHeight - currentFrame.getRegionHeight() - currentFrame.offsetY), - currentFrame.getRegionWidth(), - currentFrame.getRegionHeight(), - scaleX, - scaleY, - angle); - batch.setColor(Color.WHITE); - } else if (currentRegion != null) { - float sx = flipX ? -scaleX : scaleX; - float sy = flipY ? -scaleY : scaleY; - - batch.setColor(color); - batch.draw(currentRegion, x, y, originX, originY, width, height, sx, sy, angle); - batch.setColor(Color.WHITE); - } - } - - @Override - public void destroy() { - reset(); - } - - public boolean isAnimationFinished() { - Animation anim = animations.get(currentAnim); - if (anim == null) return true; - return anim.isAnimationFinished(stateTime); - } - - @Override - public void reset() { - setPosition(0f, 0f); - stateTime = 0; - currentAnim = null; - looping = true; - scaleX = 1f; - scaleY = 1f; - originX = 0f; - originY = 0f; - color.set(Color.WHITE); - flipX = false; - flipY = false; - angle = 0f; - if (texture != null) { - texture.dispose(); - texture = null; - } - if (currentFrame != null) { - currentFrame.getTexture().dispose(); - currentFrame = null; - } - currentRegion = null; - if (atlasRegions != null) { - for (int i = atlasRegions.size - 1; i >= 0; i--) { - var region = atlasRegions.items[i]; - if (region != null) { - region.getTexture().dispose(); - } - } - atlasRegions.setSize(0); - atlasRegions = null; - } - if (frames != null) { - for (int i = frames.length - 1; i >= 0; i--) { - var frame = frames[i]; - if (frame != null) { - for (TextureRegion region : frame) { - if (region != null) { - region.getTexture().dispose(); - } - } - } - } - frames = null; - } - } - - public float getScaleX() { - return scaleX; - } - - public float getScaleY() { - return scaleY; - } - - public void setScale(float scaleXY) { - scaleX = scaleY = scaleXY; - } - - public void setScale(float scaleX, float scaleY) { - this.scaleX = scaleX; - this.scaleY = scaleY; - } - - public float getOriginX() { - return originX; - } - - public float getOriginY() { - return originY; - } - - public void setOrigin(float originX, float originY) { - this.originX = originX; - this.originY = originY; - } - - public void setOriginCenter() { - originX = width / 2f; - originY = height / 2f; - } - - public Color getColor() { - return color; - } - - public void setColor(Color tint) { - color.set(tint); - } - - public void setColor(float r, float g, float b, float a) { - color.set(r, g, b, a); - } - - public void setAlpha(float a) { - color.a = a; - } - - public void flip(boolean x, boolean y) { - flipX ^= x; - flipY ^= y; - } - - public boolean isFlipX() { - return flipX; - } - - public boolean isFlipY() { - return flipY; - } - - public void setRegion(TextureRegion region) { - currentRegion = region; - } - - public TextureRegion getRegion() { - return currentRegion; - } - - public int getRegionWidth() { - return currentRegion != null ? currentRegion.getRegionWidth() : 0; - } - - public int getRegionHeight() { - return currentRegion != null ? currentRegion.getRegionHeight() : 0; - } - - public ObjectMap> getAnimations() { - return animations; - } - - public Array getAtlasRegions() { - return atlasRegions; - } - - public FlixelCamera[] getCameras() { - return cameras; - } - - public TextureAtlas.AtlasRegion getCurrentFrame() { - return currentFrame; - } - - public float getStateTime() { - return stateTime; - } - - public String getCurrentAnim() { - return currentAnim; - } - - public boolean isLooping() { - return looping; - } - - public TextureRegion[][] getFrames() { - return frames; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/backend/Alerter.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/backend/Alerter.java deleted file mode 100644 index d3be208..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/backend/Alerter.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.stringdotjar.flixelgdx.backend; - -/** - * Interface for displaying alert notifications to the user. - */ -public interface Alerter { - void showInfoAlert(String title, String message); - void showWarningAlert(String title, String message); - void showErrorAlert(String title, String message); -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelCamera.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelCamera.java deleted file mode 100644 index 9cdc422..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelCamera.java +++ /dev/null @@ -1,1186 +0,0 @@ -package me.stringdotjar.flixelgdx.display; - -import com.badlogic.gdx.graphics.Camera; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.OrthographicCamera; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Rectangle; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.viewport.FitViewport; -import com.badlogic.gdx.utils.viewport.Viewport; -import me.stringdotjar.flixelgdx.Flixel; -import me.stringdotjar.flixelgdx.FlixelBasic; -import me.stringdotjar.flixelgdx.FlixelObject; - -/** - * The camera class is used to display the game's visuals. - * - *

- * By default, one camera is created automatically, that is the same size as the - * window. You can - * add more cameras or even replace the main camera using utilities in - * {@code FlixelGame}. - * - *

- * Every camera wraps a libGDX {@link Camera} and {@link Viewport} internally. - * By default, an - * {@link OrthographicCamera} and {@link FitViewport} are used, but custom types can be provided - * via the constructor overloads. - * - * @see FlxCamera - * (HaxeFlixel) - */ -public class FlixelCamera extends FlixelBasic { - - /** - * Any {@code FlixelCamera} with a zoom of 0 (the default constructor value) - * will receive this zoom level instead. - */ - public static float defaultZoom = 1.0f; - - private Camera camera; - private Viewport viewport; - - /** The alpha value of this camera display (0.0 to 1.0). */ - public float alpha = 1.0f; - - /** The angle of the camera display in degrees. */ - public float angle = 0f; - - /** - * Whether the camera display is smooth and filtered, or chunky and pixelated. - */ - public boolean antialiasing = false; - - /** The natural background color of the camera. Defaults to black. */ - public Color bgColor = new Color(Color.BLACK); - - /** The color tint of the camera display. */ - public Color color = new Color(Color.WHITE); - - /** Whether positions of rendered objects are rounded to whole pixels. */ - public boolean pixelPerfectRender = false; - - /** - * If {@code true}, screen shake offsets will be rounded to whole pixels. - * If {@code null}, {@link #pixelPerfectRender} is used instead. - */ - public boolean pixelPerfectShake = false; - - /** Whether to use alpha blending for the camera's background fill. */ - public boolean useBgAlphaBlending = false; - - /** - * The X position of this camera's display in native screen pixels. - * {@link #zoom} does NOT affect this value. - */ - public float x; - - /** - * The Y position of this camera's display in native screen pixels. - * {@link #zoom} does NOT affect this value. - */ - public float y; - - /** How wide the camera display is, in game pixels. */ - public int width; - - /** How tall the camera display is, in game pixels. */ - public int height; - - /** - * The basic parallax scrolling values, essentially the camera's top-left corner - * position - * in world coordinates. Use {@link #focusOn(Vector2)} to look at a specific - * world point. - */ - public final Vector2 scroll = new Vector2(); - - /** - * Lower bound of the camera's scroll on the X axis. {@code null} = unbounded. - */ - public Float minScrollX; - - /** - * Upper bound of the camera's scroll on the X axis. {@code null} = unbounded. - */ - public Float maxScrollX; - - /** - * Lower bound of the camera's scroll on the Y axis. {@code null} = unbounded. - */ - public Float minScrollY; - - /** - * Upper bound of the camera's scroll on the Y axis. {@code null} = unbounded. - */ - public Float maxScrollY; - - /** - * The dead zone rectangle, measured from the camera's upper left corner in game pixels. - * The camera will always keep the focus object inside this zone unless bumping against - * scroll bounds. For rapid prototyping, use the preset styles with - * {@link #follow(FlixelObject, FollowStyle, float)}. - */ - public Rectangle deadzone; - - /** Used to force the camera to look ahead of the target. */ - public final Vector2 followLead = new Vector2(); - - /** - * The ratio of the distance to the follow target the camera moves per 1/60 sec. - * {@code 1.0} = snap to target. {@code 0.0} = don't move. Lower values produce - * smoother motion. - */ - public float followLerp = 1.0f; - - /** The current follow style. */ - public FollowStyle style = FollowStyle.LOCKON; - - /** The {@link FlixelObject} the camera follows. Set via {@link #follow}. */ - public FlixelObject target; - - /** Offset applied to the follow target's position. */ - public final Vector2 targetOffset = new Vector2(); - - /** Camera's initial zoom value, captured at construction time. */ - public final float initialZoom; - - /** - * Internal zoom storage. Use {@link #getZoom()} / {@link #setZoom(float)} to access. - * A value of {@code 1} = 1:1. {@code 2} = 2x zoom in (world appears larger). - */ - private float zoom; - - private boolean flashActive = false; - private final Color flashColor = new Color(Color.WHITE); - private float flashDuration = 1f; - private float flashElapsed = 0f; - private float flashAlpha = 0f; - private Runnable flashOnComplete; - - private boolean fadeActive = false; - private final Color fadeColor = new Color(Color.BLACK); - private float fadeDuration = 1f; - private float fadeElapsed = 0f; - private float fadeAlpha = 0f; - private boolean fadeIn = false; - private Runnable fadeOnComplete; - - private boolean shakeActive = false; - private float shakeIntensity = 0.05f; - private float shakeDuration = 0.5f; - private float shakeElapsed = 0f; - private FlxAxes shakeAxes = FlxAxes.XY; - private Runnable shakeOnComplete; - private float shakeOffsetX = 0f; - private float shakeOffsetY = 0f; - - /** Reusable vector for internal calculations that would otherwise allocate. */ - private final Vector2 tmpVec = new Vector2(); - - /** Reusable rectangle for internal calculations that would otherwise allocate. */ - private final Rectangle tmpRect = new Rectangle(); - - /** - * Creates a camera sized to the current window using the default - * {@link OrthographicCamera} and {@link FitViewport}. - */ - public FlixelCamera() { - this(0f, 0f, Flixel.getWindowWidth(), Flixel.getWindowHeight(), 0f); - } - - /** - * Creates a camera with the given dimensions using the default camera and - * viewport types. - * - * @param width The width of the camera display in game pixels. - * @param height The height of the camera display in game pixels. - */ - public FlixelCamera(int width, int height) { - this(0f, 0f, width, height, 0f); - } - - /** - * Creates a camera with a custom libGDX {@link Camera}, wrapped in a default - * {@link FitViewport}. - * - * @param width The width of the camera display in game pixels. - * @param height The height of the camera display in game pixels. - * @param camera A custom libGDX Camera (e.g. - * {@link com.badlogic.gdx.graphics.PerspectiveCamera}). - */ - public FlixelCamera(int width, int height, Camera camera) { - this(0f, 0f, width, height, 0f, camera, null); - } - - /** - * Creates a camera with a custom libGDX {@link Viewport}. The camera is - * extracted from the viewport. - * - * @param width The width of the camera display in game pixels. - * @param height The height of the camera display in game pixels. - * @param viewport A custom libGDX Viewport (e.g. - * {@link com.badlogic.gdx.utils.viewport.ScreenViewport}). - */ - public FlixelCamera(int width, int height, Viewport viewport) { - this(0f, 0f, width, height, 0f, null, viewport); - } - - /** - * Creates a camera at the given display position, size, and zoom level using - * default types. - * - * @param x X location of the camera's display in native screen pixels. - * @param y Y location of the camera's display in native screen pixels. - * @param width The width of the camera display in game pixels. 0 = window - * width. - * @param height The height of the camera display in game pixels. 0 = window - * height. - * @param zoom The initial zoom level. 0 = {@link #defaultZoom}. - */ - public FlixelCamera(float x, float y, int width, int height, float zoom) { - this(x, y, width, height, zoom, null, null); - } - - /** - * Full constructor allowing fully custom libGDX {@link Camera} and - * {@link Viewport} types. - * - *

- * If {@code viewport} is provided, its camera is used (the {@code camera} parameter is ignored). - * If only {@code camera} is provided, it is wrapped in a new {@link FitViewport}. - * If neither is provided, an {@link OrthographicCamera} and {@link FitViewport} - * are created. - * - * @param x X location of the camera's display in native screen pixels. - * @param y Y location of the camera's display in native screen pixels. - * @param width The width of the camera display in game pixels. 0 = window width. - * @param height The height of the camera display in game pixels. 0 = window height. - * @param zoom The initial zoom level. 0 = {@link #defaultZoom}. 2 = 2x magnification. - * @param camera Custom libGDX Camera, or {@code null} for a default {@link OrthographicCamera}. - * @param viewport Custom libGDX Viewport, or {@code null} for a default {@link FitViewport}. - */ - public FlixelCamera(float x, float y, int width, int height, float zoom, Camera camera, Viewport viewport) { - super(); - this.x = x; - this.y = y; - this.width = (width <= 0) ? Flixel.getWindowWidth() : width; - this.height = (height <= 0) ? Flixel.getWindowHeight() : height; - - if (viewport != null) { - this.viewport = viewport; - this.camera = viewport.getCamera(); - } else if (camera != null) { - this.camera = camera; - this.viewport = new FitViewport(this.width, this.height, this.camera); - } else { - this.camera = new OrthographicCamera(this.width, this.height); - this.viewport = new FitViewport(this.width, this.height, this.camera); - } - - this.zoom = (zoom == 0f) ? defaultZoom : zoom; - this.initialZoom = this.zoom; - applyZoom(); - } - - /** Returns the underlying libGDX {@link Camera} used for projection. */ - public Camera getCamera() { - return camera; - } - - /** Returns the underlying libGDX {@link Viewport} used for screen scaling. */ - public Viewport getViewport() { - return viewport; - } - - /** - * Applies the viewport's OpenGL viewport rectangle. Call before rendering - * through this camera. - */ - public void apply() { - viewport.apply(); - } - - /** - * Updates the viewport in response to a window resize event. - * - * @param screenWidth The new screen width in pixels. - * @param screenHeight The new screen height in pixels. - * @param centerCamera Whether to re-center the camera in the viewport. - */ - public void update(int screenWidth, int screenHeight, boolean centerCamera) { - viewport.update(screenWidth, screenHeight, centerCamera); - } - - /** Returns the world width of the underlying viewport. */ - public float getWorldWidth() { - return viewport.getWorldWidth(); - } - - /** Returns the world height of the underlying viewport. */ - public float getWorldHeight() { - return viewport.getWorldHeight(); - } - - /** - * Updates the camera scroll, follow logic, and active effects. - * Called once per frame from the game loop. - * - * @param elapsed Seconds elapsed since the last frame. - */ - @Override - public void update(float elapsed) { - if (!active || !exists) { - return; - } - - updateFollow(elapsed); - updateScroll(); - updateFlash(elapsed); - updateFade(elapsed); - updateShake(elapsed); - - if (camera instanceof OrthographicCamera ortho) { - ortho.up.set(0, 1, 0); - ortho.direction.set(0, 0, -1); - if (angle != 0f) { - ortho.rotate(angle); - } - } - - float camX = scroll.x + getViewWidth() / 2f + shakeOffsetX; - float camY = scroll.y + getViewHeight() / 2f + shakeOffsetY; - camera.position.set(camX, camY, 0); - camera.update(); - } - - /** - * Tells this camera to follow the given sprite using {@link FollowStyle#LOCKON} - * and lerp of 1. - * - * @param target The sprite to follow. Pass {@code null} to stop following. - */ - public void follow(FlixelObject target) { - follow(target, FollowStyle.LOCKON, 1.0f); - } - - /** - * Tells this camera to follow the given sprite with the specified style and a - * lerp of 1. - * - * @param target The sprite to follow. Pass {@code null} to stop following. - * @param style One of the preset {@link FollowStyle} dead zone presets. - */ - public void follow(FlixelObject target, FollowStyle style) { - follow(target, style, 1.0f); - } - - /** - * Tells this camera to follow the given sprite. - * - * @param target The sprite to follow. Pass {@code null} to stop following. - * @param style One of the preset {@link FollowStyle} dead zone presets. - * @param lerp How much lag the camera should have. {@code 1.0} = snap, lower - * = smoother. - */ - public void follow(FlixelObject target, FollowStyle style, float lerp) { - this.target = target; - this.style = style; - this.followLerp = lerp; - updateDeadzoneForStyle(); - } - - /** - * Instantly moves the camera so the given world point is centered. - * - * @param point The world-space point to focus on. - */ - public void focusOn(Vector2 point) { - scroll.set( - point.x - getViewWidth() / 2f, - point.y - getViewHeight() / 2f - ); - } - - /** - * Snaps the camera to the current {@link #target} position with no easing, then - * clamps scroll to bounds. Useful after teleporting the target. - */ - public void snapToTarget() { - if (target == null) { - return; - } - float tx = target.getX() + target.getWidth() / 2f + targetOffset.x + followLead.x; - float ty = target.getY() + target.getHeight() / 2f + targetOffset.y + followLead.y; - focusOn(tmpVec.set(tx, ty)); - updateScroll(); - } - - private void updateFollow(float elapsed) { - if (target == null) { - return; - } - - float tx = target.getX() + target.getWidth() / 2f + targetOffset.x + followLead.x; - float ty = target.getY() + target.getHeight() / 2f + targetOffset.y + followLead.y; - - float desiredX = tx - getViewWidth() / 2f; - float desiredY = ty - getViewHeight() / 2f; - - if (followLerp >= 1.0f) { - scroll.set(desiredX, desiredY); - return; - } - - if (deadzone != null) { - float dzLeft = scroll.x + deadzone.x; - float dzRight = dzLeft + deadzone.width; - float dzTop = scroll.y + deadzone.y; - float dzBottom = dzTop + deadzone.height; - - if (tx < dzLeft) { - desiredX = tx - deadzone.x; - } else if (tx > dzRight) { - desiredX = tx - deadzone.x - deadzone.width; - } else { - desiredX = scroll.x; - } - - if (ty < dzTop) { - desiredY = ty - deadzone.y; - } else if (ty > dzBottom) { - desiredY = ty - deadzone.y - deadzone.height; - } else { - desiredY = scroll.y; - } - } - - float lerpFactor = 1f - (float) Math.pow(1f - followLerp, elapsed * 60f); - scroll.x = MathUtils.lerp(scroll.x, desiredX, lerpFactor); - scroll.y = MathUtils.lerp(scroll.y, desiredY, lerpFactor); - } - - private void updateDeadzoneForStyle() { - if (style == null || style == FollowStyle.NO_DEAD_ZONE) { - deadzone = null; - return; - } - - float w, h; - switch (style) { - case LOCKON -> { - w = 1; - h = 1; - } - case PLATFORMER -> { - w = width / 8f; - h = height / 3f; - } - case TOPDOWN -> { - w = width / 3f; - h = height / 3f; - } - case TOPDOWN_TIGHT -> { - w = width / 8f; - h = height / 8f; - } - case SCREEN_BY_SCREEN -> { - w = width; - h = height; - } - default -> { - deadzone = null; - return; - } - } - deadzone = new Rectangle((width - w) / 2f, (height - h) / 2f, w, h); - } - - /** - * Specifies the bounds of where the camera scroll is allowed. Pass {@code null} - * for any side - * to leave it unbounded. - * - * @param minX Lower X bound (or {@code null}). - * @param maxX Upper X bound (or {@code null}). - * @param minY Lower Y bound (or {@code null}). - * @param maxY Upper Y bound (or {@code null}). - */ - public void setScrollBounds(Float minX, Float maxX, Float minY, Float maxY) { - this.minScrollX = minX; - this.maxScrollX = maxX; - this.minScrollY = minY; - this.maxScrollY = maxY; - } - - /** - * Specifies scroll bounds as a bounding rectangle (typically the level size). - * - * @param x Smallest X value (usually 0). - * @param y Smallest Y value (usually 0). - * @param w Largest X extent (usually level width). - * @param h Largest Y extent (usually level height). - */ - public void setScrollBoundsRect(float x, float y, float w, float h) { - setScrollBoundsRect(x, y, w, h, false); - } - - /** - * Specifies scroll bounds as a bounding rectangle. - * - * @param x Smallest X value (usually 0). - * @param y Smallest Y value (usually 0). - * @param w Largest X extent (usually level width). - * @param h Largest Y extent (usually level height). - * @param updateWorld Reserved for future use (quad-tree bounds). - */ - public void setScrollBoundsRect(float x, float y, float w, float h, boolean updateWorld) { - minScrollX = x; - maxScrollX = x + w; - minScrollY = y; - maxScrollY = y + h; - } - - /** - * Clamps the current {@link #scroll} to the configured scroll bounds. - * Called automatically each frame by {@link #update(float)}. - */ - public void updateScroll() { - float vw = getViewWidth(); - float vh = getViewHeight(); - if (minScrollX != null && scroll.x < minScrollX) { - scroll.x = minScrollX; - } - if (maxScrollX != null && scroll.x + vw > maxScrollX) { - scroll.x = maxScrollX - vw; - } - if (minScrollY != null && scroll.y < minScrollY) { - scroll.y = minScrollY; - } - if (maxScrollY != null && scroll.y + vh > maxScrollY) { - scroll.y = maxScrollY - vh; - } - } - - /** - * Clamps the given scroll position to the camera's min/max bounds, modifying it - * in-place. - * - * @param scrollPos The scroll position to restrict. - * @return The same vector, clamped within bounds. - */ - public Vector2 bindScrollPos(Vector2 scrollPos) { - float vw = getViewWidth(); - float vh = getViewHeight(); - if (minScrollX != null) { - scrollPos.x = Math.max(scrollPos.x, minScrollX); - } - if (maxScrollX != null) { - scrollPos.x = Math.min(scrollPos.x, maxScrollX - vw); - } - if (minScrollY != null) { - scrollPos.y = Math.max(scrollPos.y, minScrollY); - } - if (maxScrollY != null) { - scrollPos.y = Math.min(scrollPos.y, maxScrollY - vh); - } - return scrollPos; - } - - /** Flashes white for 1 second. */ - public void flash() { - flash(Color.WHITE, 1f, null, false); - } - - /** Flashes the given color for 1 second. */ - public void flash(Color color) { - flash(color, 1f, null, false); - } - - /** Flashes the given color for the specified duration. */ - public void flash(Color color, float duration) { - flash(color, duration, null, false); - } - - /** - * The screen is filled with this color and gradually returns to normal. - * - * @param color The color to flash. - * @param duration How long the flash takes to fade, in seconds. - * @param onComplete Callback invoked when the flash finishes, or {@code null}. - * @param force If {@code true}, resets any currently-running flash. - */ - public void flash(Color color, float duration, Runnable onComplete, boolean force) { - if (flashActive && !force) { - return; - } - flashActive = true; - flashColor.set(color); - flashDuration = Math.max(duration, 0.001f); - flashElapsed = 0f; - flashAlpha = 1f; - flashOnComplete = onComplete; - } - - private void updateFlash(float elapsed) { - if (!flashActive) { - return; - } - flashElapsed += elapsed; - flashAlpha = 1f - (flashElapsed / flashDuration); - if (flashAlpha <= 0f) { - flashAlpha = 0f; - flashActive = false; - if (flashOnComplete != null) { - flashOnComplete.run(); - } - } - } - - /** Fades to black over 1 second. */ - public void fade() { - fade(Color.BLACK, 1f, false, null, false); - } - - /** Fades to the given color over 1 second. */ - public void fade(Color color) { - fade(color, 1f, false, null, false); - } - - /** Fades to the given color over the specified duration. */ - public void fade(Color color, float duration) { - fade(color, duration, false, null, false); - } - - /** Fades to/from the given color. */ - public void fade(Color color, float duration, boolean fadeIn) { - fade(color, duration, fadeIn, null, false); - } - - /** - * The screen is gradually filled with (or cleared of) this color. - * - * @param color The color to fade to/from. - * @param duration How long the fade takes, in seconds. - * @param fadeIn {@code true} = fade FROM the color to clear. {@code false} - * = fade TO the color. - * @param onComplete Callback invoked when the fade finishes, or {@code null}. - * @param force If {@code true}, resets any currently-running fade. - */ - public void fade(Color color, float duration, boolean fadeIn, Runnable onComplete, boolean force) { - if (fadeActive && !force) { - return; - } - fadeActive = true; - fadeColor.set(color); - fadeDuration = Math.max(duration, 0.001f); - fadeElapsed = 0f; - this.fadeIn = fadeIn; - fadeAlpha = fadeIn ? 1f : 0f; - fadeOnComplete = onComplete; - } - - private void updateFade(float elapsed) { - if (!fadeActive) { - return; - } - fadeElapsed += elapsed; - float progress = fadeElapsed / fadeDuration; - fadeAlpha = fadeIn ? (1f - progress) : progress; - if (progress >= 1f) { - fadeAlpha = fadeIn ? 0f : 1f; - fadeActive = false; - if (fadeOnComplete != null) { - fadeOnComplete.run(); - } - } - } - - /** Shakes with default intensity (0.05) for 0.5 seconds on both axes. */ - public void shake() { - shake(0.05f, 0.5f, null, true, FlxAxes.XY); - } - - /** Shakes with the given intensity for 0.5 seconds on both axes. */ - public void shake(float intensity) { - shake(intensity, 0.5f, null, true, FlxAxes.XY); - } - - /** Shakes with the given intensity and duration on both axes. */ - public void shake(float intensity, float duration) { - shake(intensity, duration, null, true, FlxAxes.XY); - } - - /** - * A simple screen-shake effect. - * - * @param intensity Fraction of camera size representing the max shake - * distance. - * @param duration How long the shake lasts, in seconds. - * @param onComplete Callback invoked when the shake finishes, or {@code null}. - * @param force If {@code true}, resets any currently-running shake - * (default unlike flash/fade). - * @param axes Which axes to shake on. - */ - public void shake(float intensity, float duration, Runnable onComplete, boolean force, FlxAxes axes) { - if (shakeActive && !force) { - return; - } - shakeActive = true; - shakeIntensity = intensity; - shakeDuration = Math.max(duration, 0.001f); - shakeElapsed = 0f; - shakeAxes = (axes != null) ? axes : FlxAxes.XY; - shakeOnComplete = onComplete; - shakeOffsetX = 0f; - shakeOffsetY = 0f; - } - - private void updateShake(float elapsed) { - if (!shakeActive) { - return; - } - shakeElapsed += elapsed; - if (shakeElapsed >= shakeDuration) { - shakeActive = false; - shakeOffsetX = 0f; - shakeOffsetY = 0f; - if (shakeOnComplete != null) { - shakeOnComplete.run(); - } - return; - } - - float sx = (shakeAxes == FlxAxes.Y) ? 0 : (MathUtils.random(-1f, 1f) * shakeIntensity * width); - float sy = (shakeAxes == FlxAxes.X) ? 0 : (MathUtils.random(-1f, 1f) * shakeIntensity * height); - - boolean pp = pixelPerfectShake || pixelPerfectRender; - if (pp) { - sx = Math.round(sx); - sy = Math.round(sy); - } - - shakeOffsetX = sx; - shakeOffsetY = sy; - } - - /** Stops all screen effects (flash, fade, shake) on this camera. */ - public void stopFX() { - stopFlash(); - stopFade(); - stopShake(); - } - - /** Stops the flash effect on this camera. */ - public void stopFlash() { - flashActive = false; - flashAlpha = 0f; - } - - /** Stops the fade effect on this camera. */ - public void stopFade() { - fadeActive = false; - fadeAlpha = 0f; - } - - /** Stops the shake effect on this camera. */ - public void stopShake() { - shakeActive = false; - shakeOffsetX = 0f; - shakeOffsetY = 0f; - } - - /** - * Fills the camera display with the specified color using the given batch and a - * 1x1 white texture. - * - * @param fillColor The color to fill with (alpha channel is respected). - * @param blendAlpha Whether to blend the alpha or overwrite previous contents. - * @param fxAlpha Additional alpha multiplier (0.0 to 1.0). - * @param batch An active {@link Batch} to draw with (must be between - * begin/end). - * @param whitePixel A 1x1 white {@link Texture} used for color drawing. - */ - public void fill(Color fillColor, boolean blendAlpha, float fxAlpha, Batch batch, Texture whitePixel) { - float a = blendAlpha ? fillColor.a * fxAlpha : fxAlpha; - batch.setColor(fillColor.r, fillColor.g, fillColor.b, a); - batch.draw(whitePixel, scroll.x, scroll.y, getViewWidth(), getViewHeight()); - batch.setColor(Color.WHITE); - } - - /** - * Draws active screen effects (flash and fade overlays) using the given batch. - * Call this after drawing all game objects but before {@code batch.end()}. - * - * @param batch An active {@link Batch} (must be between begin/end). - * @param whitePixel A 1x1 white {@link Texture} used for color drawing. - */ - public void drawFX(Batch batch, Texture whitePixel) { - if (flashActive && flashAlpha > 0f) { - batch.setColor(flashColor.r, flashColor.g, flashColor.b, flashAlpha * alpha); - batch.draw(whitePixel, scroll.x, scroll.y, getViewWidth(), getViewHeight()); - } - if (fadeActive || fadeAlpha > 0f) { - batch.setColor(fadeColor.r, fadeColor.g, fadeColor.b, fadeAlpha * alpha); - batch.draw(whitePixel, scroll.x, scroll.y, getViewWidth(), getViewHeight()); - } - batch.setColor(Color.WHITE); - } - - /** - * Checks whether this camera's display area contains the given point (screen - * coordinates). - * - * @param point The point to test. - * @return {@code true} if the point is inside the camera display. - */ - public boolean containsPoint(Vector2 point) { - return containsPoint(point, 0, 0); - } - - /** - * Checks whether this camera's display area overlaps a rectangle at the given - * point. - * - * @param point Top-left corner of the rectangle in screen coordinates. - * @param width Width of the rectangle. - * @param height Height of the rectangle. - * @return {@code true} if any part of the rectangle overlaps the camera - * display. - */ - public boolean containsPoint(Vector2 point, float width, float height) { - return point.x + width > x - && point.x < x + this.width - && point.y + height > y - && point.y < y + this.height; - } - - /** - * Checks whether this camera's display area overlaps the given rectangle - * (screen coordinates). - * - * @param rect The rectangle to test. - * @return {@code true} if the rectangle overlaps the camera display. - */ - public boolean containsRect(Rectangle rect) { - return containsPoint(tmpVec.set(rect.x, rect.y), rect.width, rect.height); - } - - /** The width of the visible area in world-space, accounting for zoom. */ - public float getViewWidth() { - return width / zoom; - } - - /** The height of the visible area in world-space, accounting for zoom. */ - public float getViewHeight() { - return height / zoom; - } - - /** The left edge of the visible area in world-space. */ - public float getViewX() { - return scroll.x + getViewMarginX(); - } - - /** The top edge of the visible area in world-space. */ - public float getViewY() { - return scroll.y + getViewMarginY(); - } - - /** Alias for {@link #getViewX()}. */ - public float getViewLeft() { - return getViewX(); - } - - /** Alias for {@link #getViewY()}. */ - public float getViewTop() { - return getViewY(); - } - - /** The right edge of the visible area in world-space. */ - public float getViewRight() { - return getViewX() + getViewWidth(); - } - - /** The bottom edge of the visible area in world-space. */ - public float getViewBottom() { - return getViewY() + getViewHeight(); - } - - /** Margin cut off on each side horizontally by zoom, in world-space. */ - public float getViewMarginX() { - return (width - getViewWidth()) / 2f; - } - - /** Margin cut off on each side vertically by zoom, in world-space. */ - public float getViewMarginY() { - return (height - getViewHeight()) / 2f; - } - - /** Margin cut off on the left by zoom. */ - public float getViewMarginLeft() { - return getViewMarginX(); - } - - /** Margin cut off on the right by zoom. */ - public float getViewMarginRight() { - return getViewMarginX(); - } - - /** Margin cut off on the top by zoom. */ - public float getViewMarginTop() { - return getViewMarginY(); - } - - /** Margin cut off on the bottom by zoom. */ - public float getViewMarginBottom() { - return getViewMarginY(); - } - - /** - * Returns a {@link Rectangle} describing the view margins (position = margin offsets, size = visible area). - * - * @return A new rectangle with the margin bounds. - */ - public Rectangle getViewMarginRect() { - return tmpRect.set(getViewMarginLeft(), getViewMarginTop(), getViewWidth(), getViewHeight()); - } - - /** - * Returns the current zoom level. {@code 1} = 1:1, {@code 2} = 2x magnification. - * Changing zoom affects all view properties ({@link #getViewWidth()}, etc.). - */ - public float getZoom() { - return zoom; - } - - /** - * Sets the zoom level. {@code 1} = 1:1, {@code 2} = 2x magnification (world appears larger). - * Cameras always zoom toward their center. - * - * @param zoom The new zoom level. - */ - public void setZoom(float zoom) { - float oldZoom = this.zoom; - this.zoom = zoom; - // Keep the center of the view fixed in world space so zoom happens from center, not from the left edge. - float centerX = scroll.x + width / (2f * oldZoom); - float centerY = scroll.y + height / (2f * oldZoom); - scroll.x = centerX - width / (2f * this.zoom); - scroll.y = centerY - height / (2f * this.zoom); - applyZoom(); - } - - /** - * Changes the zoom level by the given amount. - * - * @param zoom The amount to change the zoom level by. - */ - public void changeZoom(float zoom) { - setZoom(getZoom() + zoom); - } - - private void applyZoom() { - if (camera instanceof OrthographicCamera ortho) { - ortho.zoom = 1f / zoom; - } - } - - /** The horizontal scale factor derived from zoom. */ - public float getScaleX() { - return zoom; - } - - /** The vertical scale factor derived from zoom. */ - public float getScaleY() { - return zoom; - } - - /** - * Product of the camera's {@link #getScaleX()} and the game's scale mode. - * For a default setup this equals {@link #getScaleX()}. - */ - public float getTotalScaleX() { - return getScaleX(); - } - - /** - * Product of the camera's {@link #getScaleY()} and the game's scale mode. - * For a default setup this equals {@link #getScaleY()}. - */ - public float getTotalScaleY() { - return getScaleY(); - } - - /** - * Sets the display position of this camera. - * - * @param x The new X display position. - * @param y The new Y display position. - */ - public void setPosition(float x, float y) { - this.x = x; - this.y = y; - } - - /** - * Sets the zoom-based scale of this camera. Because cameras use a single zoom - * value, this - * sets zoom to the average of {@code scaleX} and {@code scaleY}. - * - * @param scaleX The desired horizontal scale. - * @param scaleY The desired vertical scale. - */ - public void setScale(float scaleX, float scaleY) { - setZoom((scaleX + scaleY) / 2f); - } - - /** - * Sets both {@link #width} and {@link #height} of the camera display. - * - * @param width The new width in game pixels. - * @param height The new height in game pixels. - */ - public void setSize(int width, int height) { - this.width = width; - this.height = height; - } - - /** - * Copies the bounds, follow target, deadzone info, and scroll from another - * camera. - * - * @param other The camera to copy from. - * @return This camera for chaining. - */ - public FlixelCamera copyFrom(FlixelCamera other) { - x = other.x; - y = other.y; - width = other.width; - height = other.height; - scroll.set(other.scroll); - - target = other.target; - targetOffset.set(other.targetOffset); - followLead.set(other.followLead); - followLerp = other.followLerp; - style = other.style; - deadzone = (other.deadzone != null) ? new Rectangle(other.deadzone) : null; - - minScrollX = other.minScrollX; - maxScrollX = other.maxScrollX; - minScrollY = other.minScrollY; - maxScrollY = other.maxScrollY; - - setZoom(other.zoom); - return this; - } - - /** - * Called by the game front end on window resize. Triggers repositioning of - * internal display - * objects. - */ - public void onResize() { - viewport.update( - Flixel.getWindowWidth(), - Flixel.getWindowHeight(), - true - ); - } - - /** - * Cleans up this camera's state, stopping all effects and clearing the follow - * target. - */ - @Override - public void destroy() { - stopFX(); - target = null; - deadzone = null; - flashOnComplete = null; - fadeOnComplete = null; - shakeOnComplete = null; - super.destroy(); - } - - public boolean isFlashActive() { - return flashActive; - } - - public boolean isFadeActive() { - return fadeActive; - } - - public boolean isShakeActive() { - return shakeActive; - } - - public Color getFlashColor() { - return flashColor; - } - - public float getFlashAlpha() { - return flashAlpha; - } - - public Color getFadeColor() { - return fadeColor; - } - - public float getFadeAlpha() { - return fadeAlpha; - } - - /** - * Preset dead zone styles used with - * {@link #follow(FlixelObject, FollowStyle, float)}. - */ - public enum FollowStyle { - - /** - * Camera follows the target and keeps it centered on the screen (with no dead zone). - * The camera snaps to the target's position each frame. - */ - LOCKON, - - /** - * A horizontally-biased dead zone placed near the bottom of the camera. - * Useful for platformers to show more of what is ahead and to prevent - * the camera from moving up and down too frequently. - */ - PLATFORMER, - - /** - * The dead zone is centered, allowing free camera movement in all directions, - * commonly used in top-down games. - */ - TOPDOWN, - - /** - * Like TOPDOWN but with a tighter (smaller) dead zone, so the camera - * follows the target more closely. - */ - TOPDOWN_TIGHT, - - /** - * The camera moves in whole-screen increments, or "pages", jumping - * once the target leaves the current screen. Good for classic puzzle or - * arcade games with discrete screen segments. - */ - SCREEN_BY_SCREEN, - - /** - * No dead zone, the camera only follows the target when explicitly moved; - * the camera does not track the target automatically. - */ - NO_DEAD_ZONE - } - - /** Axes on which an effect (e.g. shake) can operate. */ - public enum FlxAxes { - X, Y, XY - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelState.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelState.java deleted file mode 100644 index 57229b6..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelState.java +++ /dev/null @@ -1,199 +0,0 @@ -package me.stringdotjar.flixelgdx.display; - -import com.badlogic.gdx.Screen; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.g2d.Batch; - -import me.stringdotjar.flixelgdx.FlixelBasic; -import me.stringdotjar.flixelgdx.group.FlixelGroup; - -/** - * Base class for creating a better screen display with more functionality than the default {@link - * com.badlogic.gdx.Screen} interface. - * - *

A {@code FlixelState} can open a {@link FlixelSubState} on top of itself. - * By default, when a substate is active the parent state will continue to be drawn - * ({@link #persistentDraw} = {@code true}) but will stop updating - * ({@link #persistentUpdate} = {@code false}). - * - * @see FlxState (HaxeFlixel) - */ -public abstract class FlixelState extends FlixelGroup implements Screen { - - /** Should {@code this} state update its logic even when a substate is currently opened? */ - public boolean persistentUpdate = false; - - /** Should {@code this} state draw its members even when a substate is currently opened? */ - public boolean persistentDraw = true; - - /** - * If substates get destroyed when they are closed. Setting this to {@code false} might - * reduce state creation time, at the cost of greater memory usage. - */ - public boolean destroySubStates = true; - - /** The background color of {@code this} current state. */ - protected Color bgColor; - - /** The currently active substate opened on top of {@code this} state. */ - private FlixelSubState subState; - - public FlixelState() { - super(0); - } - - @Override - public final void show() {} - - @Override - public final void render(float delta) {} - - /** - * Called when the state is first created. This is where you want to assign your - * sprites and setup everything your state uses! - * - *

Make sure to override this, NOT the constructor! - */ - public void create() {} - - /** - * Updates the logic of {@code this} state. - * - * @param delta The amount of time that occurred since the last frame. - */ - public void update(float delta) {} - - /** - * Draws {@code this} state's members onto the screen. - * - * @param batch The batch that's used to draw {@code this} state's members. - */ - public void draw(Batch batch) {} - - /** - * Opens a {@link FlixelSubState} on top of {@code this} state. If there is already - * an active substate, it will be closed first. - * - * @param subState The substate to open. - */ - public void openSubState(FlixelSubState subState) { - if (subState == null) { - return; - } - if (this.subState == subState) { - return; - } - if (this.subState != null) { - closeSubState(); - } - - this.subState = subState; - subState.parentState = this; - subState.create(); - - if (subState.openCallback != null) { - subState.openCallback.run(); - } - } - - /** - * Closes the currently active substate, if one exists. - */ - public void closeSubState() { - if (subState == null) { - return; - } - FlixelSubState closing = subState; - subState = null; - closing.parentState = null; - - if (closing.closeCallback != null) { - closing.closeCallback.run(); - } - if (destroySubStates) { - closing.dispose(); - } - } - - /** - * Reloads the current substate's parent reference. Called internally after state - * transitions to ensure the parent link is correct. - */ - public void resetSubState() { - if (subState != null) { - subState.parentState = this; - } - } - - /** - * Called from {@link me.stringdotjar.flixelgdx.Flixel#switchState(FlixelState)} before - * the actual state switch happens. Override this to play an exit animation or transition, - * then call {@code onOutroComplete} when finished. - * - *

The default implementation calls {@code onOutroComplete} immediately. - * - * @param onOutroComplete Callback to invoke when the outro is complete. - */ - public void startOutro(Runnable onOutroComplete) { - if (onOutroComplete != null) { - onOutroComplete.run(); - } - } - - @Override - public void resize(int width, int height) {} - - @Override - public void pause() {} - - @Override - public void resume() {} - - @Override - public void hide() {} - - /** - * Disposes {@code this} state, any active substate, and all members. Called automatically - * when {@link me.stringdotjar.flixelgdx.Flixel#switchState(FlixelState)} is used, so that - * sprites and other objects release their resources. - */ - @Override - public void dispose() { - if (subState != null) { - closeSubState(); - } - if (members == null) { - return; - } - Object[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelBasic obj = (FlixelBasic) items[i]; - if (obj != null) { - obj.destroy(); - } - } - members.end(); - members.clear(); - } - - /** - * Adds a new object to {@code this} state. If it is {@code null}, it will not be added and - * simply ignored. - * - * @param object The object to add to the state. - */ - public void add(FlixelBasic object) { - if (object != null) { - members.add(object); - } - } - - /** Returns the currently active substate, or {@code null} if none is open. */ - public FlixelSubState getSubState() { - return subState; - } - - public Color getBgColor() { - return (bgColor != null) ? bgColor : Color.BLACK; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelSubState.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelSubState.java deleted file mode 100644 index 7ee8515..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/display/FlixelSubState.java +++ /dev/null @@ -1,53 +0,0 @@ -package me.stringdotjar.flixelgdx.display; - -import com.badlogic.gdx.graphics.Color; - -/** - * A {@code FlixelSubState} can be opened inside of a {@link FlixelState}. By default, it - * stops the parent state from updating, making it convenient for pause screens or menus. - * - *

The parent state's {@link FlixelState#persistentUpdate} and - * {@link FlixelState#persistentDraw} flags control whether it continues to update and - * draw while this substate is active. - * - *

Substates can be nested: a substate can open another substate on top of itself. - * - * @see FlxSubState (HaxeFlixel) - */ -public abstract class FlixelSubState extends FlixelState { - - /** Called when this substate is closed. */ - public Runnable closeCallback; - - /** Called when this substate is opened or resumed. */ - public Runnable openCallback; - - /** The parent state that opened this substate. Set internally by {@link FlixelState#openSubState}. */ - FlixelState parentState; - - /** - * Creates a new substate with a clear background. - */ - public FlixelSubState() { - this(Color.CLEAR); - } - - /** - * Creates a new substate with the given background color. - * - * @param bgColor The background color for this substate. - */ - public FlixelSubState(Color bgColor) { - super(); - this.bgColor = bgColor; - } - - /** - * Closes this substate by telling the parent state to remove it. - */ - public void close() { - if (parentState != null) { - parentState.closeSubState(); - } - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroup.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroup.java deleted file mode 100644 index da62146..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroup.java +++ /dev/null @@ -1,117 +0,0 @@ -package me.stringdotjar.flixelgdx.group; - -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.utils.SnapshotArray; - -import me.stringdotjar.flixelgdx.FlixelBasic; - -import java.util.function.Consumer; - -/** - * Base class for creating groups with a list of members inside of it. - */ -public abstract class FlixelGroup extends FlixelBasic implements FlixelGroupable { - - /** - * The list of members that {@code this} group contains. - */ - protected SnapshotArray members; - - /** - * Maximum number of members allowed. When {@code 0}, the group can grow without limit (default). - * When {@code > 0}, {@link #add} will not add if at capacity. - */ - protected int maxSize = 0; - - /** - * Creates a new FlixelGroup with no maximum size. - */ - public FlixelGroup() { - this(0); - } - - /** - * Creates a new FlixelGroup with the given maximum size. - * - * @param maxSize Maximum number of members allowed. When {@code 0}, the group can grow without limit (default). - * When {@code > 0}, {@link #add} will not add if at capacity. - */ - public FlixelGroup(int maxSize) { - this.maxSize = Math.max(0, maxSize); - members = new SnapshotArray<>(FlixelBasic[]::new); - } - - @Override - public void add(T member) { - members.add(member); - } - - @Override - public void update(float elapsed) { - FlixelBasic[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelBasic member = items[i]; - if (member == null) { - continue; - } - member.update(elapsed); - } - members.end(); - } - - @Override - public void draw(Batch batch) { - FlixelBasic[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelBasic member = items[i]; - if (member == null) { - continue; - } - member.draw(batch); - } - members.end(); - } - - @Override - public void remove(T member) { - members.removeValue(member, true); - } - - @Override - public void destroy() { - members.forEach(FlixelBasic::destroy); - members.clear(); - } - - @Override - public void clear() { - members.clear(); - } - - public void forEachMember(Consumer callback) { - FlixelBasic[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelBasic member = items[i]; - if (member == null) { - continue; - } - callback.accept(member); - } - members.end(); - } - - public void forEachMemberType(Class type, Consumer callback) { - FlixelBasic[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelBasic member = items[i]; - if (type.isInstance(member)) { - callback.accept(type.cast(member)); - } - } - members.end(); - } - - public SnapshotArray getMembers() { - return members; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroupable.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroupable.java deleted file mode 100644 index 119341b..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelGroupable.java +++ /dev/null @@ -1,10 +0,0 @@ -package me.stringdotjar.flixelgdx.group; - -/** - * Interface for creating new groups with members inside of them. - */ -public interface FlixelGroupable { - void add(T member); - void remove(T member); - void clear(); -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelSpriteGroup.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelSpriteGroup.java deleted file mode 100644 index a5838c8..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/group/FlixelSpriteGroup.java +++ /dev/null @@ -1,742 +0,0 @@ -package me.stringdotjar.flixelgdx.group; - -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.math.MathUtils; -import com.badlogic.gdx.math.Rectangle; -import com.badlogic.gdx.math.Vector2; -import com.badlogic.gdx.utils.SnapshotArray; - -import me.stringdotjar.flixelgdx.FlixelSprite; - -import java.util.Comparator; -import java.util.Random; -import java.util.function.Consumer; -import java.util.function.Predicate; - -/** - * A special {@link FlixelSprite} that can be treated like a single sprite even when - * made up of several member sprites. It implements the {@link FlixelGroupable} API - * for managing members while inheriting all sprite properties from {@link FlixelSprite}. - *

- * Because {@code FlixelSpriteGroup} extends {@code FlixelSprite}, groups can be nested - * inside other groups, enabling complex hierarchical sprite compositions. Any property - * change on the group (position, alpha, color, scale, rotation, flip) automatically - * propagates to all members. - *

- * Sprites added to the group are automatically offset by the group's current position - * and have their alpha multiplied by the group's alpha. When a sprite is removed, its - * position offset is subtracted to restore local coordinates. - *

- * Rotation behavior is controlled by {@link RotationMode}: - *

    - *
  • {@link RotationMode#INDIVIDUAL} (default) – the rotation delta is applied - * to each sprite's own rotation; no positional changes occur.
  • - *
  • {@link RotationMode#WHEEL} – sprites are arranged radially around the center - * like a wheel, repositioned absolutely each frame in {@link #update(float)}.
  • - *
  • {@link RotationMode#ORBIT} – sprites orbit around the group origin as a rigid body; - * both position and individual rotation are adjusted by the delta.
  • - *
- */ -public class FlixelSpriteGroup extends FlixelSprite implements FlixelGroupable { - - /** The members belonging to this group. */ - protected final SnapshotArray members; - - /** Maximum members allowed. When {@code 0}, the group can grow without limit. */ - protected int maxSize; - - /** Distance of each sprite from the center when using {@link RotationMode#WHEEL}. */ - private float rotationRadius; - - private static final Random RANDOM = new Random(); - - /** Reusable rectangle for internal calculations that would otherwise allocate. */ - private final Rectangle tmpBoundsRect = new Rectangle(); - - private RotationMode rotationMode = RotationMode.INDIVIDUAL; - private boolean visible = true; - - /** Creates a new group with unlimited size, a default rotation radius of 100, and 0 rotation. */ - public FlixelSpriteGroup() { - this(0, 100f, 0f); - } - - /** - * Creates a new group with the given maximum size. - * - * @param maxSize the maximum number of members allowed; 0 for unlimited. - */ - public FlixelSpriteGroup(int maxSize) { - this(maxSize, 100f, 0f); - } - - /** - * Creates a new group with the given parameters. - * - * @param maxSize The maximum number of members allowed; 0 for unlimited. - * @param rotationRadius The distance of each sprite from the center in {@link RotationMode#WHEEL}. - * @param rotation The initial rotation in degrees. - */ - public FlixelSpriteGroup(int maxSize, float rotationRadius, float rotation) { - super(); - this.maxSize = Math.max(0, maxSize); - this.rotationRadius = rotationRadius; - super.setAngle(rotation); - members = new SnapshotArray<>(FlixelSprite[]::new); - } - - @Override - public void setX(float x) { - float dx = x - getX(); - super.setX(x); - if (rotationMode != RotationMode.WHEEL) { - transformMembersX(dx); - } - } - - @Override - public void setY(float y) { - float dy = y - getY(); - super.setY(y); - if (rotationMode != RotationMode.WHEEL) { - transformMembersY(dy); - } - } - - @Override - public void setPosition(float x, float y) { - float dx = x - getX(); - float dy = y - getY(); - super.setPosition(x, y); - if (rotationMode != RotationMode.WHEEL) { - transformMembersPosition(dx, dy); - } - } - - @Override - public FlixelSprite loadGraphic(Texture texture, int frameWidth, int frameHeight) { - throw new UnsupportedOperationException("Loading a texture for a group is not supported. Use add() instead."); - } - - /** - * Sets the group's rotation in degrees. The behavior depends on the current {@link RotationMode}: - *
    - *
  • {@link RotationMode#INDIVIDUAL} – the delta is applied to each sprite's - * own rotation.
  • - *
  • {@link RotationMode#WHEEL} – the value is stored and applied during the - * next {@link #update(float)} call.
  • - *
  • {@link RotationMode#ORBIT} – each sprite's position is rotated around the group - * origin by the delta, and its individual rotation is adjusted by the same amount.
  • - *
- * - * @param degrees the new rotation in degrees. - */ - @Override - public void setAngle(float degrees) { - float delta = degrees - getAngle(); - super.setAngle(degrees); - - switch (rotationMode) { - case INDIVIDUAL: - transformMembersIndividualRotation(delta); - break; - case ORBIT: - orbitMembersAroundCenter(delta); - break; - case WHEEL: - break; - } - } - - public RotationMode getRotationMode() { - return rotationMode; - } - - public void setRotationMode(RotationMode rotationMode) { - this.rotationMode = rotationMode; - } - - public float getRotationRadius() { - return rotationRadius; - } - - public void setRotationRadius(float rotationRadius) { - this.rotationRadius = rotationRadius; - } - - /** - * Sets the opacity of the group and all of its current members. Members added later via - * {@link #add(FlixelSprite)} will have their alpha multiplied by this value rather than - * overwritten. - * - * @param a Alpha between 0 (fully transparent) and 1 (fully opaque). - */ - @Override - public void setAlpha(float a) { - super.setAlpha(a); - forEach(s -> s.setAlpha(a)); - } - - public float getAlpha() { - return getColor().a; - } - - /** Sets a color tint on the group and propagates it to all current members. */ - @Override - public void setColor(Color tint) { - super.setColor(tint); - forEach(s -> s.setColor(tint)); - } - - /** Sets a color tint on the group and propagates it to all current members. */ - @Override - public void setColor(float r, float g, float b, float a) { - super.setColor(r, g, b, a); - forEach(s -> s.setColor(r, g, b, a)); - } - - /** Sets a uniform scale on the group and propagates it to all current members. */ - @Override - public void setScale(float scaleXY) { - super.setScale(scaleXY); - forEach(s -> s.setScale(scaleXY)); - } - - /** Sets a non-uniform scale on the group and propagates it to all current members. */ - @Override - public void setScale(float scaleX, float scaleY) { - super.setScale(scaleX, scaleY); - forEach(s -> s.setScale(scaleX, scaleY)); - } - - /** Toggles the flip state on the X and/or Y axis for the group and all current members. */ - @Override - public void flip(boolean x, boolean y) { - super.flip(x, y); - forEach(s -> s.flip(x, y)); - } - - /** - * Sets the X-axis flip state on the group and all members to the desired value. - * Unlike {@link #flip(boolean, boolean)}, which toggles, this method ensures a - * specific state. - * - * @param flipX {@code true} to flip horizontally, {@code false} to un-flip. - */ - public void setFlipX(boolean flipX) { - if (isFlipX() != flipX) { - super.flip(true, false); - } - forEach(s -> { - if (s.isFlipX() != flipX) { - s.flip(true, false); - } - }); - } - - /** - * Sets the Y-axis flip state on the group and all members to the desired value. - * Unlike {@link #flip(boolean, boolean)}, which toggles, this method ensures a - * specific state. - * - * @param flipY {@code true} to flip vertically, {@code false} to un-flip. - */ - public void setFlipY(boolean flipY) { - if (isFlipY() != flipY) { - super.flip(false, true); - } - forEach(s -> { - if (s.isFlipY() != flipY) { - s.flip(false, true); - } - }); - } - - public boolean isVisible() { - return visible; - } - - public void setVisible(boolean visible) { - this.visible = visible; - } - - /** Sets the rotation and scale pivot point on every current member. */ - @Override - public void setOrigin(float originX, float originY) { - super.setOrigin(originX, originY); - forEach(s -> s.setOrigin(originX, originY)); - } - - /** Centers the origin on every current member based on each member's own dimensions. */ - @Override - public void setOriginCenter() { - super.setOriginCenter(); - forEach(FlixelSprite::setOriginCenter); - } - - /** - * Adds a sprite to the group. The sprite's position is automatically offset by the - * group's current position, and its alpha is multiplied by the group's alpha. If the - * group has a {@link #maxSize} and is already at capacity, the sprite is not added. - */ - @Override - public void add(FlixelSprite sprite) { - if (sprite == null) { - return; - } - if (maxSize > 0 && members.size >= maxSize) { - return; - } - preAdd(sprite); - members.add(sprite); - } - - /** Adds a sprite to the group and returns it, allowing for method chaining. */ - public FlixelSprite addAndReturn(FlixelSprite sprite) { - add(sprite); - return sprite; - } - - /** - * Inserts a sprite at the given index, offset by the group's current position. The - * index is clamped to the valid range {@code [0, length]}. - * - * @param index The index to insert the sprite at. - * @param sprite The sprite to insert. - */ - public void insert(int index, FlixelSprite sprite) { - if (sprite == null) { - return; - } - if (maxSize > 0 && members.size >= maxSize) { - return; - } - preAdd(sprite); - index = MathUtils.clamp(index, 0, members.size); - members.insert(index, sprite); - } - - /** - * Removes a sprite from the group. The group's position offset is subtracted from the - * sprite to restore it to local coordinates. - */ - @Override - public void remove(FlixelSprite sprite) { - if (sprite == null) { - return; - } - members.removeValue(sprite, true); - sprite.setX(sprite.getX() - getX()); - sprite.setY(sprite.getY() - getY()); - } - - /** - * Replaces an existing member with a new sprite. The new sprite is offset by the group's - * current position. If {@code oldSprite} is not found, {@code newSprite} is simply added - * to the end of the group instead. - * - * @param oldSprite The member to replace. - * @param newSprite The replacement sprite. - * @return The replacement sprite. - */ - public FlixelSprite replace(FlixelSprite oldSprite, FlixelSprite newSprite) { - if (oldSprite == null || newSprite == null) { - return newSprite; - } - int idx = members.indexOf(oldSprite, true); - if (idx < 0) { - add(newSprite); - return newSprite; - } - preAdd(newSprite); - members.set(idx, newSprite); - return newSprite; - } - - /** Destroys every member and then clears the group. */ - @Override - public void clear() { - forEach(FlixelSprite::destroy); - members.clear(); - } - - /** Returns the member at the given index, or {@code null} if the index is out of bounds. */ - public FlixelSprite get(int index) { - if (index < 0 || index >= members.size) { - return null; - } - return members.get(index); - } - - public int getLength() { - return members.size; - } - - /** Returns the number of non-null members, which may differ from {@link #getLength()}. */ - public int countMembers() { - int count = 0; - for (int i = 0, n = members.size; i < n; i++) { - if (members.get(i) != null) { - count++; - } - } - return count; - } - - public boolean isEmpty() { - return members.size == 0; - } - - public int getMaxSize() { - return maxSize; - } - - public void setMaxSize(int maxSize) { - this.maxSize = Math.max(0, maxSize); - } - - public SnapshotArray getMembers() { - return members; - } - - /** Returns a random member, or {@code null} if the group is empty. */ - public FlixelSprite getRandom() { - return getRandom(0, members.size); - } - - /** - * Returns a random member from the range [{@code startIndex}, {@code startIndex + length}). - * - * @param startIndex The first index (inclusive). - * @param length The number of elements to consider. If {@code <= 0}, the entire group is used. - * @return A random member from the range, or {@code null} if the range is empty. - */ - public FlixelSprite getRandom(int startIndex, int length) { - if (members.size == 0) { - return null; - } - startIndex = MathUtils.clamp(startIndex, 0, members.size - 1); - if (length <= 0) { - length = members.size; - } - int end = Math.min(startIndex + length, members.size); - int span = end - startIndex; - if (span <= 0) { - return null; - } - return members.get(startIndex + RANDOM.nextInt(span)); - } - - /** Applies a function to every non-null member in order. */ - public void forEach(Consumer function) { - if (function == null || members == null || members.size == 0) { - return; - } - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite s = members.get(i); - if (s != null) { - function.accept(s); - } - } - } - - /** - * Applies a function only to members that are instances of the given type. This is useful - * when a group contains a mix of {@link FlixelSprite} subclasses (including nested - * {@link FlixelSpriteGroup} instances). - * - * @param type The class to filter by. - * @param callback The function to apply to each matching member. - * @param The member subtype. - */ - public void forEachOfType(Class type, Consumer callback) { - if (type == null || callback == null) { - return; - } - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite s = members.get(i); - if (type.isInstance(s)) { - callback.accept(type.cast(s)); - } - } - } - - public void sort(Comparator comparator) { - if (comparator == null) { - return; - } - members.sort(comparator); - } - - /** Returns the first member that satisfies the predicate, or {@code null} if none match. */ - public FlixelSprite getFirstMatching(Predicate predicate) { - if (predicate == null) { - return null; - } - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite s = members.get(i); - if (s != null && predicate.test(s)) { - return s; - } - } - return null; - } - - public int indexOf(FlixelSprite sprite) { - return members.indexOf(sprite, true); - } - - public boolean contains(FlixelSprite sprite) { - return members.indexOf(sprite, true) >= 0; - } - - /** - * Moves a member to the end of the draw list so that it renders on top of all other - * members. Has no effect if the sprite is not a member of this group. - */ - public void bringToFront(FlixelSprite sprite) { - if (members.removeValue(sprite, true)) { - members.add(sprite); - } - } - - /** - * Moves a member to the beginning of the draw list so that it renders behind all other - * members. Has no effect if the sprite is not a member of this group. - */ - public void sendToBack(FlixelSprite sprite) { - if (members.removeValue(sprite, true)) { - members.insert(0, sprite); - } - } - - /** - * Swaps the draw order of two members by their indices. Out-of-bounds indices are - * silently ignored. - */ - public void swapMembers(int index1, int index2) { - if (index1 < 0 || index1 >= members.size || index2 < 0 || index2 >= members.size) { - return; - } - members.swap(index1, index2); - } - - /** - * Computes the axis-aligned bounding rectangle that encloses all members, taking each - * member's position, size, and scale into account. - * - * @param out An optional output rectangle. If {@code null}, a new one is created. - * @return The bounding rectangle. - */ - public Rectangle getBounds(Rectangle out) { - if (out == null) { - out = new Rectangle(); - } - if (members.size == 0) { - out.set(getX(), getY(), 0, 0); - return out; - } - - float minX = Float.MAX_VALUE; - float minY = Float.MAX_VALUE; - float maxX = -Float.MAX_VALUE; - float maxY = -Float.MAX_VALUE; - - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite s = members.get(i); - if (s == null) { - continue; - } - float sx = s.getX(); - float sy = s.getY(); - float sw = s.getWidth() * Math.abs(s.getScaleX()); - float sh = s.getHeight() * Math.abs(s.getScaleY()); - - if (sx < minX) minX = sx; - if (sy < minY) minY = sy; - if (sx + sw > maxX) maxX = sx + sw; - if (sy + sh > maxY) maxY = sy + sh; - } - - out.set(minX, minY, maxX - minX, maxY - minY); - return out; - } - - /** - * Returns the center point of the bounding rectangle that encompasses all members. - * - * @param out An optional output vector. If {@code null}, a new one is created. - * @return The midpoint of the group's bounds. - */ - public Vector2 getMidpoint(Vector2 out) { - if (out == null) { - out = new Vector2(); - } - Rectangle bounds = getBounds(tmpBoundsRect); - out.set(bounds.x + bounds.width / 2f, bounds.y + bounds.height / 2f); - return out; - } - - @Override - public void update(float delta) { - super.update(delta); - - FlixelSprite[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite member = items[i]; - if (member != null) { - member.update(delta); - } - } - members.end(); - - if (rotationMode == RotationMode.WHEEL) { - applyWheelRotation(); - } - } - - /** - * Draws all members in insertion order. The group itself does not render its own graphic; - * only its members are drawn. Nothing is rendered when {@link #isVisible()} is {@code false}. - */ - @Override - public void draw(Batch batch) { - if (!visible) { - return; - } - - FlixelSprite[] items = members.begin(); - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite member = items[i]; - if (member != null) { - member.draw(batch); - } - } - members.end(); - } - - @Override - public void destroy() { - forEach(FlixelSprite::destroy); - members.clear(); - super.destroy(); - } - - @Override - public void reset() { - forEach(FlixelSprite::reset); - members.clear(); - rotationMode = RotationMode.INDIVIDUAL; - rotationRadius = 100f; - visible = true; - super.reset(); - } - - /** - * Offsets a sprite by the group's current position and multiplies its alpha by the - * group's alpha before it is inserted into the members list. - */ - private void preAdd(FlixelSprite sprite) { - sprite.setX(sprite.getX() + getX()); - sprite.setY(sprite.getY() + getY()); - sprite.setAlpha(sprite.getColor().a * getColor().a); - } - - private void transformMembersX(float dx) { - forEach(s -> s.setX(s.getX() + dx)); - } - - private void transformMembersY(float dy) { - forEach(s -> s.setY(s.getY() + dy)); - } - - private void transformMembersPosition(float dx, float dy) { - forEach(s -> s.setPosition(s.getX() + dx, s.getY() + dy)); - } - - /** - * Applies the rotation delta to each sprite's own rotation without changing - * any positions. Used by {@link RotationMode#INDIVIDUAL}. - */ - private void transformMembersIndividualRotation(float delta) { - forEach(s -> s.setAngle(s.getAngle() + delta)); - } - - /** - * Rotates every member's position around the group origin by {@code angleDelta} degrees - * and adjusts each sprite's own rotation by the same amount. Used by - * {@link RotationMode#ORBIT}. - */ - private void orbitMembersAroundCenter(float angleDelta) { - float cos = MathUtils.cosDeg(angleDelta); - float sin = MathUtils.sinDeg(angleDelta); - - for (int i = 0, n = members.size; i < n; i++) { - FlixelSprite s = members.get(i); - if (s == null) { - continue; - } - - float localX = s.getX() - getX(); - float localY = s.getY() - getY(); - float rotatedX = localX * cos - localY * sin; - float rotatedY = localX * sin + localY * cos; - s.setPosition(getX() + rotatedX, getY() + rotatedY); - s.setAngle(s.getAngle() + angleDelta); - } - } - - /** - * Positions and rotates each sprite radially around the group center. Called every - * frame from {@link #update(float)} when using {@link RotationMode#WHEEL}. - */ - private void applyWheelRotation() { - int n = members.size; - if (n == 0) { - return; - } - - float angleStep = 360f / n; - for (int i = 0; i < n; i++) { - FlixelSprite s = members.get(i); - if (s == null) { - continue; - } - - float angleDeg = getAngle() + angleStep * i; - float px = getX() + rotationRadius * MathUtils.cosDeg(angleDeg); - float py = getY() + rotationRadius * MathUtils.sinDeg(angleDeg); - s.setPosition(px, py); - s.setAngle(angleDeg); - } - } - - /** Controls how a {@link FlixelSpriteGroup}'s rotation affects its members. */ - public enum RotationMode { - - /** - * Rotation delta is applied to each sprite's individual rotation. - * No positional changes occur. - */ - INDIVIDUAL, - - /** - * Sprites are arranged in a radial pattern around the group center. Each sprite is - * positioned at {@link FlixelSpriteGroup#rotationRadius} from the center, spaced - * evenly around 360 degrees. Positions and rotations are set absolutely each frame - * in {@link FlixelSpriteGroup#update(float)}. - *

- * Individual sprite rotations cannot be changed independently in this mode. - */ - WHEEL, - - /** - * All sprites orbit around the group origin as a rigid body. When the rotation - * changes, each sprite's position is rotated around the center by the delta, and - * its individual rotation is adjusted by the same amount. - */ - ORBIT - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java deleted file mode 100644 index 3b522fc..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java +++ /dev/null @@ -1,11 +0,0 @@ -package me.stringdotjar.flixelgdx.input; - -import com.badlogic.gdx.Input; - -/** - * A simple extension of {@link Input.Keys} to simplify accessing key constants. - */ -public class FlixelKey extends Input.Keys { - - private FlixelKey() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogLevel.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogLevel.java deleted file mode 100644 index f5d12b9..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogLevel.java +++ /dev/null @@ -1,25 +0,0 @@ -package me.stringdotjar.flixelgdx.logging; - -/** - * An enum that defines the log levels for FlixelGDX's logging system. This is used to determine the - * severity of a log message and how it should be displayed in the console. - */ -public enum FlixelLogLevel { - - /** - * Simple white/gray text and simple informational log level that is used for general information about the game. - */ - INFO, - - /** - * Highlighted yellow in the console and, although not critical, indicates that something may be - * wrong and should be looked into. - */ - WARN, - - /** - * Highlighted red in the console and indicates an error. Shows something is wrong and - * should be looked into immediately. - */ - ERROR -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogMode.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogMode.java deleted file mode 100644 index e01ba04..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogMode.java +++ /dev/null @@ -1,20 +0,0 @@ -package me.stringdotjar.flixelgdx.logging; - -/** - * An enum that defines the log modes for FlixelGDX's logging system. This is used to determine how the - * log messages should be displayed in the console. - */ -public enum FlixelLogMode { - - /** - * Provides a simple log output that only includes the location of the log call and the message. - * Great for people who are familiar with Haxe and its {@code trace()} function! - */ - SIMPLE, - - /** - * Provides a more detailed log output that includes the time of the log call, class, line number, and - * method of the call. Great for debugging purposes and people who like a more professional feel for their game! - */ - DETAILED -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java deleted file mode 100644 index 985ef80..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/logging/FlixelLogger.java +++ /dev/null @@ -1,226 +0,0 @@ -package me.stringdotjar.flixelgdx.logging; - -import com.badlogic.gdx.files.FileHandle; - -import me.stringdotjar.flixelgdx.util.FlixelConstants; -import me.stringdotjar.flixelgdx.util.FlixelRuntimeUtil; -import org.jetbrains.annotations.NotNull; - -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.function.Consumer; - -/** - * Logger instance for Flixel that formats and outputs log messages to the console and optionally - * to a file. Console output respects the current {@link FlixelLogMode}; file output always uses - * detailed format. - */ -public class FlixelLogger { - - /** Default tag to use when logging without a specific tag. */ - private String defaultTag = ""; - - /** Where the log file is written (for reference); actual writes go through the file line consumer. */ - private FileHandle logFileLocation; - - /** Log mode for console output. File output always uses {@link FlixelLogMode#DETAILED}. */ - private FlixelLogMode logMode; - - /** Callback for when a log message is written to the file. */ - private Consumer fileLineConsumer; - - /** - * Creates a logger that outputs to the console and a file. - * - * @param logMode The mode used for console output. - */ - public FlixelLogger(FlixelLogMode logMode) { - this(null, logMode, null); - } - - /** - * Creates a logger with an optional log file location and optional consumer for file lines. - * - * @param logFileLocation Where the log file is stored (which might be null). - * @param logMode The mode used for console output. - * @param fileLineConsumer Callback for when a log message is written to the file. - */ - public FlixelLogger(FileHandle logFileLocation, FlixelLogMode logMode, Consumer fileLineConsumer) { - this.logFileLocation = logFileLocation; - this.logMode = logMode != null ? logMode : FlixelLogMode.SIMPLE; - this.fileLineConsumer = fileLineConsumer; - } - - public FileHandle getLogFileLocation() { - return logFileLocation; - } - - public void setLogFileLocation(FileHandle logFileLocation) { - this.logFileLocation = logFileLocation; - } - - public FlixelLogMode getLogMode() { - return logMode; - } - - public void setLogMode(FlixelLogMode logMode) { - this.logMode = logMode != null ? logMode : FlixelLogMode.SIMPLE; - } - - public Consumer getFileLineConsumer() { - return fileLineConsumer; - } - - public void setFileLineConsumer(Consumer fileLineConsumer) { - this.fileLineConsumer = fileLineConsumer; - } - - public void info(Object message) { - info(defaultTag, message); - } - - public void info(String tag, Object message) { - outputLog(tag, message.toString(), FlixelLogLevel.INFO); - } - - public void warn(Object message) { - warn(defaultTag, message); - } - - public void warn(String tag, Object message) { - outputLog(tag, message.toString(), FlixelLogLevel.WARN); - } - - public void error(Object message) { - error(defaultTag, message, null); - } - - public void error(Object message, Throwable throwable) { - error(defaultTag, message, throwable); - } - - public void error(String tag, Object message) { - error(tag, message, null); - } - - public void error(String tag, Object message, Throwable throwable) { - String msg = (throwable != null) ? (message.toString() + " | Exception: " + throwable) : message.toString(); - outputLog(tag, msg, FlixelLogLevel.ERROR); - } - - /** - * Formats and outputs a log message to the console (according to {@link #logMode}) and, if a - * file line consumer is set, passes the detailed (plain) line for file output. - */ - protected void outputLog(String tag, Object message, FlixelLogLevel level) { - StackWalker.StackFrame caller = getCaller(); - - String file = "UnknownFile.java:0"; - String simpleFile = "UnknownFile.java:0"; - String method = "unknownMethod()"; - if (caller != null) { - file = caller.getFileName() + ":" + caller.getLineNumber(); - String className = caller.getClassName(); - int lastDot = className.lastIndexOf('.'); - String packagePath = (lastDot > 0) ? className.substring(0, lastDot).replace('.', '/') : ""; - simpleFile = packagePath.isEmpty() - ? caller.getFileName() + ":" + caller.getLineNumber() - : packagePath + "/" + caller.getFileName() + ":" + caller.getLineNumber(); - method = caller.getMethodName() + "()"; - } - - String rawMessage = (message != null) ? message.toString() : "null"; - String color = switch (level) { - case INFO -> FlixelConstants.AsciiCodes.WHITE; - case WARN -> FlixelConstants.AsciiCodes.YELLOW; - case ERROR -> FlixelConstants.AsciiCodes.RED; - }; - boolean underlineFile = (level == FlixelLogLevel.ERROR); - - // Console: use current log mode. - String coloredLog; - if (logMode == FlixelLogMode.SIMPLE) { - coloredLog = colorText(simpleFile + ":", color, true, false, underlineFile) - + " " - + colorText(rawMessage, color, false, true, false); - } else { - String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - String levelTag = "[" + level + "]"; - String tagPart = "[" + tag + "]"; - String filePart = "[" + file + "]"; - String methodPart = "[" + method + "]"; - coloredLog = colorText(timestamp + " ", color, false, false, false) - + colorText(levelTag + " ", color, true, false, false) - + colorText(tagPart + " ", color, true, false, false) - + colorText(filePart + " ", color, true, false, underlineFile) - + colorText(methodPart + " ", color, false, false, false) - + colorText(rawMessage, color, false, true, false); - } - System.out.println(coloredLog); - - // File: always detailed (plain, no ANSI). - if (fileLineConsumer != null) { - String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); - String levelTag = "[" + level + "]"; - String tagPart = "[" + tag + "]"; - String filePart = "[" + file + "]"; - String methodPart = "[" + method + "]"; - String plainLog = timestamp + " " + levelTag + " " + tagPart + " " + filePart + " " + methodPart + " " + rawMessage; - fileLineConsumer.accept(plainLog); - } - } - - /** - * Gets the location of where the log was called from. - * - *

This is used to get the file and method name of where the log was called from. - * - * @return The location of where the log was called from. - */ - protected StackWalker.StackFrame getCaller() { - // TODO: Make this work for TeaVM, as call stacks aren't supported for web builds/TeaVM! - return StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE) - .walk(frames -> frames.filter(f -> { - // Filter packages to not log. - String pkg = f.getDeclaringClass().getPackageName(); - if (pkg.startsWith(FlixelRuntimeUtil.getLibraryRoot())) return false; // Do not include FlixelGDX logs. - if (pkg.startsWith("org.codehaus.groovy.")) return false; - if (pkg.startsWith("groovy.lang.")) return false; - if (pkg.contains("$_run_closure")) return false; - if (pkg.contains("$$Lambda$")) return false; - if (pkg.startsWith("sun.reflect.") || pkg.startsWith("java.lang.reflect.")) return false; - return true; - }) - .findFirst() - ) - .orElse(null); - } - - /** - * Wraps text with ANSI color/format codes for console output. - */ - protected String colorText(String text, String color, boolean bold, boolean italic, boolean underline) { - StringBuilder sb = new StringBuilder(); - if (bold) { - sb.append(FlixelConstants.AsciiCodes.BOLD); - } - if (italic) { - sb.append(FlixelConstants.AsciiCodes.ITALIC); - } - if (underline) { - sb.append(FlixelConstants.AsciiCodes.UNDERLINE); - } - sb.append(color); - sb.append(text); - sb.append(FlixelConstants.AsciiCodes.RESET); - return sb.toString(); - } - - public String getDefaultTag() { - return defaultTag; - } - - public void setDefaultTag(@NotNull String defaultTag) { - this.defaultTag = defaultTag; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignal.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignal.java deleted file mode 100644 index 5fecf97..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignal.java +++ /dev/null @@ -1,95 +0,0 @@ -package me.stringdotjar.flixelgdx.signal; - -import com.badlogic.gdx.utils.SnapshotArray; - -/** - * Utility class for creating objects that can execute multiple callbacks when it is dispatched (or triggered). - */ -public class FlixelSignal { - - private final SnapshotArray> callbacks; - private final SnapshotArray> tempCallbacks; - - public FlixelSignal() { - callbacks = new SnapshotArray<>(); - tempCallbacks = new SnapshotArray<>(); - } - - /** - * Adds a new callback to {@code this} signal to be executed upon {@code dispatch()} being - * called. - * - * @param callback The new callback to add to the signal, with the type intended to be a Java {@code record}. - */ - public void add(SignalHandler callback) { - if (callback != null) { - callbacks.add(callback); - } - } - - /** - * Adds a temporary callback that only gets ran once. When {@code dispatch()} is - * executed, the temporary callback is removed. - * - * @param callback The new temporary callback to add, with the type intended to be a Java {@code record}. - */ - public void addOnce(SignalHandler callback) { - if (callback != null) { - tempCallbacks.add(callback); - } - } - - /** - * Removes a specific callback from {@code this} signal. - * - * @param callback The callback to remove. - */ - public void remove(SignalHandler callback) { - callbacks.removeValue(callback, true); - tempCallbacks.removeValue(callback, true); - } - - /** Removes all callbacks from {@code this} signal. */ - public void clear() { - callbacks.clear(); - tempCallbacks.clear(); - } - - /** Triggers {@code this} signal and executes all callbacks. */ - public void dispatch() { - dispatch(null); - } - - /** - * Triggers {@code this} signal and executes all callbacks. - * - * @param data The parameters that {@code this} signal takes. - */ - @SuppressWarnings("unchecked") - public void dispatch(T data) { - Object[] items = callbacks.begin(); - for (int i = 0, n = callbacks.size; i < n; i++) { - SignalHandler callback = (SignalHandler) items[i]; - if (callback != null) { - callback.execute(data); - } - } - callbacks.end(); - - if (tempCallbacks.size > 0) { - Object[] tempItems = tempCallbacks.begin(); - for (int i = 0, n = tempCallbacks.size; i < n; i++) { - SignalHandler callback = (SignalHandler) tempItems[i]; - if (callback != null) { - callback.execute(data); - } - } - tempCallbacks.end(); - tempCallbacks.clear(); - } - } - - public interface SignalHandler { - void execute(T data); - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java deleted file mode 100644 index 289c73b..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java +++ /dev/null @@ -1,48 +0,0 @@ -package me.stringdotjar.flixelgdx.signal; - -import games.rednblack.miniaudio.MASound; -import me.stringdotjar.flixelgdx.Flixel; -import me.stringdotjar.flixelgdx.display.FlixelState; - -/** - * Convenience class for holding all signal data types used in the default signals stored in - * the global {@link Flixel} manager class. - * - *

{@link UpdateSignalData} is a mutable, reusable class rather than a record because it is - * dispatched every frame. Allocating a new object 120 times per second (pre+post) adds GC - * pressure that causes frame stutters. Signal handlers must not hold a reference to the data - * object past the callback return. - */ -public final class FlixelSignalData { - - /** - * Mutable carrier for per-frame update data. Reuse the same instance across frames to - * avoid GC pressure. Do NOT store a reference to this object; read values during the - * callback only. - */ - public static final class UpdateSignalData { - private float delta; - - public UpdateSignalData() {} - - public UpdateSignalData(float delta) { - this.delta = delta; - } - - public float delta() { - return delta; - } - - public void set(float delta) { - this.delta = delta; - } - } - - public record StateSwitchSignalData(FlixelState screen) {} - - public record SoundPlayedSignalData(MASound sound) {} - - public record MusicPlayedSignalData(MASound music) {} - - private FlixelSignalData() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelFontRegistry.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelFontRegistry.java deleted file mode 100644 index cc0dceb..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelFontRegistry.java +++ /dev/null @@ -1,212 +0,0 @@ -package me.stringdotjar.flixelgdx.text; - -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; -import org.jetbrains.annotations.NotNull; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * A global registry for TrueType ({@code .ttf}/{@code .otf}) fonts that can be used - * with {@link FlixelText}. - * - *

Fonts are registered once with a unique string identifier and an asset path, then - * referenced by that identifier throughout the game. The registry caches - * {@link FreeTypeFontGenerator} instances internally so that multiple {@link FlixelText} - * objects sharing the same font ID reuse the same generator, avoiding redundant file parsing. - * - *

Usage

- *
{@code
- * // Register fonts at startup (e.g. in your FlixelState.create()):
- * FlixelFontRegistry.register("pixel", Gdx.files.internal("fonts/pixel.ttf"));
- * FlixelFontRegistry.register("ui",    Gdx.files.internal("fonts/opensans.ttf"));
- *
- * // Optionally set a global default so FlixelText objects use it automatically:
- * FlixelFontRegistry.setDefault("ui");
- *
- * // Use in FlixelText:
- * FlixelText title = new FlixelText(0, 0, 0, "Hello!", 32);
- * title.setFont("pixel");
- *
- * // Clean up when the game shuts down:
- * FlixelFontRegistry.dispose();
- * }
- * - *

Lifecycle

- *

{@link #dispose()} is called when the game shuts down (in - * {@code FlixelGame.dispose()}) to release all cached generators. Individual entries - * can be removed earlier with {@link #unregister(String)}. - */ -public final class FlixelFontRegistry { - - private static final Map entries = new HashMap<>(); - - /** The ID of the font that FlixelText uses when no explicit font is set. */ - private static String defaultFontId; - - private FlixelFontRegistry() {} - - /** - * Registers a TrueType font under the given identifier. If an entry with the same - * ID already exists, it is replaced (and its cached generator is disposed). - * - * @param id A unique identifier for this font (e.g. {@code "pixel"}, {@code "main"}, {@code "bold"}). - * @param fontFile A libGDX {@link FileHandle} pointing to the {@code .ttf} or {@code .otf} asset. - * @throws IllegalArgumentException if {@code id} is {@code null}/empty or {@code fontFile} is {@code null}. - */ - public static void register(@NotNull String id, @NotNull FileHandle fontFile) { - if (id.isEmpty()) { - throw new IllegalArgumentException("Font ID must not be empty."); - } - - Entry existing = entries.get(id); - if (existing != null) { - existing.dispose(); - } - entries.put(id, new Entry(fontFile)); - } - - /** - * Removes a previously registered font and disposes its cached generator. - * Does nothing if the ID is not registered. If the removed font was the - * {@linkplain #setDefault(String) default}, the default is cleared. - * - * @param id The font identifier to remove. - */ - public static void unregister(String id) { - Entry removed = entries.remove(id); - if (removed != null) { - removed.dispose(); - } - if (id != null && id.equals(defaultFontId)) { - defaultFontId = null; - } - } - - /** - * Returns whether a font with the given ID is registered. - * - * @param id The font identifier to check. - * @return {@code true} if the font is registered. - */ - public static boolean has(String id) { - return entries.containsKey(id); - } - - /** - * Returns the {@link FileHandle} for a registered font. - * - * @param id The font identifier. - * @return The font's file handle. - * @throws IllegalArgumentException if the ID is not registered. - */ - public static FileHandle getFile(String id) { - Entry entry = requireEntry(id); - return entry.fontFile; - } - - /** - * Returns the cached {@link FreeTypeFontGenerator} for a registered font, - * creating it lazily on first access. The generator is owned by the registry - * and must not be disposed by the caller. - * - * @param id The font identifier. - * @return The shared font generator. - * @throws IllegalArgumentException if the ID is not registered. - */ - public static FreeTypeFontGenerator getGenerator(String id) { - Entry entry = requireEntry(id); - return entry.getOrCreateGenerator(); - } - - /** - * Returns an unmodifiable view of all currently registered font IDs. - * - * @return A set of registered font identifiers. - */ - public static Set getRegisteredIds() { - return Collections.unmodifiableSet(entries.keySet()); - } - - /** - * Sets the global default font that {@link FlixelText} will use when no explicit - * font is set via {@link FlixelText#setFont(FileHandle)} or - * {@link FlixelText#setFont(String)}. Pass {@code null} to clear the default, - * which causes FlixelText to fall back to libGDX's built-in bitmap font. - * - * @param id The registered font ID to use as the default, or {@code null} to clear. - * @throws IllegalArgumentException if {@code id} is non-null but not registered. - */ - public static void setDefault(String id) { - if (id != null && !entries.containsKey(id)) { - throw new IllegalArgumentException("Font id \"" + id + "\" is not registered."); - } - defaultFontId = id; - } - - /** - * Returns the ID of the current default font, or {@code null} if none is set. - */ - public static String getDefault() { - return defaultFontId; - } - - /** - * Returns the {@link FreeTypeFontGenerator} for the default font, or {@code null} - * if no default is set. - */ - public static FreeTypeFontGenerator getDefaultGenerator() { - if (defaultFontId == null || !entries.containsKey(defaultFontId)) { - return null; - } - return entries.get(defaultFontId).getOrCreateGenerator(); - } - - /** - * Disposes all cached {@link FreeTypeFontGenerator} instances and clears the - * registry. This should be called when the game shuts down. - */ - public static void dispose() { - for (Entry entry : entries.values()) { - entry.dispose(); - } - entries.clear(); - defaultFontId = null; - } - - private static Entry requireEntry(String id) { - Entry entry = entries.get(id); - if (entry == null) { - throw new IllegalArgumentException("No font registered with id \"" + id + "\"."); - } - return entry; - } - - /** Holds the file handle and a lazily-created generator for a single registered font. */ - private static final class Entry { - - final FileHandle fontFile; - FreeTypeFontGenerator generator; - - Entry(FileHandle fontFile) { - this.fontFile = fontFile; - } - - FreeTypeFontGenerator getOrCreateGenerator() { - if (generator == null) { - generator = new FreeTypeFontGenerator(fontFile); - } - return generator; - } - - void dispose() { - if (generator != null) { - generator.dispose(); - generator = null; - } - } - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelText.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelText.java deleted file mode 100644 index 60eb4d1..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/text/FlixelText.java +++ /dev/null @@ -1,1133 +0,0 @@ -package me.stringdotjar.flixelgdx.text; - -import com.badlogic.gdx.files.FileHandle; -import com.badlogic.gdx.graphics.Color; -import com.badlogic.gdx.graphics.Texture; -import com.badlogic.gdx.graphics.g2d.Animation; -import com.badlogic.gdx.graphics.g2d.Batch; -import com.badlogic.gdx.graphics.g2d.BitmapFont; -import com.badlogic.gdx.graphics.g2d.GlyphLayout; -import com.badlogic.gdx.graphics.g2d.TextureAtlas; -import com.badlogic.gdx.graphics.g2d.TextureRegion; -import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator; -import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter; -import com.badlogic.gdx.math.Matrix4; -import com.badlogic.gdx.utils.Align; -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.XmlReader; - -import me.stringdotjar.flixelgdx.FlixelSprite; - -import com.badlogic.gdx.utils.ObjectMap; - -import org.jetbrains.annotations.NotNull; - -/** - * A display object for rendering text on screen. - * - *

Extends {@link FlixelSprite} so that text objects can be added to sprite groups and - * states, with full support for tinting, fading, rotation, and scaling. Uses libGDX's - * {@link BitmapFont} for rendering and optionally {@link FreeTypeFontGenerator} for - * dynamic font generation from {@code .ttf}/{@code .otf} files. - * - *

Auto-sizing

- *

By default, {@code FlixelText} auto-sizes to fit its text content. To use a fixed - * width, pass a positive {@code fieldWidth} to the constructor or call - * {@link #setFieldWidth(float)}. A fixed height can be set via {@link #setFieldHeight(float)}. - * - *

Fonts

- *

The default font is libGDX's built-in bitmap font (Arial 15px), scaled to the - * requested size. For best quality at any size, supply a {@code .ttf} or {@code .otf} - * file via {@link #setFont(FileHandle)}, which uses FreeType to generate a crisp - * bitmap font at the exact pixel size requested. - * - *

Border Styles

- *

Text can be rendered with borders via {@link #setBorderStyle(BorderStyle, Color, float, float)}. - * Supported styles are {@link BorderStyle#SHADOW}, {@link BorderStyle#OUTLINE}, and - * {@link BorderStyle#OUTLINE_FAST}. - * - *

Sprite Methods

- *

Graphic-loading and animation methods inherited from {@link FlixelSprite} are not - * applicable to text and will throw {@link UnsupportedOperationException} if called. - */ -public class FlixelText extends FlixelSprite { - - /** The text being displayed. */ - private String text = ""; - - /** Font size in pixels. */ - private int size; - - /** Horizontal alignment within the field. */ - private Alignment alignment = Alignment.LEFT; - - /** Whether text wraps at {@link #fieldWidth}. Defaults to {@code true}. */ - private boolean wordWrap = true; - - /** - * Whether the field dimensions are determined automatically from the text - * content. Requires {@link #wordWrap} to be {@code false} to take full effect. - */ - private boolean autoSize = true; - - /** The width of the text field. {@code 0} means auto-width. */ - private float fieldWidth; - - /** The height of the text field. {@code 0} means auto-height. */ - private float fieldHeight; - - /** Whether to render bold text. Only effective with FreeType fonts. */ - private boolean bold = false; - - /** Whether to render italic text. Only effective with FreeType fonts. */ - private boolean italic = false; - - /** Whether the text is underlined. Stored for API compatibility. */ - private boolean underline = false; - - /** Extra horizontal spacing between characters, in pixels. */ - private float letterSpacing = 0; - - /** The current border style. */ - private BorderStyle borderStyle = BorderStyle.NONE; - - /** The color of the border in RGBA. */ - private final Color borderColor = new Color(Color.CLEAR); - - /** The size of the border in pixels. */ - private float borderSize = 1; - - /** - * Quality of the border rendering. {@code 0}: single iteration, - * {@code 1}: one iteration for every pixel in {@link #borderSize}. - */ - private float borderQuality = 1; - - /** The font used for text rendering. */ - private BitmapFont bitmapFont; - - /** Cached text layout used for measurement and drawing. */ - private final GlyphLayout glyphLayout = new GlyphLayout(); - - /** The TrueType font file used for FreeType generation, or {@code null} for the default font. */ - private FileHandle fontFile; - - /** - * The {@link FlixelFontRegistry} ID for the font, or {@code null} if a direct - * {@link FileHandle} or the default font is used instead. - */ - private String fontRegistryId; - - /** - * The FreeType generator used by this instance. May be owned by - * {@link FlixelFontRegistry} (shared) or by this instance (private). - */ - private FreeTypeFontGenerator generator; - - /** The path used to create a privately-owned {@link #generator}, for change detection. */ - private String currentGeneratorPath; - - /** Whether this instance created (and therefore owns) the current {@link #generator}. */ - private boolean ownsGenerator; - - /** Whether the font needs to be regenerated (size, bold, italic, or font file changed). */ - private boolean fontDirty = true; - - /** Whether the text layout needs to be recalculated. */ - private boolean layoutDirty = true; - - /** Reusable matrix to save the batch's transform before applying text transforms. */ - private final Matrix4 savedTransform = new Matrix4(); - - /** Reusable matrix for applying rotation/scale transforms during drawing. */ - private final Matrix4 textTransform = new Matrix4(); - - /** Creates a new text object at (0, 0) with default settings. */ - public FlixelText() { - this(0, 0, 0, null, 8); - } - - /** - * Creates a new text object at the specified position. - * - * @param x The x position of the text. - * @param y The y position of the text. - */ - public FlixelText(float x, float y) { - this(x, y, 0, null, 8); - } - - /** - * Creates a new text object at the specified position with a field width. - * - * @param x The x position of the text. - * @param y The y position of the text. - * @param fieldWidth The width of the text field. Auto-sizes if {@code <= 0}. - */ - public FlixelText(float x, float y, float fieldWidth) { - this(x, y, fieldWidth, null, 8); - } - - /** - * Creates a new text object with position, field width, and initial text. - * - * @param x The x position of the text. - * @param y The y position of the text. - * @param fieldWidth The width of the text field. Auto-sizes if {@code <= 0}. - * @param text The text to display initially. - */ - public FlixelText(float x, float y, float fieldWidth, String text) { - this(x, y, fieldWidth, text, 8); - } - - /** - * Creates a new text object with all primary parameters. - * - * @param x The x position of the text. - * @param y The y position of the text. - * @param fieldWidth The width of the text field. Auto-sizes if {@code <= 0}. - * @param text The text to display initially. - * @param size The font size in pixels. - */ - public FlixelText(float x, float y, float fieldWidth, String text, int size) { - super(); - setPosition(x, y); - setFieldWidth(fieldWidth); - setAutoSize(fieldWidth <= 0); - setText(text); - setTextSize(size); - } - - /** Returns the text currently being displayed. */ - public String getText() { - return text; - } - - /** - * Sets the text to display. - * - * @param text The new text string. - * @return This instance for chaining. - */ - public FlixelText setText(String text) { - String newText = (text != null) ? text : "null"; - if (!this.text.equals(newText)) { - this.text = newText; - layoutDirty = true; - } - return this; - } - - /** Returns the font size in pixels. */ - public int getTextSize() { - return size; - } - - /** - * Sets the font size in pixels. When using a FreeType font, this triggers font - * regeneration. When using the default font, the built-in bitmap font is scaled. - * - * @param size The new font size (minimum 1). - * @return This instance for chaining. - */ - public FlixelText setTextSize(int size) { - int newSize = Math.max(1, size); - if (this.size != newSize) { - this.size = newSize; - fontDirty = true; - layoutDirty = true; - } - return this; - } - - /** Returns the current text alignment. */ - public Alignment getAlignment() { - return alignment; - } - - /** - * Sets the horizontal alignment of the text within the field. Only has a visible - * effect when {@link #isAutoSize()} is {@code false} (i.e. the field has a fixed width). - * - * @param alignment The alignment to use. - * @return This instance for chaining. - */ - public FlixelText setAlignment(@NotNull Alignment alignment) { - if (this.alignment != alignment) { - this.alignment = alignment; - layoutDirty = true; - } - return this; - } - - /** Returns whether word wrapping is enabled. */ - public boolean isWordWrap() { - return wordWrap; - } - - /** - * Enables or disables word wrapping. Defaults to {@code true}. - * - * @param wordWrap Whether to wrap text at the field width. - * @return This instance for chaining. - */ - public FlixelText setWordWrap(boolean wordWrap) { - if (this.wordWrap != wordWrap) { - this.wordWrap = wordWrap; - layoutDirty = true; - } - return this; - } - - /** Returns whether the text field auto-sizes to fit its content. */ - public boolean isAutoSize() { - return autoSize; - } - - /** - * Sets whether the text field auto-sizes to fit content. When {@code true}, - * {@link #getFieldWidth()} and {@link #getFieldHeight()} are determined - * automatically. Requires {@link #isWordWrap()} to be {@code false} to - * take full effect. - * - * @param autoSize Whether to auto-size. - * @return This instance for chaining. - */ - public FlixelText setAutoSize(boolean autoSize) { - if (this.autoSize != autoSize) { - this.autoSize = autoSize; - layoutDirty = true; - } - return this; - } - - /** Returns the width of the text field, or {@code 0} if auto-sizing. */ - public float getFieldWidth() { - return fieldWidth; - } - - /** - * Sets the width of the text field. Enables auto-sizing if {@code <= 0}. - * - * @param fieldWidth The field width in pixels. - * @return This instance for chaining. - */ - public FlixelText setFieldWidth(float fieldWidth) { - float newWidth = Math.max(0, fieldWidth); - if (this.fieldWidth != newWidth) { - this.fieldWidth = newWidth; - if (newWidth <= 0) { - autoSize = true; - } - layoutDirty = true; - } - return this; - } - - /** Returns the height of the text field, or {@code 0} if auto-height. */ - public float getFieldHeight() { - return fieldHeight; - } - - /** - * Sets the height of the text field. When {@code <= 0}, height is determined - * automatically from the text content. Has no effect when {@link #isAutoSize()} - * is {@code true}. - * - * @param fieldHeight The field height in pixels. - * @return This instance for chaining. - */ - public FlixelText setFieldHeight(float fieldHeight) { - float newHeight = Math.max(0, fieldHeight); - if (this.fieldHeight != newHeight) { - this.fieldHeight = newHeight; - layoutDirty = true; - } - return this; - } - - /** Returns whether bold text is enabled. */ - public boolean isBold() { - return bold; - } - - /** - * Sets whether to use bold text. Only takes visual effect when a {@code .ttf} - * font has been set via {@link #setFont(FileHandle)}, since the default bitmap - * font does not support runtime weight changes. - * - * @param bold Whether to use bold. - * @return This instance for chaining. - */ - public FlixelText setBold(boolean bold) { - if (this.bold != bold) { - this.bold = bold; - if (fontFile != null || fontRegistryId != null) { - fontDirty = true; - } - layoutDirty = true; - } - return this; - } - - /** Returns whether italic text is enabled. */ - public boolean isItalic() { - return italic; - } - - /** - * Sets whether to use italic text. Only takes visual effect when a {@code .ttf} - * font has been set via {@link #setFont(FileHandle)}, since the default bitmap - * font does not support runtime style changes. - * - * @param italic Whether to use italic. - * @return This instance for chaining. - */ - public FlixelText setItalic(boolean italic) { - if (this.italic != italic) { - this.italic = italic; - if (fontFile != null || fontRegistryId != null) { - fontDirty = true; - } - layoutDirty = true; - } - return this; - } - - /** Returns whether underline is enabled. */ - public boolean isUnderline() { - return underline; - } - - /** - * Sets whether to underline the text. This property is stored for API - * compatibility but visual rendering of underlines is limited. - * - * @param underline Whether to underline. - * @return This instance for chaining. - */ - public FlixelText setUnderline(boolean underline) { - this.underline = underline; - return this; - } - - /** Returns the letter spacing in pixels. */ - public float getLetterSpacing() { - return letterSpacing; - } - - /** - * Sets the spacing between characters in pixels. Only takes visual effect - * when a {@code .ttf} font has been set via {@link #setFont(FileHandle)}, - * as FreeType uses this value during glyph generation. - * - * @param letterSpacing The spacing in pixels. - * @return This instance for chaining. - */ - public FlixelText setLetterSpacing(float letterSpacing) { - if (this.letterSpacing != letterSpacing) { - this.letterSpacing = letterSpacing; - if (fontFile != null || fontRegistryId != null) { - fontDirty = true; - } - layoutDirty = true; - } - return this; - } - - /** - * Returns whether this text uses a custom embedded font ({@code true}) or the - * default libGDX bitmap font ({@code false}). A font is considered embedded when - * set via {@link #setFont(FileHandle)}, {@link #setFont(String)}, or when a - * {@linkplain FlixelFontRegistry#setDefault(String) registry default} is active. - */ - public boolean isEmbedded() { - return fontFile != null || fontRegistryId != null - || FlixelFontRegistry.getDefault() != null; - } - - /** - * Returns the {@link FlixelFontRegistry} ID currently set on this text, or - * {@code null} if a direct {@link FileHandle} or the default font is used. - */ - public String getFontRegistryId() { - return fontRegistryId; - } - - /** - * Sets the font by its {@link FlixelFontRegistry} identifier. The font must - * have been previously registered via - * {@link FlixelFontRegistry#register(String, FileHandle)}. Pass {@code null} - * to clear the registry reference and fall back to the default resolution order - * (direct file → registry default → built-in font). - * - * @param id The registered font ID, or {@code null} to clear. - * @return This instance for chaining. - * @throws IllegalArgumentException if {@code id} is non-null but not registered. - */ - public FlixelText setFont(String id) { - if (id != null && !FlixelFontRegistry.has(id)) { - throw new IllegalArgumentException("No font registered with id \"" + id + "\"."); - } - disposeOwnedGenerator(); - this.fontRegistryId = id; - this.fontFile = null; - fontDirty = true; - layoutDirty = true; - return this; - } - - /** - * Sets a custom TrueType font file for this text. Uses FreeType to generate a - * bitmap font at the current size. Pass {@code null} to revert to the default - * font. This clears any previously set {@linkplain #setFont(String) registry ID}. - * - * @param fontFile The {@code .ttf} or {@code .otf} file handle, or {@code null}. - * @return This instance for chaining. - */ - public FlixelText setFont(FileHandle fontFile) { - disposeOwnedGenerator(); - this.fontFile = fontFile; - this.fontRegistryId = null; - fontDirty = true; - layoutDirty = true; - return this; - } - - /** - * Sets a pre-built {@link BitmapFont} directly, bypassing FreeType generation. - * This gives full control over font settings. The caller is responsible for the - * font's lifecycle if this text is destroyed or the font is replaced. - * Clears any previously set font file or registry ID. - * - * @param font The bitmap font to use. Must not be {@code null}. - * @return This instance for chaining. - * @throws IllegalArgumentException if {@code font} is {@code null}. - */ - public FlixelText setBitmapFont(BitmapFont font) { - if (font == null) { - throw new IllegalArgumentException("BitmapFont cannot be null."); - } - disposeFont(); - this.bitmapFont = font; - this.fontFile = null; - this.fontRegistryId = null; - fontDirty = false; - layoutDirty = true; - return this; - } - - /** Returns the current border style. */ - public BorderStyle getBorderStyle() { - return borderStyle; - } - - /** Returns the border color. */ - public Color getBorderColor() { - return borderColor; - } - - /** Returns the border size in pixels. */ - public float getBorderSize() { - return borderSize; - } - - /** Returns the border rendering quality. */ - public float getBorderQuality() { - return borderQuality; - } - - /** - * Sets the border style, color, size, and quality in one call. - * - * @param style The border style. - * @param color The border color in RGBA. Pass {@code null} for transparent. - * @param size The border size in pixels. - * @param quality Rendering quality. {@code 0}: single iteration, {@code 1}: one - * iteration per pixel in {@code size}. - * @return This instance for chaining. - */ - public FlixelText setBorderStyle(BorderStyle style, Color color, float size, float quality) { - this.borderStyle = (style != null) ? style : BorderStyle.NONE; - this.borderColor.set((color != null) ? color : Color.CLEAR); - this.borderSize = Math.max(0, size); - this.borderQuality = Math.max(0, quality); - return this; - } - - /** - * Sets the border style and color with default size (1) and quality (1). - * - * @param style The border style. - * @param color The border color. - * @return This instance for chaining. - */ - public FlixelText setBorderStyle(BorderStyle style, Color color) { - return setBorderStyle(style, color, 1, 1); - } - - /** - * Sets the border style with a default black color, size of 1, and quality of 1. - * - * @param style The border style. - * @return This instance for chaining. - */ - public FlixelText setBorderStyle(BorderStyle style) { - return setBorderStyle(style, Color.BLACK, 1, 1); - } - - /** - * Convenience method to set many text properties at once. Pass {@code null} or - * {@code 0} for any parameter to keep its current value. - * - * @param fontFile The {@code .ttf}/{@code .otf} font file, or {@code null} to keep current. - * @param size The font size, or {@code 0} to keep current. - * @param color The text color, or {@code null} to keep current. - * @param alignment The text alignment, or {@code null} to keep current. - * @param borderStyle The border style, or {@code null} to keep current. - * @param borderColor The border color, or {@code null} to keep current. - * @return This instance for chaining. - */ - public FlixelText setFormat(FileHandle fontFile, int size, Color color, - Alignment alignment, BorderStyle borderStyle, - Color borderColor) { - if (fontFile != null) { - setFont(fontFile); - } - if (size > 0) { - setTextSize(size); - } - if (color != null) { - setColor(color); - } - if (alignment != null) { - setAlignment(alignment); - } - if (borderStyle != null) { - setBorderStyle(borderStyle, (borderColor != null) ? borderColor : this.borderColor); - } else if (borderColor != null) { - this.borderColor.set(borderColor); - } - return this; - } - - /** - * Simplified format setter with font file, size, and color. - * - * @param fontFile The font file, or {@code null} to keep current. - * @param size The font size, or {@code 0} to keep current. - * @param color The text color, or {@code null} to keep current. - * @return This instance for chaining. - */ - public FlixelText setFormat(FileHandle fontFile, int size, Color color) { - return setFormat(fontFile, size, color, null, null, null); - } - - /** - * Convenience format setter using a {@link FlixelFontRegistry} font ID. - * Pass {@code null} for any parameter to keep its current value. - * - * @param fontId The registered font ID, or {@code null} to keep current. - * @param size The font size, or {@code 0} to keep current. - * @param color The text color, or {@code null} to keep current. - * @param alignment The text alignment, or {@code null} to keep current. - * @param borderStyle The border style, or {@code null} to keep current. - * @param borderColor The border color, or {@code null} to keep current. - * @return This instance for chaining. - */ - public FlixelText setFormat(String fontId, int size, Color color, - Alignment alignment, BorderStyle borderStyle, - Color borderColor) { - if (fontId != null) { - setFont(fontId); - } - if (size > 0) { - setTextSize(size); - } - if (color != null) { - setColor(color); - } - if (alignment != null) { - setAlignment(alignment); - } - if (borderStyle != null) { - setBorderStyle(borderStyle, (borderColor != null) ? borderColor : this.borderColor); - } else if (borderColor != null) { - this.borderColor.set(borderColor); - } - return this; - } - - /** - * Simplified format setter with a registry font ID, size, and color. - * - * @param fontId The registered font ID, or {@code null} to keep current. - * @param size The font size, or {@code 0} to keep current. - * @param color The text color, or {@code null} to keep current. - * @return This instance for chaining. - */ - public FlixelText setFormat(String fontId, int size, Color color) { - return setFormat(fontId, size, color, null, null, null); - } - - /** - * Simplified format setter with size and color only. - * - * @param size The font size, or {@code 0} to keep current. - * @param color The text color, or {@code null} to keep current. - * @return This instance for chaining. - */ - public FlixelText setFormat(int size, Color color) { - return setFormat((FileHandle) null, size, color, null, null, null); - } - - /** - * Returns the actual rendered width of the text content (which may differ from - * {@link #getFieldWidth()} when a fixed field width is set). Triggers a layout - * rebuild if necessary. - */ - public float getTextWidth() { - rebuildIfDirty(); - return glyphLayout.width; - } - - /** - * Returns the actual rendered height of the text content (which may differ from - * {@link #getFieldHeight()} when a fixed field height is set). Triggers a layout - * rebuild if necessary. - */ - public float getTextHeight() { - rebuildIfDirty(); - return glyphLayout.height; - } - - /** - * Text objects do not use frame-based animation. This override prevents the - * animation state machine in {@link FlixelSprite} from running. - */ - @Override - public void update(float delta) { - // No-op: text does not animate. - } - - @Override - public void draw(Batch batch) { - if (text.isEmpty()) { - return; - } - rebuildIfDirty(); - - float scaleX = getScaleX(); - float scaleY = getScaleY(); - float rotation = getAngle(); - boolean needsTransform = rotation != 0 || scaleX != 1 || scaleY != 1; - - float textTop = getHeight(); - - if (needsTransform) { - savedTransform.set(batch.getTransformMatrix()); - - float ox = getOriginX(); - float oy = getOriginY(); - textTransform.set(savedTransform); - textTransform.translate(getX() + ox, getY() + oy, 0); - textTransform.rotate(0, 0, 1, rotation); - textTransform.scale(scaleX, scaleY, 1); - textTransform.translate(-ox, -oy, 0); - batch.setTransformMatrix(textTransform); - - drawTextContent(batch, 0, textTop); - - batch.setTransformMatrix(savedTransform); - } else { - drawTextContent(batch, getX(), getY() + textTop); - } - } - - /** @throws UnsupportedOperationException always; text objects cannot load graphics. */ - @Override - public final FlixelSprite loadGraphic(FileHandle path) { - throw new UnsupportedOperationException("FlixelText does not support loadGraphic(). Use setText() instead."); - } - - /** @throws UnsupportedOperationException always; text objects cannot load graphics. */ - @Override - public final FlixelSprite loadGraphic(FileHandle path, int frameWidth) { - throw new UnsupportedOperationException("FlixelText does not support loadGraphic(). Use setText() instead."); - } - - /** @throws UnsupportedOperationException always; text objects cannot load graphics. */ - @Override - public final FlixelSprite loadGraphic(FileHandle path, int frameWidth, int frameHeight) { - throw new UnsupportedOperationException("FlixelText does not support loadGraphic(). Use setText() instead."); - } - - /** @throws UnsupportedOperationException always; text objects cannot load graphics. */ - @Override - public final FlixelSprite loadGraphic(Texture texture, int frameWidth, int frameHeight) { - throw new UnsupportedOperationException("FlixelText does not support loadGraphic(). Use setText() instead."); - } - - /** @throws UnsupportedOperationException always; text objects cannot load sparrow frames. */ - @Override - public final FlixelSprite loadSparrowFrames(FileHandle texture, FileHandle xmlFile) { - throw new UnsupportedOperationException("FlixelText does not support loadSparrowFrames()."); - } - - /** @throws UnsupportedOperationException always; text objects cannot load sparrow frames. */ - @Override - public final FlixelSprite loadSparrowFrames(Texture texture, XmlReader.Element xmlFile) { - throw new UnsupportedOperationException("FlixelText does not support loadSparrowFrames()."); - } - - /** @throws UnsupportedOperationException always; text objects do not have frame animations. */ - @Override - public final void addAnimationByPrefix(String name, String prefix, int frameRate, boolean loop) { - throw new UnsupportedOperationException("FlixelText does not support animations."); - } - - /** @throws UnsupportedOperationException always; text objects do not have frame animations. */ - @Override - public final void addAnimation(String name, int[] frameIndices, float frameDuration) { - throw new UnsupportedOperationException("FlixelText does not support animations."); - } - - /** @throws UnsupportedOperationException always; text objects do not have frame animations. */ - @Override - public final void playAnimation(String name) { - throw new UnsupportedOperationException("FlixelText does not support animations."); - } - - /** @throws UnsupportedOperationException always; text objects do not have frame animations. */ - @Override - public final void playAnimation(String name, boolean loop) { - throw new UnsupportedOperationException("FlixelText does not support animations."); - } - - /** @throws UnsupportedOperationException always; text objects do not have frame animations. */ - @Override - public final void playAnimation(String name, boolean loop, boolean forceRestart) { - throw new UnsupportedOperationException("FlixelText does not support animations."); - } - - /** @return {@code true} always, since text has no animations to finish. */ - @Override - public final boolean isAnimationFinished() { - return true; - } - - private static final ObjectMap> EMPTY_ANIMATIONS = new ObjectMap<>(0); - - /** @return An empty map; text has no animations. */ - @Override - public final ObjectMap> getAnimations() { - return EMPTY_ANIMATIONS; - } - - /** @return {@code null} always; text has no atlas regions. */ - @Override - public final Array getAtlasRegions() { - return null; - } - - /** @return {@code null} always; text has no animation frames. */ - @Override - public final TextureAtlas.AtlasRegion getCurrentFrame() { - return null; - } - - /** @return {@code null} always; text has no image frames. */ - @Override - public final TextureRegion[][] getFrames() { - return null; - } - - @Override - public void destroy() { - reset(); - } - - @Override - public void reset() { - disposeFont(); - text = ""; - size = 8; - alignment = Alignment.LEFT; - wordWrap = true; - autoSize = true; - fieldWidth = 0; - fieldHeight = 0; - bold = false; - italic = false; - underline = false; - letterSpacing = 0; - borderStyle = BorderStyle.NONE; - borderColor.set(Color.CLEAR); - borderSize = 1; - borderQuality = 1; - fontFile = null; - fontRegistryId = null; - currentGeneratorPath = null; - ownsGenerator = false; - fontDirty = true; - layoutDirty = true; - setPosition(0, 0); - setColor(Color.WHITE); - } - - @Override - public String toString() { - return "FlixelText(text=\"" + text + "\", size=" + size - + ", x=" + getX() + ", y=" + getY() - + ", fieldWidth=" + fieldWidth + ", autoSize=" + autoSize + ")"; - } - - /** - * Rebuilds the font and/or layout if their dirty flags are set. - * Called lazily before drawing or when dimensions are queried. - */ - private void rebuildIfDirty() { - if (fontDirty) { - rebuildFont(); - fontDirty = false; - layoutDirty = true; - } - if (layoutDirty) { - rebuildLayout(); - layoutDirty = false; - } - } - - /** - * Regenerates the {@link BitmapFont} based on current settings. The font source - * is resolved in this order: - *

    - *
  1. {@link #fontRegistryId} — shared generator from {@link FlixelFontRegistry}
  2. - *
  3. {@link #fontFile} — privately-owned generator for a direct file
  4. - *
  5. {@link FlixelFontRegistry#getDefault()} — global registry default
  6. - *
  7. libGDX built-in bitmap font (Arial 15px, scaled)
  8. - *
- */ - private void rebuildFont() { - BitmapFont oldFont = bitmapFont; - - FreeTypeFontGenerator gen = resolveGenerator(); - if (gen != null) { - FreeTypeFontParameter param = new FreeTypeFontParameter(); - param.size = size; - param.spaceX = (int) letterSpacing; - param.genMipMaps = true; - param.minFilter = Texture.TextureFilter.Linear; - param.magFilter = Texture.TextureFilter.Linear; - - bitmapFont = gen.generateFont(param); - } else { - bitmapFont = new BitmapFont(); - float defaultHeight = bitmapFont.getLineHeight(); - if (defaultHeight > 0) { - bitmapFont.getData().setScale(size / defaultHeight); - } - bitmapFont.getRegion() - .getTexture() - .setFilter(Texture.TextureFilter.Linear, Texture.TextureFilter.Linear); - } - - if (oldFont != null) { - oldFont.dispose(); - } - } - - /** - * Resolves the {@link FreeTypeFontGenerator} to use, following the cascade: - * registry ID → direct file → registry default → {@code null}. - */ - private FreeTypeFontGenerator resolveGenerator() { - if (fontRegistryId != null) { - generator = FlixelFontRegistry.getGenerator(fontRegistryId); - ownsGenerator = false; - return generator; - } - - if (fontFile != null) { - String path = fontFile.path(); - if (!ownsGenerator || generator == null || !path.equals(currentGeneratorPath)) { - disposeOwnedGenerator(); - generator = new FreeTypeFontGenerator(fontFile); - currentGeneratorPath = path; - ownsGenerator = true; - } - return generator; - } - - FreeTypeFontGenerator defaultGen = FlixelFontRegistry.getDefaultGenerator(); - if (defaultGen != null) { - generator = defaultGen; - ownsGenerator = false; - return generator; - } - - return null; - } - - /** Recalculates the text layout and updates the sprite dimensions. */ - private void rebuildLayout() { - if (bitmapFont == null) { - return; - } - - boolean fixedWidth = fieldWidth > 0 && !autoSize; - - if (fixedWidth) { - glyphLayout.setText(bitmapFont, text, Color.WHITE, fieldWidth, - alignment.gdxAlign, wordWrap); - } else if (alignment != Alignment.LEFT) { - glyphLayout.setText(bitmapFont, text); - float naturalWidth = glyphLayout.width; - if (naturalWidth > 0) { - glyphLayout.setText(bitmapFont, text, Color.WHITE, naturalWidth, alignment.gdxAlign, false); - } - } else { - glyphLayout.setText(bitmapFont, text); - } - - float w = fixedWidth ? fieldWidth : glyphLayout.width; - float h = (fieldHeight > 0 && !autoSize) ? fieldHeight : glyphLayout.height; - setSize(w, h); - setOriginCenter(); - } - - /** - * Draws the full text content (border + main text) at the given coordinates. - * - * @param batch The sprite batch. - * @param x The x coordinate of the text's left edge. - * @param y The y coordinate of the text's top edge (BitmapFont convention). - */ - private void drawTextContent(Batch batch, float x, float y) { - Color spriteColor = getColor(); - - if (borderStyle != BorderStyle.NONE && borderColor.a > 0 && borderSize > 0) { - drawBorder(batch, x, y); - } - - updateLayoutColors(spriteColor); - bitmapFont.draw(batch, glyphLayout, x, y); - } - - /** Draws the text border/outline by rendering the layout at offset positions. */ - private void drawBorder(Batch batch, float x, float y) { - updateLayoutColors(borderColor); - - switch (borderStyle) { - case SHADOW: - bitmapFont.draw(batch, glyphLayout, x + borderSize, y - borderSize); - break; - - case OUTLINE_FAST: - bitmapFont.draw(batch, glyphLayout, x - borderSize, y); - bitmapFont.draw(batch, glyphLayout, x + borderSize, y); - bitmapFont.draw(batch, glyphLayout, x, y - borderSize); - bitmapFont.draw(batch, glyphLayout, x, y + borderSize); - break; - - case OUTLINE: - int iterations = Math.max(1, (int) (borderSize * borderQuality)); - float step = borderSize / iterations; - for (int i = 1; i <= iterations; i++) { - float offset = step * i; - bitmapFont.draw(batch, glyphLayout, x - offset, y - offset); - bitmapFont.draw(batch, glyphLayout, x, y - offset); - bitmapFont.draw(batch, glyphLayout, x + offset, y - offset); - bitmapFont.draw(batch, glyphLayout, x - offset, y); - bitmapFont.draw(batch, glyphLayout, x + offset, y); - bitmapFont.draw(batch, glyphLayout, x - offset, y + offset); - bitmapFont.draw(batch, glyphLayout, x, y + offset); - bitmapFont.draw(batch, glyphLayout, x + offset, y + offset); - } - break; - - default: - break; - } - } - - /** - * Updates the color of all glyphs in the cached layout. The layout stores colors - * as (glyphIndex, ABGR8888) pairs in {@link GlyphLayout#colors}. - */ - private void updateLayoutColors(Color color) { - int colorBits = color.toIntBits(); - for (int i = 1; i < glyphLayout.colors.size; i += 2) { - glyphLayout.colors.set(i, colorBits); - } - } - - /** Disposes the BitmapFont and any privately-owned generator. */ - private void disposeFont() { - if (bitmapFont != null) { - bitmapFont.dispose(); - bitmapFont = null; - } - disposeOwnedGenerator(); - } - - /** Disposes the generator only if this instance owns it (not borrowed from the registry). */ - private void disposeOwnedGenerator() { - if (generator != null && ownsGenerator) { - generator.dispose(); - } - generator = null; - currentGeneratorPath = null; - ownsGenerator = false; - } - - /** Horizontal alignment options for text within its field. */ - public enum Alignment { - LEFT(Align.left), - CENTER(Align.center), - RIGHT(Align.right); - - final int gdxAlign; - - Alignment(int gdxAlign) { - this.gdxAlign = gdxAlign; - } - - public static Alignment fromInt(int value) { - return switch (value) { - case 0 -> LEFT; - case 1 -> CENTER; - case 2 -> RIGHT; - default -> throw new IllegalArgumentException("Invalid alignment value: " + value); - }; - } - - public int toInt() { - return switch (this) { - case LEFT -> 0; - case CENTER -> 1; - case RIGHT -> 2; - }; - } - - public int toGdxAlign() { - return switch (this) { - case LEFT -> Align.left; - case CENTER -> Align.center; - case RIGHT -> Align.right; - }; - } - } - - /** Border/outline styles for text rendering. */ - public enum BorderStyle { - /** No border. */ - NONE, - /** A simple drop-shadow offset below and to the right of the text. */ - SHADOW, - /** A full outline drawn in all 8 directions around each glyph. */ - OUTLINE, - /** A faster outline using only the 4 cardinal directions. */ - OUTLINE_FAST - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelEase.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelEase.java deleted file mode 100644 index 074e992..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelEase.java +++ /dev/null @@ -1,220 +0,0 @@ -package me.stringdotjar.flixelgdx.tween; - -/** Class where all easer functions are stored, mostly used for tweening. */ -public final class FlixelEase { - - // Easing constants for specific functions. - private static final float PI2 = (float) Math.PI / 2; - private static final float EL = (float) ((float) 2 * Math.PI / .45); - private static final float B1 = (float) ((float) 1 / 2.75); - private static final float B2 = (float) ((float) 2 / 2.75); - private static final float B3 = (float) ((float) 1.5 / 2.75); - private static final float B4 = (float) ((float) 2.5 / 2.75); - private static final float B5 = (float) ((float) 2.25 / 2.75); - private static final float B6 = (float) ((float) 2.625 / 2.75); - private static final float ELASTIC_AMPLITUDE = 1; - private static final float ELASTIC_PERIOD = 0.4f; - - private FlixelEase() {} - - public static float linear(float t) { - return t; - } - - public static float quadIn(float t) { - return t * t; - } - - public static float quadOut(float t) { - return -t * (t - 2); - } - - public static float quadInOut(float t) { - return t <= .5 ? t * t * 2 : 1 - (--t) * t * 2; - } - - public static float cubeIn(float t) { - return t * t * t; - } - - public static float cubeOut(float t) { - return 1 + (--t) * t * t; - } - - public static float cubeInOut(float t) { - return t <= .5 ? t * t * t * 4 : 1 + (--t) * t * t * 4; - } - - public static float quartIn(float t) { - return t * t * t * t; - } - - public static float quartOut(float t) { - return 1 - (t -= 1) * t * t * t; - } - - public static float quartInOut(float t) { - return t <= .5 ? t * t * t * t * 8 : (float) ((1 - (t = t * 2 - 2) * t * t * t) / 2 + .5); - } - - public static float quintIn(float t) { - return t * t * t * t * t; - } - - public static float quintOut(float t) { - return (t = t - 1) * t * t * t * t + 1; - } - - public static float quintInOut(float t) { - return ((t *= 2) < 1) ? (t * t * t * t * t) / 2 : ((t -= 2) * t * t * t * t + 2) / 2; - } - - public static float smoothStepIn(float t) { - return 2 * smoothStepInOut(t / 2); - } - - public static float smoothStepOut(float t) { - return 2 * smoothStepInOut((float) (t / 2 + 0.5)) - 1; - } - - public static float smoothStepInOut(float t) { - return t * t * (t * -2 + 3); - } - - public static float smootherStepIn(float t) { - return 2 * smootherStepInOut(t / 2); - } - - public static float smootherStepOut(float t) { - return 2 * smootherStepInOut((float) (t / 2 + 0.5)) - 1; - } - - public static float smootherStepInOut(float t) { - return t * t * t * (t * (t * 6 - 15) + 10); - } - - public static float sineIn(float t) { - return (float) (-Math.cos(PI2 * t) + 1); - } - - public static float sineOut(float t) { - return (float) Math.sin(PI2 * t); - } - - public static float sineInOut(float t) { - return (float) (-Math.cos(Math.PI * t) / 2 + .5); - } - - public static float bounceIn(float t) { - return 1 - bounceOut(1 - t); - } - - public static float bounceOut(float t) { - if (t < B1) return (float) (7.5625 * t * t); - if (t < B2) return (float) (7.5625 * (t - B3) * (t - B3) + .75); - if (t < B4) return (float) (7.5625 * (t - B5) * (t - B5) + .9375); - return (float) (7.5625 * (t - B6) * (t - B6) + .984375); - } - - public static float bounceInOut(float t) { - return t < 0.5 ? (1 - bounceOut(1 - 2 * t)) / 2 : (1 + bounceOut(2 * t - 1)) / 2; - } - - public static float circIn(float t) { - return (float) -(Math.sqrt(1 - t * t) - 1); - } - - public static float circOut(float t) { - return (float) Math.sqrt(1 - (t - 1) * (t - 1)); - } - - public static float circInOut(float t) { - return (float) - (t <= .5 - ? (Math.sqrt(1 - t * t * 4) - 1) / -2 - : (Math.sqrt(1 - (t * 2 - 2) * (t * 2 - 2)) + 1) / 2); - } - - public static float expoIn(float t) { - return (float) Math.pow(2, 10 * (t - 1)); - } - - public static float expoOut(float t) { - return (float) (-Math.pow(2, -10 * t) + 1); - } - - public static float expoInOut(float t) { - return (float) - (t < .5 ? Math.pow(2, 10 * (t * 2 - 1)) / 2 : (-Math.pow(2, -10 * (t * 2 - 1)) + 2) / 2); - } - - public static float backIn(float t) { - return (float) (t * t * (2.70158 * t - 1.70158)); - } - - public static float backOut(float t) { - return (float) (1 - (--t) * (t) * (-2.70158 * t - 1.70158)); - } - - public static float backInOut(float t) { - t *= 2; - if (t < 1) return (float) (t * t * (2.70158 * t - 1.70158) / 2); - t--; - return (float) ((1 - (--t) * (t) * (-2.70158 * t - 1.70158)) / 2 + .5); - } - - public static float elasticIn(float t) { - return (float) - -(ELASTIC_AMPLITUDE - * Math.pow(2, 10 * (t -= 1)) - * Math.sin( - (t - (ELASTIC_PERIOD / (2 * Math.PI) * Math.asin(1 / ELASTIC_AMPLITUDE))) - * (2 * Math.PI) - / ELASTIC_PERIOD)); - } - - public static float elasticOut(float t) { - return (float) - (ELASTIC_AMPLITUDE - * Math.pow(2, -10 * t) - * Math.sin( - (t - (ELASTIC_PERIOD / (2 * Math.PI) * Math.asin(1 / ELASTIC_AMPLITUDE))) - * (2 * Math.PI) - / ELASTIC_PERIOD) - + 1); - } - - public static float elasticInOut(float t) { - if (t < 0.5) { - return (float) - (-0.5 - * (Math.pow(2, 10 * (t -= 0.5f)) - * Math.sin((t - (ELASTIC_PERIOD / 4)) * (2 * Math.PI) / ELASTIC_PERIOD))); - } - return (float) - (Math.pow(2, -10 * (t -= 0.5f)) - * Math.sin((t - (ELASTIC_PERIOD / 4)) * (2 * Math.PI) / ELASTIC_PERIOD) - * 0.5 - + 1); - } - - @FunctionalInterface - public interface FunkinEaseFunction { - float compute(float t); - } - - @FunctionalInterface - public interface FunkinEaseStartCallback { - void run(FlixelTween tween); - } - - @FunctionalInterface - public interface FunkinEaseUpdateCallback { - void run(FlixelTween tween); - } - - @FunctionalInterface - public interface FunkinEaseCompleteCallback { - void run(FlixelTween tween); - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTween.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTween.java deleted file mode 100644 index 445af1d..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTween.java +++ /dev/null @@ -1,296 +0,0 @@ -package me.stringdotjar.flixelgdx.tween; - -import com.badlogic.gdx.utils.Pool; -import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenSettings; -import me.stringdotjar.flixelgdx.tween.type.FlixelNumTween; -import me.stringdotjar.flixelgdx.tween.type.FlixelPropertyTween; -import me.stringdotjar.flixelgdx.tween.type.FlixelVarTween; -import org.jetbrains.annotations.NotNull; - -/** - * Core class for creating new tweens to add nice and smooth animations. - * - *

Please note that while this isn't an abstract class, it is advised to NOT create instances - * of this class, since it does not implement the actual tweening logic. Instead, you should use one of - * its subclasses, such as {@link FlixelVarTween}, or create your own subclass and add your own functionality. - * - *

The only reason this class is not abstract is to allow pooling of generic tweens when needed to save memory. - */ -public class FlixelTween implements Pool.Poolable { - - /** The global tween manager for the entire game. */ - private static final FlixelTweenManager globalManager = new FlixelTweenManager(); - - /** The settings used for how the tween is handled and calculated (aka how it looks and animates). */ - protected FlixelTweenSettings tweenSettings; - - /** The parent manager that {@code this} tween gets updated in. */ - protected FlixelTweenManager manager; - - /** How far the tween is tweening itself. This is what's used to actually tween the object! */ - protected float scale = 0.0f; - - /** How many seconds has elapsed since {@code this} tween started. */ - protected float secondsSinceStart = 0.0f; - - /** How many times {@code this} tween has updated. */ - protected int executions = 0; - - /** Is {@code this} tween currently paused? */ - public boolean paused = false; - - /** Is {@code this} tween currently active? */ - public boolean running = false; - - /** Is {@code this} tween finished tweening? */ - public boolean finished = false; - - /** Is {@code this} tween tweening backwards? */ - protected boolean backward = false; - - /** Default constructor for pooling purposes. */ - protected FlixelTween() {} - - /** - * Constructs a new tween with the provided settings. - * - * @param tweenSettings The settings that configure and determine how the tween should animate. - */ - protected FlixelTween(FlixelTweenSettings tweenSettings) { - this.tweenSettings = tweenSettings; - } - - /** - * Creates a new reflection-based tween with the provided settings and starts it in the global tween manager. - * - * @param object The object to tween its values. - * @param tweenSettings The settings that configure and determine how the tween should animate. - * @param updateCallback Callback function for updating the objects values when the tween updates. - * @return The newly created and started tween. - */ - public static FlixelTween tween(Object object, FlixelTweenSettings tweenSettings, FlixelVarTween.FunkinVarTweenUpdateCallback updateCallback) { - return new FlixelVarTween(object, tweenSettings, updateCallback) - .setManager(globalManager) - .start(); - } - - /** - * Creates a new property-based tween with the provided settings and starts it in the global tween manager. - * - * @param tweenSettings The settings that configure and determine how the tween should animate. - * @return The newly created and started tween. - */ - public static FlixelTween tween(FlixelTweenSettings tweenSettings) { - return new FlixelPropertyTween(tweenSettings) - .setManager(globalManager) - .start(); - } - - /** - * Creates a new numerical tween with the provided settings and starts it in the global tween manager. - * - * @param from The starting floating point value. - * @param to The ending floating point value. - * @param tweenSettings The settings that configure and determine how the tween should animate. - * @param updateCallback Callback function for updating any variable(s) that needs the current value when the tween updates. - * @return The newly created and started tween. - */ - public static FlixelTween num(float from, float to, FlixelTweenSettings tweenSettings, FlixelNumTween.FlixelNumTweenUpdateCallback updateCallback) { - return new FlixelNumTween(from, to, tweenSettings, updateCallback) - .setManager(globalManager) - .start(); - } - - /** - * Starts {@code this} tween and resets every value to its initial state. - * - * @return {@code this} tween. - */ - public FlixelTween start() { - resetBasic(); - running = true; - finished = false; - return this; - } - - /** - * Updates {@code this} tween by the given delta time. - * - * @param elapsed How much time has passed since the last update. - */ - public void update(float elapsed) { - if (paused || finished || !running || manager == null) { - return; - } - if (tweenSettings == null) { - return; - } - - var ease = tweenSettings.getEase(); - var duration = tweenSettings.getDuration(); - var onStart = tweenSettings.getOnStart(); - var onUpdate = tweenSettings.getOnUpdate(); - var onComplete = tweenSettings.getOnComplete(); - var framerate = tweenSettings.getFramerate(); - - float preTick = secondsSinceStart; - secondsSinceStart += elapsed; - float postTick = secondsSinceStart; - - float delay = (executions > 0) ? tweenSettings.getLoopDelay() : tweenSettings.getStartDelay(); - if (secondsSinceStart < delay) { - return; - } - - if (framerate > 0) { - preTick = Math.round(preTick * framerate) / framerate; - postTick = Math.round(postTick * framerate) / framerate; - } - - scale = Math.max((postTick - delay), 0.0f) / duration; - if (ease != null) { - scale = ease.compute(scale); - } - if (backward) { - scale = 1 - scale; - } - if (secondsSinceStart >= delay && !running) { - running = true; - if (onStart != null) { - onStart.run(this); - } - } - - // Check if the tween has finished. - if (secondsSinceStart >= duration + delay) { - scale = (backward) ? 0 : 1; - updateTweenValues(); - finished = true; - if (onComplete != null) { - onComplete.run(this); - } - } else { - updateTweenValues(); - if (postTick > preTick && onUpdate != null) { - onUpdate.run(this); - } - } - } - - /** - * Hook method called by {@link #update(float)} after {@link #scale} has been computed - * and all common checks have passed. Subclasses should override this to apply their - * tween-specific value updates instead of overriding {@link #update(float)}. - * - *

This method is guaranteed to only be called when the tween is active (not paused, - * not finished, has a manager and settings). The {@link #scale} field is already set to - * the correct value for the current frame. - */ - protected void updateTweenValues() { - // No-op by default; subclasses provide their own implementation. - } - - /** - * Resumes {@code this} tween if it was previously paused. - * - * @return {@code this} tween. - */ - public FlixelTween resume() { - paused = false; - running = true; - return this; - } - - /** - * Pauses {@code this} tween, stopping it from updating until resumed. - * - * @return {@code this} tween. - */ - public FlixelTween pause() { - paused = true; - running = false; - return this; - } - - /** - * Stops {@code this} tween. Note that this does not remove the tween from the active tweens in - * its manager. - * - * @return {@code this} tween. - */ - public FlixelTween stop() { - running = false; - return this; - } - - /** - * Restarts {@code this} tween if it is currently running and not finished. - * - * @return {@code this} tween. - */ - public FlixelTween restart() { - if (running && !finished && manager != null) { - start(); - } - return this; - } - - /** - * Cancels {@code this} tween, removes it from its manager and automatically defaults its values. - */ - public FlixelTween cancel() { - resetBasic(); - manager.getTweenPool().free(this); - return this; - } - - @Override - public void reset() { - resetBasic(); - manager = null; - } - - /** - * Resets only the basic values of {@code this} tween without removing any references to the - * object, its settings or its callback function. - */ - public void resetBasic() { - scale = 0.0f; - secondsSinceStart = 0.0f; - executions = 0; - paused = false; - running = false; - finished = false; - backward = false; - } - - public FlixelTweenSettings getTweenSettings() { - return tweenSettings; - } - - public FlixelTween setTweenSettings(@NotNull FlixelTweenSettings tweenSettings) { - this.tweenSettings = tweenSettings; - return this; - } - - public static FlixelTweenManager getGlobalManager() { - return globalManager; - } - - public FlixelTweenManager getManager() { - return manager; - } - - public FlixelTween setManager(FlixelTweenManager newManager) { - if (newManager != null) { - if (manager != null) { - int index = manager.getActiveTweens().indexOf(this, true); - manager.getActiveTweens().removeIndex(index); - manager.getTweenPool().free(this); - } - manager = newManager; - manager.getActiveTweens().add(this); - } - return this; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTweenManager.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTweenManager.java deleted file mode 100644 index 1c7f0b8..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/FlixelTweenManager.java +++ /dev/null @@ -1,65 +0,0 @@ -package me.stringdotjar.flixelgdx.tween; - -import com.badlogic.gdx.utils.Pool; -import com.badlogic.gdx.utils.SnapshotArray; - -/** Manager class for handling a list of active {@link FlixelTween}s. */ -public class FlixelTweenManager { - - /** Array where all current active tweens are stored. */ - protected final SnapshotArray activeTweens = new SnapshotArray<>(FlixelTween[]::new); - - /** A pool where all unused tweens are stored to preserve memory. */ - protected final Pool tweenPool = new Pool<>() { - @Override - protected FlixelTween newObject() { - return new FlixelTween(); - } - }; - - /** - * Updates all active tweens that are stored and updated in {@code this} manager. - * - *

Iterates in reverse so that finished ONESHOT tweens can be removed by index - * without skipping elements or traversing null padding beyond the array's valid size. - * - * @param elapsed The amount of time that has passed since the last frame. - */ - public void update(float elapsed) { - FlixelTween[] tweens = activeTweens.begin(); - for (int i = 0, n = activeTweens.size; i < n; i++) { - FlixelTween tween = tweens[i]; - if (tween == null) { - continue; - } - tween.update(elapsed); - - if (tween.finished) { - if (tween.manager != this) { - continue; - } - var settings = tween.getTweenSettings(); - if (settings == null) { - continue; - } - - switch (settings.getType()) { - case ONESHOT -> { - activeTweens.removeValue(tween, true); - tweenPool.free(tween); - } - case PERSIST -> {} // Do nothing, let it be. - } - } - } - activeTweens.end(); - } - - public SnapshotArray getActiveTweens() { - return activeTweens; - } - - public Pool getTweenPool() { - return tweenPool; - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenSettings.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenSettings.java deleted file mode 100644 index a083700..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenSettings.java +++ /dev/null @@ -1,246 +0,0 @@ -package me.stringdotjar.flixelgdx.tween.settings; - -import com.badlogic.gdx.utils.Array; -import me.stringdotjar.flixelgdx.tween.FlixelTween; -import me.stringdotjar.flixelgdx.tween.FlixelEase; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -/** - * Class for holding basic data, containing configurations to be used on a {@link FlixelTween}. - */ -public class FlixelTweenSettings { - - private float duration; - private float startDelay; - private float loopDelay; - private float framerate; - private FlixelTweenType type; - private FlixelEase.FunkinEaseFunction ease; - private FlixelEase.FunkinEaseStartCallback onStart; - private FlixelEase.FunkinEaseUpdateCallback onUpdate; - private FlixelEase.FunkinEaseCompleteCallback onComplete; - private Array goals; - private Array propertyGoals; - - public FlixelTweenSettings() { - this(FlixelTweenType.ONESHOT, FlixelEase::linear); - } - - /** - * @param type The type of tween it should be. - */ - public FlixelTweenSettings(@NotNull FlixelTweenType type) { - this(type, FlixelEase::linear); - } - - /** - * @param type The type of tween it should be. - * @param ease The easer function the tween should use (aka how it should be animated). - */ - public FlixelTweenSettings( - @NotNull FlixelTweenType type, - @Nullable FlixelEase.FunkinEaseFunction ease) { - this.duration = 1.0f; - this.startDelay = 0.0f; - this.loopDelay = 0.0f; - this.framerate = 0.0f; - this.type = type; - this.ease = ease; - this.onStart = null; - this.onUpdate = null; - this.onComplete = null; - this.goals = new Array<>(); - this.propertyGoals = new Array<>(); - } - - /** - * Adds a new goal to tween an objects field value to using reflection. - * - *

Note that this is only used on a {@link me.stringdotjar.flixelgdx.tween.type.FlixelVarTween}. - * - * @param field The field to tween. - * @param value The value to tween the field to. - * @return {@code this} tween settings object for chaining. - */ - public FlixelTweenSettings addGoal(String field, float value) { - goals.add(new FlixelTweenVarGoal(field, value)); - return this; - } - - /** - * Adds a new property goal that tweens a value via a getter and setter rather than direct field - * access. This allows side effects (bounds updates, listeners, etc.) to fire naturally through - * the setter on every interpolated step. - * - *

The getter is called once at tween start to capture the initial value. Each subsequent - * update interpolates from that captured value toward {@code toValue} and passes the result to - * the setter. - * - * @param getter Supplies the current value of the property at tween start. - * @param toValue The value to tween the property to. - * @param setter Consumes the interpolated value on every tween update. - * @return {@code this} tween settings object for chaining. - */ - public FlixelTweenSettings addPropertyGoal(@NotNull FlixelTweenPropertyGoal.FlixelTweenPropertyFloatGetter getter, float toValue, @NotNull FlixelTweenPropertyGoal.FlixelTweenPropertyFloatSetter setter) { - propertyGoals.add(new FlixelTweenPropertyGoal(getter, toValue, setter)); - return this; - } - - /** - * Sets the duration of how long the tween should last for. - * - * @param duration The new value to set. - * @return {@code this} tween settings object for chaining. - */ - public FlixelTweenSettings setDuration(float duration) { - this.duration = duration; - return this; - } - - public float getDuration() { - return duration; - } - - public FlixelTweenType getType() { - return type; - } - - public FlixelEase.FunkinEaseFunction getEase() { - return ease; - } - - public FlixelEase.FunkinEaseStartCallback getOnStart() { - return onStart; - } - - public FlixelEase.FunkinEaseUpdateCallback getOnUpdate() { - return onUpdate; - } - - public FlixelEase.FunkinEaseCompleteCallback getOnComplete() { - return onComplete; - } - - public FlixelTweenVarGoal getGoal(String field) { - for (FlixelTweenVarGoal goal : goals) { - if (goal.field().equals(field)) { - return goal; - } - } - return null; - } - - public Array getGoals() { - return goals; - } - - public Array getPropertyGoals() { - return propertyGoals; - } - - public float getLoopDelay() { - return loopDelay; - } - - public float getStartDelay() { - return startDelay; - } - - public void forEachGoal(FlixelTweenGoalVisitor visitor) { - for (int i = 0; i < goals.size; i++) { - var goal = goals.get(i); - if (goal == null) { - continue; - } - visitor.visit(goal.field(), goal.value()); - } - } - - public float getFramerate() { - return framerate; - } - - public FlixelTweenSettings setEase(FlixelEase.FunkinEaseFunction ease) { - this.ease = ease; - return this; - } - - public void clearGoals() { - goals.clear(); - propertyGoals.clear(); - } - - public FlixelTweenSettings setStartDelay(float startDelay) { - this.startDelay = startDelay; - return this; - } - - public FlixelTweenSettings setLoopDelay(float loopDelay) { - this.loopDelay = loopDelay; - return this; - } - - public FlixelTweenSettings setFramerate(float framerate) { - this.framerate = framerate; - return this; - } - - public FlixelTweenSettings setType(@NotNull FlixelTweenType type) { - this.type = type; - return this; - } - - public FlixelTweenSettings setOnStart(FlixelEase.FunkinEaseStartCallback onStart) { - this.onStart = onStart; - return this; - } - - public FlixelTweenSettings setOnUpdate(FlixelEase.FunkinEaseUpdateCallback onUpdate) { - this.onUpdate = onUpdate; - return this; - } - - public FlixelTweenSettings setOnComplete(FlixelEase.FunkinEaseCompleteCallback onComplete) { - this.onComplete = onComplete; - return this; - } - - /** - * A record containing basic info for a {@link FlixelVarTween} goal (aka a field to tween a numeric value to). - * - * @param field The field to tween. - * @param value The value to tween the field to. - */ - public record FlixelTweenVarGoal(@NotNull String field, float value) {} - - /** - * A record containing a getter, a target value, and a setter for a property-based tween goal. - * Unlike {@link FlixelTweenVarGoal}, this does not rely on reflection; the caller supplies how to - * read the initial value and how to apply each interpolated step, so setter side-effects fire - * naturally on every update. - * - * @param getter Supplies the initial value of the property when the tween starts. - * @param toValue The value to tween the property to. - * @param setter Consumes the interpolated value on every tween update. - */ - public record FlixelTweenPropertyGoal(@NotNull FlixelTweenPropertyFloatGetter getter, float toValue, @NotNull FlixelTweenPropertyFloatSetter setter) { - - /** Supplies a primitive {@code float} without boxing. */ - @FunctionalInterface - public interface FlixelTweenPropertyFloatGetter { - float get(); - } - - /** Consumes a primitive {@code float} without boxing. */ - @FunctionalInterface - public interface FlixelTweenPropertyFloatSetter { - void set(float value); - } - } - - @FunctionalInterface - public interface FlixelTweenGoalVisitor { - void visit(String field, float value); - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenType.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenType.java deleted file mode 100644 index e2a2012..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/settings/FlixelTweenType.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.stringdotjar.flixelgdx.tween.settings; - -/** Enum containing all different tween types that can determine */ -public enum FlixelTweenType { - - /** Will stop and remove itself from the manager when it finishes. */ - ONESHOT, - /** Will stop when it finishes but remain in the manager. */ - PERSIST, - /** Will play tween in reverse direction */ - BACKWARD, - /** Will restart immediately when it finishes. */ - LOOPING, - /** "To and from", will play tween hither and thither. Also loops indefinitely. */ - PINGPONG; -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelNumTween.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelNumTween.java deleted file mode 100644 index cdd4814..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelNumTween.java +++ /dev/null @@ -1,68 +0,0 @@ -package me.stringdotjar.flixelgdx.tween.type; - -import me.stringdotjar.flixelgdx.tween.FlixelTween; -import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenSettings; - -/** - * Tween type that tweens one numerical value to another. - */ -public class FlixelNumTween extends FlixelTween { - - /** The starting value of the tween. */ - protected float start; - - /** The target value of the tween. */ - protected float end; - - /** The current value of the tween. */ - protected float value; - - /** The range between the start and end values. */ - protected float range; - - /** Callback function for updating the value when the tween updates. */ - protected FlixelNumTweenUpdateCallback updateCallback; - - /** - * Constructs a new numerical tween, which will tween a simple starting number to an ending value. - * - * @param start The starting value. - * @param end The ending value. - * @param settings The settings that configure and determine how the tween should animate. - * @param updateCallback Callback function for updating any variable that needs the current value when the tween updates. - */ - public FlixelNumTween(float start, float end, FlixelTweenSettings settings, FlixelNumTweenUpdateCallback updateCallback) { - super(settings); - this.start = start; - this.end = end; - this.value = start; - this.range = end - start; - this.updateCallback = updateCallback; - } - - @Override - protected void updateTweenValues() { - if (updateCallback == null) { - return; - } - - value = start + range * scale; - - updateCallback.update(value); - } - - /** - * Functional interface for updating the numerical value when the tween updates. This is for updating any - * variable that needs the current value of the tween. - */ - @FunctionalInterface - public interface FlixelNumTweenUpdateCallback { - - /** - * A callback method that is called when the tween updates its value during its tweening (or animating) process. - * - * @param value The new current value of the numerical tween during the animation. - */ - void update(float value); - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelPropertyTween.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelPropertyTween.java deleted file mode 100644 index ebfb88f..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelPropertyTween.java +++ /dev/null @@ -1,88 +0,0 @@ -package me.stringdotjar.flixelgdx.tween.type; - -import com.badlogic.gdx.utils.Array; -import com.badlogic.gdx.utils.FloatArray; - -import me.stringdotjar.flixelgdx.tween.FlixelTween; -import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenSettings; - -/** - * Tween type for animating values via getter/setter pairs (property goals) rather than - * reflection. Use this when you need setter side effects (e.g. bounds updates, listeners) to - * run on every interpolated step. Configure with {@link FlixelTweenSettings#addPropertyGoal}. - * - *

Note that this is faster than {@link FlixelVarTween} since it does not use reflection to access the fields. - * It is recommended to use this when you need setter side effects (e.g. bounds updates, listeners) to - * run on every interpolated step, the {@code FlixelVarTween} type is there for convenience. - */ -public class FlixelPropertyTween extends FlixelTween { - - /** - * Cached property goals captured at {@link #start()} to avoid re-allocating the list every - * frame inside {@link #updateTweenValues()}. - */ - protected Array cachedPropertyGoals = new Array<>(); - - /** - * Initial values of each property goal, captured from their getter at {@link #start()}, indexed - * parallel to {@link #cachedPropertyGoals}. - */ - protected FloatArray propertyGoalStartValues = new FloatArray(); - - /** - * Constructs a new property tween with the given settings. Property goals must be added via - * {@link FlixelTweenSettings#addPropertyGoal} before starting. - * - * @param settings The settings that configure and determine how the tween should animate. - */ - public FlixelPropertyTween(FlixelTweenSettings settings) { - super(settings); - } - - @Override - public FlixelTween start() { - super.start(); - - if (tweenSettings == null) { - return this; - } - - var propertyGoals = tweenSettings.getPropertyGoals(); - if (propertyGoals == null || propertyGoals.isEmpty()) { - return this; - } - - cachedPropertyGoals.clear(); - propertyGoalStartValues.clear(); - for (int i = 0; i < propertyGoals.size; i++) { - var goal = propertyGoals.get(i); - if (goal == null) { - continue; - } - cachedPropertyGoals.add(goal); - propertyGoalStartValues.add(goal.getter().get()); - } - - return this; - } - - @Override - protected void updateTweenValues() { - for (int i = 0; i < cachedPropertyGoals.size; i++) { - float startValue = propertyGoalStartValues.get(i); - var goal = cachedPropertyGoals.get(i); - if (goal == null) { - continue; - } - float newValue = startValue + (goal.toValue() - startValue) * scale; - goal.setter().set(newValue); - } - } - - @Override - public void reset() { - super.reset(); - cachedPropertyGoals.clear(); - propertyGoalStartValues.clear(); - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelVarTween.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelVarTween.java deleted file mode 100644 index c207e0d..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/tween/type/FlixelVarTween.java +++ /dev/null @@ -1,157 +0,0 @@ -package me.stringdotjar.flixelgdx.tween.type; - -import com.badlogic.gdx.utils.ObjectFloatMap; -import com.badlogic.gdx.utils.ObjectSet; - -import me.stringdotjar.flixelgdx.tween.FlixelTween; -import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenSettings; -import me.stringdotjar.flixelgdx.util.FlixelConstants; -import me.stringdotjar.flixelgdx.util.FlixelReflectUtil; - -import java.lang.reflect.Field; - -/** - * Tween type for tweening specific fields on an object using reflection. - * - *

Although it is slightly slower than a {@link FlixelPropertyTween}, this type is here - * just in case you need it and for convenience. - */ -public class FlixelVarTween extends FlixelTween { - - /** The object to tween. */ - protected Object object; - - /** The initial values of the fields being tweened. */ - protected final ObjectFloatMap initialValues = new ObjectFloatMap<>(); - - /** Reusable map for computed tween values, cleared and repopulated each frame. */ - protected final ObjectFloatMap currentValues = new ObjectFloatMap<>(); - - /** - * Maps each goal field name to its target value. - */ - protected final ObjectFloatMap goalValues = new ObjectFloatMap<>(); - - /** - * Cache of the fields being tweened for faster access so they aren't accessed every time the - * {@link #start()} method is called. - */ - protected Field[] fieldsCache = null; - - /** The update callback for {@code this} tween to change the objects values every update. */ - protected FlixelVarTween.FunkinVarTweenUpdateCallback updateCallback; - - /** - * Constructs a new object tween using reflection. - * - *

Note that this does NOT add the tween to the global manager, it just assigns its main - * values. That's it. If you wish to create a tween to automatically start working, you might want - * to see {@link FlixelTween#tween(Object object, FlixelTweenSettings tweenSettings, - * FunkinVarTweenUpdateCallback updateCallback)}. - * - * @param object The object to tween values. - * @param settings The settings that configure and determine how the tween should animate and last for. - * @param updateCallback Callback function for updating the objects values when the tween updates. - */ - public FlixelVarTween(Object object, FlixelTweenSettings settings, FunkinVarTweenUpdateCallback updateCallback) { - super(settings); - this.object = object; - this.updateCallback = updateCallback; - } - - @Override - public FlixelTween start() { - super.start(); - - if (tweenSettings == null) { - return this; - } - - var goals = tweenSettings.getGoals(); - if (goals == null || goals.isEmpty()) { - return this; - } - - if (fieldsCache == null) { - fieldsCache = FlixelReflectUtil.getAllFieldsAsArray(object.getClass()); - } - - ObjectSet floatFieldIds = new ObjectSet<>(fieldsCache.length); - for (Field f : fieldsCache) { - if (f != null && f.getType() == float.class) { - String name = f.getName(); - if (name != null && !name.isEmpty()) { - floatFieldIds.add(name); - } - } - } - - final Field[] fields = fieldsCache; - final Object target = object; - tweenSettings.forEachGoal((fieldName, value) -> { - if (!floatFieldIds.contains(fieldName)) { - String message = "Field \"" + fieldName + "\" does not exist on the given object or is not a float field."; - throw new RuntimeException(message); - } - for (Field field : fields) { - if (!field.getName().equals(fieldName)) { - continue; - } - try { - if (!field.trySetAccessible()) { - continue; - } - initialValues.put(fieldName, field.getFloat(target)); - goalValues.put(fieldName, value); - break; - } catch (IllegalAccessException e) { - } - } - }); - - return this; - } - - @Override - protected void updateTweenValues() { - if (updateCallback == null || goalValues.isEmpty()) { - return; - } - currentValues.clear(); - for (ObjectFloatMap.Entry entry : goalValues.entries()) { - float startValue = initialValues.get(entry.key, 0f); - float newValue = startValue + (entry.value - startValue) * scale; - currentValues.put(entry.key, newValue); - } - if (currentValues.size > 0) { - updateCallback.update(currentValues); - } - } - - @Override - public void reset() { - super.reset(); - fieldsCache = null; - goalValues.clear(); - } - - @Override - public void resetBasic() { - super.resetBasic(); - initialValues.clear(); - currentValues.clear(); - } - - /** Callback interface for changing an objects values when a var tween updates its values. */ - @FunctionalInterface - public interface FunkinVarTweenUpdateCallback { - - /** - * A callback method that is called when the tween updates its values during its tweening (or - * animating) process. - * - * @param values The new current values of the fields being tweened during the animation. - */ - void update(ObjectFloatMap values); - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java deleted file mode 100644 index 9112b27..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java +++ /dev/null @@ -1,32 +0,0 @@ -package me.stringdotjar.flixelgdx.util; - -/** - * Core class for holding static classes with values which do not change. It is NOT RECOMMENDED to - * store things like {@link java.util.ArrayList}'s, as they can still be modified even if they are - * initialized as {@code final}. - */ -public final class FlixelConstants { - - /** - * Holds ASCII color code constants for text in the console. - */ - public static final class AsciiCodes { - - public static final String RESET = "\u001B[0m"; - public static final String BOLD = "\033[0;1m"; - public static final String ITALIC = "\u001B[3m"; - public static final String UNDERLINE = "\u001B[4m"; - public static final String BLACK = "\u001B[30m"; - public static final String RED = "\u001B[31m"; - public static final String GREEN = "\u001B[32m"; - public static final String YELLOW = "\u001B[33m"; - public static final String BLUE = "\u001B[34m"; - public static final String PURPLE = "\u001B[35m"; - public static final String CYAN = "\u001B[36m"; - public static final String WHITE = "\u001B[37m"; - - private AsciiCodes() {} - } - - private FlixelConstants() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java deleted file mode 100644 index 091027d..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelGitUtil.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.stringdotjar.flixelgdx.util; - -import java.io.BufferedReader; -import java.io.InputStreamReader; - -/** - * Utility class for obtaining Git information about the current repository Flixel is being ran in. - */ -public final class FlixelGitUtil { - - /** - * A record holding all info about the current Git repository. - * - * @param commit The current commit. - * @param branch The current branch. - * @param remoteUrl The repository that Polyverse is being worked on. - * @param isModified Does the current repository have any changes made to it? - */ - public record RepoInfo(String commit, String branch, String remoteUrl, String isModified) { - @Override - public String commit() { - if (commit == null || commit.isBlank()) { - return "Could not determine Git commit."; - } - return commit.length() > 7 ? commit.substring(0, 8) : commit; - } - - @Override - public String branch() { - if (branch == null || branch.isBlank()) { - return "Could not determine Git branch."; - } - return branch; - } - - @Override - public String remoteUrl() { - if (remoteUrl == null || remoteUrl.isBlank()) { - return "Could not determine Git remote URL."; - } - return remoteUrl; - } - - @Override - public String isModified() { - return (isModified != null && !isModified.isBlank()) ? "Yes" : "No"; - } - } - - /** - * Gets basic Git info about the current app running. - * - * @return A {@code RepoInfo} record with basic info. - */ - public static RepoInfo getRepoInfo() { - String commit = runGitCommand("rev-parse", "HEAD"); - String branch = runGitCommand("rev-parse", "--abbrev-ref", "HEAD"); - String remoteUrl = runGitCommand("config", "--get", "remote.origin.url"); - String isModified = runGitCommand("status", "--porcelain"); - return new RepoInfo(commit, branch, remoteUrl, isModified); - } - - private static String runGitCommand(String... args) { - try { - String[] command = new String[args.length + 1]; - command[0] = "git"; - System.arraycopy(args, 0, command, 1, args.length); - ProcessBuilder pb = new ProcessBuilder(command); - pb.redirectErrorStream(true); - Process process = pb.start(); - try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { - return reader.readLine(); - } - } catch (Exception e) { - return null; - } - } - - private FlixelGitUtil() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelMathUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelMathUtil.java deleted file mode 100644 index bd28994..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelMathUtil.java +++ /dev/null @@ -1,21 +0,0 @@ -package me.stringdotjar.flixelgdx.util; - -/** - * Utility class for various math related functions used in FlixelGDX. - */ -public final class FlixelMathUtil { - - /** - * Rounds a float value to a specified number of decimal places. - * - * @param value The float value to round. - * @param decimalPlaces The number of decimal places to round to. - * @return The rounded float value. - */ - public static float round(float value, int decimalPlaces) { - float scale = (float) Math.pow(10, decimalPlaces); - return Math.round(value * scale) / scale; - } - - private FlixelMathUtil() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelPathsUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelPathsUtil.java deleted file mode 100644 index 8764ac7..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelPathsUtil.java +++ /dev/null @@ -1,75 +0,0 @@ -package me.stringdotjar.flixelgdx.util; - -import com.badlogic.gdx.Gdx; -import com.badlogic.gdx.files.FileHandle; - -import java.io.File; -import java.io.IOException; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -/** Utility class for simplifying asset paths and libGDX {@link FileHandle}s. */ -public final class FlixelPathsUtil { - - private static final Map audioPathCache = new ConcurrentHashMap<>(); - - public static FileHandle asset(String path) { - return Gdx.files.internal(path); - } - - public static FileHandle shared(String path) { - return asset(String.format("shared/%s", path)); - } - - public static FileHandle fontAsset(String path) { - return asset(String.format("fonts/%s.ttf", path)); - } - - public static FileHandle xmlAsset(String path) { - return asset(String.format("%s.xml", path)); - } - - public static FileHandle sharedImageAsset(String path) { - return shared(String.format("images/%s.png", path)); - } - - public static FileHandle external(String path) { - return Gdx.files.external(path); - } - - /** - * Resolves an internal asset path to an absolute filesystem path that MiniAudio's native engine - * can open directly. - * - *

When running from the IDE the working directory is the {@code assets/} folder, so the raw - * relative path works as-is. When running from a packaged JAR the assets are embedded as - * classpath resources and MiniAudio cannot open them by name. In that case the resource is - * extracted to a temp file on first call, and the temp file's absolute path is returned. Results - * are cached so repeated calls for the same path do not produce extra temp files. - * - * @param path The internal asset path, e.g. {@code "shared/sounds/foo.ogg"}. - * @return An absolute filesystem path that MiniAudio can open. - */ - public static String resolveAudioPath(String path) { - return audioPathCache.computeIfAbsent(path, FlixelPathsUtil::extractAudioPath); - } - - private static String extractAudioPath(String path) { - FileHandle handle = asset(path); - if (handle.file().exists()) { - return handle.file().getAbsolutePath(); - } - // Asset is inside a JAR, copy it out to a temp file so MiniAudio can open it. - String ext = path.contains(".") ? path.substring(path.lastIndexOf('.')) : ""; - try { - File temp = File.createTempFile("flixelaudio_", ext); - temp.deleteOnExit(); - handle.copyTo(new FileHandle(temp)); - return temp.getAbsolutePath(); - } catch (IOException e) { - throw new RuntimeException("Failed to extract audio asset from JAR: " + path, e); - } - } - - private FlixelPathsUtil() {} -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java deleted file mode 100644 index be4ea93..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java +++ /dev/null @@ -1,74 +0,0 @@ -package me.stringdotjar.flixelgdx.util; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - - -/** - * Backend utility class for obtaining and manipulating fields on objects through the usage of Java reflection. - */ -public final class FlixelReflectUtil { - - private FlixelReflectUtil() {} - - /** - * Checks if a field exists on a given object. - * - * @param target The object to verify the existence of the given field to check. - * @param fieldName The name of the - * @return If the field exists on the given object. - */ - public static boolean hasField(Object target, String fieldName) { - return getAllFields(target.getClass()).stream().anyMatch(field -> field.getName().equals(fieldName)); - } - - /** - * Obtains all fields of a class, including the master types above it all the way to {@link - * Object}. - * - * @param type A class literal to obtain the fields from. - * @return All fields from itself and its master classes above it. - */ - public static List getAllFields(Class type) { - List fields = new ArrayList<>(); - for (Class c = type; c != null; c = c.getSuperclass()) { - fields.addAll(Arrays.asList(c.getDeclaredFields())); - } - return fields; - } - - /** - * Obtains all fields of a class, including the master types above it all the way to {@link - * Object}. Obvious to the name, this version returns an array instead of a list unlike its - * similar function . - * - * @param type A class literal to obtain the fields from. - * @return All fields from itself and its master classes above it. - */ - public static Field[] getAllFieldsAsArray(Class type) { - return getAllFields(type).toArray(new Field[0]); - } - - /** - * Checks if a class of a certain package is final. - * - * @param classPath The package definition of the class to check if final. An example could be - * {@code "me.stringdotjar.flixelgdx.Flixel"}. - * @return If the class provided is final. If there was an exception caught, then {@code false} is - * automatically returned. - */ - public static boolean isClassFinal(String classPath) { - try { - Class clazz = Class.forName(classPath); - // Uses the java.lang.reflect.Modifier utility. - return Modifier.isFinal(clazz.getModifiers()); - } catch (ClassNotFoundException e) { - // Treat non-existent class as non-final for safe binding, - // though the user will hit an error later. - return false; - } - } -} diff --git a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java b/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java deleted file mode 100644 index 9079425..0000000 --- a/flixelgdx/core/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java +++ /dev/null @@ -1,197 +0,0 @@ -package me.stringdotjar.flixelgdx.util; - -import java.util.jar.JarFile; - -/** - * Utility class for handling operation related to the runtime environment, including OS detection, - * extracting runtime information, obtaining information from exceptions, and other related tasks. - */ -public final class FlixelRuntimeUtil { - - /** - * Returns {@code true} when the application is running from the final packaged distribution JAR, - * and {@code false} when it is running from the IDE (even if the IDE loads individual module JARs - * on the classpath). - * - *

Gradle builds each module (e.g. {@code flixelgdx}) into its own module JAR inside - * {@code build/libs/} and puts that on the classpath during IDE runs. Checking only whether the - * code-source path ends with {@code .jar} therefore incorrectly returns {@code true} in the IDE. - * Instead, this method opens the JAR that contains this class and inspects its manifest for a - * {@code Main-Class} attribute. The only JAR in this project that carries that attribute is the - * fat distribution JAR produced by the {@code lwjgl3:jar} task. Individual module JARs do not - * have it. - * - * @return {@code true} if running from the distribution JAR, {@code false} otherwise. - */ - public static boolean isRunningFromJar() { - try { - String path = getWorkingDirectory(); - - if (path == null) { - // Failed to get the working directory, so we can't determine if we're running from a JAR or not. - return false; - } - - if (!path.endsWith(".jar")) { - // Exploded class-file directory, which we can safely assume is from the IDE. - return false; - } - // The class is inside a JAR. Module JARs built during development have no Main-Class entry. - // The fat distribution JAR always does (set by the lwjgl3:jar manifest block). - try (JarFile jar = new JarFile(path)) { - var manifest = jar.getManifest(); - return manifest != null && manifest.getMainAttributes().getValue("Main-Class") != null; - } - } catch (Exception e) { - return false; - } - } - - /** - * Returns {@code true} when the application is running inside an IDE (IntelliJ, Eclipse, Cursor, - * VS Code, etc.), and {@code false} when running from the distribution JAR or plain classpath. - * - *

Detection uses IDE-specific system properties, classpath hints, and the code source path. - * Gradle-based runs are detected via {@code build/classes} (exploded) or a JAR under - * {@code build/libs/} that is not the distribution JAR (no {@code Main-Class} in manifest). - * - * @return {@code true} if running in an IDE, {@code false} otherwise. - */ - public static boolean isRunningInIDE() { - // IntelliJ (run/debug). - if (System.getProperty("idea.launcher.port") != null) { - return true; - } - // IntelliJ fallback: idea_rt.jar on classpath (e.g. debug). - if (System.getProperty("java.class.path", "").contains("idea_rt.jar")) { - return true; - } - // Eclipse. - if (System.getProperty("eclipse.application") != null) { - return true; - } - var path = getWorkingDirectory(); - if (path == null) { - return false; - } - // IntelliJ default output. - if (path.contains("out/production")) { - return true; - } - // Eclipse default output. - if (path.contains("bin/")) { - return true; - } - // Gradle: exploded classes (e.g. build/classes/java/main). - if (path.contains("build/classes")) { - return true; - } - // Gradle: module JAR on classpath (build/libs/*.jar without Main-Class); distribution JAR has Main-Class. - if (path.contains("build/libs") && path.endsWith(".jar") && !isRunningFromJar()) { - return true; - } - return false; - } - - /** - * Detects the current runtime environment. - * - *

This method is used to determine if the application is running in the IDE, from a JAR, or from the classpath. - * - * @return The detected environment. - */ - public static RunEnvironment detectEnvironment() { - if (isRunningInIDE()) { - return RunEnvironment.IDE; - } - if (isRunningFromJar()) { - return RunEnvironment.JAR; - } - return RunEnvironment.CLASSPATH; - } - - /** - * Returns the working directory of the game. - * - * @return The working directory of the game. If an error occurs, {@code null} is returned. - */ - public static String getWorkingDirectory() { - try { - return FlixelRuntimeUtil.class - .getProtectionDomain() - .getCodeSource() - .getLocation() - .toURI() - .getPath(); - } catch (Exception e) { - return null; - } - } - - /** - * Returns the root package name of the library. This is done just in case - * (for whatever reason it may be) the root package changes. - * - * @return The root package name of the library. - */ - public static String getLibraryRoot() { - return FlixelRuntimeUtil.class.getPackageName().replaceAll("\\.[^.]+$", ""); - } - - /** - * Obtains a string representation of where an exception was thrown from, including the class, - * method, file, and line number. - * - * @param exception The exception to obtain the location from. - * @return A string representation of where the exception was thrown from. - */ - public static String getExceptionLocation(Throwable exception) { - if (exception == null) { - return "Unknown Location"; - } - StackTraceElement[] stackTrace = exception.getStackTrace(); - if (stackTrace.length == 0) { - return "Unknown Location"; - } - StackTraceElement element = stackTrace[0]; - return "FILE=" - + element.getFileName() - + ", CLASS=" - + element.getClassName() - + ", METHOD=" - + element.getMethodName() - + "(), LINE=" - + element.getLineNumber(); - } - - /** - * Obtains a full detailed message from an exception, including its type, location, and stack trace. - * - * @param exception The exception to obtain the message from. - * @return A full detailed message from the exception. - */ - public static String getFullExceptionMessage(Throwable exception) { - if (exception == null) { - return "No exception provided."; - } - StringBuilder messageBuilder = new StringBuilder(); - messageBuilder.append("Exception: ").append(exception).append("\n"); - messageBuilder.append("Location: ").append(getExceptionLocation(exception)).append("\n"); - messageBuilder.append("Stack Trace:\n"); - for (StackTraceElement element : exception.getStackTrace()) { - messageBuilder.append("\tat ").append(element.toString()).append("\n"); - } - return messageBuilder.toString(); - } - - /** - * Enum representing the environment in which Flixel is running in. - */ - public enum RunEnvironment { - IDE, - JAR, - CLASSPATH - } - - private FlixelRuntimeUtil() {} -} diff --git a/flixelgdx/core/src/main/java/module-info.java b/flixelgdx/core/src/main/java/module-info.java deleted file mode 100644 index 784f441..0000000 --- a/flixelgdx/core/src/main/java/module-info.java +++ /dev/null @@ -1,23 +0,0 @@ -module me.stringdotjar.flixelgdx.core { - exports me.stringdotjar.flixelgdx; - exports me.stringdotjar.flixelgdx.backend; - exports me.stringdotjar.flixelgdx.display; - exports me.stringdotjar.flixelgdx.text; - exports me.stringdotjar.flixelgdx.group; - exports me.stringdotjar.flixelgdx.input; - exports me.stringdotjar.flixelgdx.logging; - exports me.stringdotjar.flixelgdx.signal; - exports me.stringdotjar.flixelgdx.tween; - exports me.stringdotjar.flixelgdx.tween.settings; - exports me.stringdotjar.flixelgdx.tween.type; - exports me.stringdotjar.flixelgdx.util; - - // Automatic module names (from JAR filenames when on module path). - requires transitive gdx; - requires transitive gdx.freetype; - requires transitive anim8.gdx; - requires transitive libgdx.utils; - requires transitive miniaudio; - requires org.fusesource.jansi; - requires org.jetbrains.annotations; -} diff --git a/flixelgdx/lwjgl3/build.gradle b/flixelgdx/lwjgl3/build.gradle deleted file mode 100644 index 4e8bcd5..0000000 --- a/flixelgdx/lwjgl3/build.gradle +++ /dev/null @@ -1,18 +0,0 @@ -[compileJava, compileTestJava]*.options*.encoding = 'UTF-8' -eclipse.project.name = appName + '-flixelgdx-lwjgl3' - -apply plugin: "java-library" - -java { - sourceCompatibility = 17 - targetCompatibility = 17 -} - -dependencies { - api project(":flixelgdx:core") - - api "games.rednblack.miniaudio:gdx-miniaudio-platform:$miniaudioVersion:natives-desktop" - api "com.badlogicgames.gdx:gdx-backend-lwjgl3:$gdxVersion" - api "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" - api "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" -} diff --git a/flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/FlixelLwjgl3Launcher.java b/flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/FlixelLwjgl3Launcher.java deleted file mode 100644 index 9a91ab0..0000000 --- a/flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/FlixelLwjgl3Launcher.java +++ /dev/null @@ -1,56 +0,0 @@ -package me.stringdotjar.flixelgdx.backend.lwjgl3; - -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Application; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3ApplicationConfiguration; -import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowAdapter; -import me.stringdotjar.flixelgdx.Flixel; -import me.stringdotjar.flixelgdx.FlixelGame; -import me.stringdotjar.flixelgdx.backend.lwjgl3.alert.FlixelLwjgl3Alerter; - -/** - * Launches the desktop (LWJGL3) version of the Flixel game. - */ -public class FlixelLwjgl3Launcher { - - /** - * Launches the LWJGL3 version of the Flixel game with the given game instance. This should be called from the main - * method of the libGDX LWJGL3 launcher class, and the game instance should be created in the same general area. - * - * @param game The game instance to launch. This should already be initialized with the desired configuration values. - */ - public static void launch(FlixelGame game, String... icons) { - Flixel.initialize(game, new FlixelLwjgl3Alerter()); - - Lwjgl3ApplicationConfiguration configuration = new Lwjgl3ApplicationConfiguration(); - configuration.setTitle(game.getTitle()); - configuration.useVsync(game.isVsync()); - configuration.setForegroundFPS(game.getFramerate()); - if (game.isFullscreen()) { - configuration.setFullscreenMode(Lwjgl3ApplicationConfiguration.getDisplayMode()); - } else { - configuration.setWindowedMode(game.getWindowWidth(), game.getWindowHeight()); - } - configuration.setWindowIcon(icons); - configuration.setWindowListener(new Lwjgl3WindowAdapter() { - @Override - public void focusGained() { - super.focusGained(); - Flixel.getGame().onWindowFocused(); - } - - @Override - public void focusLost() { - super.focusLost(); - Flixel.getGame().onWindowUnfocused(); - } - - @Override - public void iconified(boolean isIconified) { - super.iconified(isIconified); - Flixel.getGame().onWindowMinimized(isIconified); - } - }); - - new Lwjgl3Application(game, configuration); - } -} diff --git a/flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/alert/FlixelLwjgl3Alerter.java b/flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/alert/FlixelLwjgl3Alerter.java deleted file mode 100644 index bc5601d..0000000 --- a/flixelgdx/lwjgl3/src/main/java/me/stringdotjar/flixelgdx/backend/lwjgl3/alert/FlixelLwjgl3Alerter.java +++ /dev/null @@ -1,41 +0,0 @@ -package me.stringdotjar.flixelgdx.backend.lwjgl3.alert; - -import me.stringdotjar.flixelgdx.backend.Alerter; - -import java.awt.EventQueue; -import java.lang.reflect.InvocationTargetException; - -import javax.swing.JOptionPane; - -public class FlixelLwjgl3Alerter implements Alerter { - - @Override - public void showInfoAlert(String title, String message) { - showAlert(title, message, JOptionPane.INFORMATION_MESSAGE); - } - - @Override - public void showWarningAlert(String title, String message) { - showAlert(title, message, JOptionPane.WARNING_MESSAGE); - } - - @Override - public void showErrorAlert(String title, String message) { - showAlert(title, message, JOptionPane.ERROR_MESSAGE); - } - - private void showAlert(String title, Object message, int type) { - String msg = message != null ? message.toString() : "null"; - if (EventQueue.isDispatchThread()) { - JOptionPane.showMessageDialog(null, msg, title, type); - } else { - try { - EventQueue.invokeAndWait(() -> { - JOptionPane.showMessageDialog(null, msg, title, type); - }); - } catch (InterruptedException | InvocationTargetException e) { - // Ignore. - } - } - } -} diff --git a/lwjgl3/build.gradle b/lwjgl3/build.gradle index bcd9bd5..453c610 100644 --- a/lwjgl3/build.gradle +++ b/lwjgl3/build.gradle @@ -26,7 +26,9 @@ if (JavaVersion.current().isJava9Compatible()) { dependencies { implementation project(':polyverse') - implementation project(":flixelgdx:lwjgl3") + implementation("com.github.stringdotjar:flixelgdx-lwjgl3:master-SNAPSHOT") { + changing = true + } if (enableGraalNative == 'true') { implementation "io.github.berstanio:gdx-svmhelper-backend-lwjgl3:$graalHelperVersion" diff --git a/polyverse/build.gradle b/polyverse/build.gradle index 6c123dd..ad12c6d 100644 --- a/polyverse/build.gradle +++ b/polyverse/build.gradle @@ -11,7 +11,9 @@ processResources.from(generatePolyverseVersion) { } dependencies { - implementation project(":flixelgdx:core") + implementation("com.github.stringdotjar:flixelgdx-core:master-SNAPSHOT") { + changing = true + } implementation "org.apache.groovy:groovy:5.0.4" implementation "org.jetbrains:annotations:26.1.0" diff --git a/settings.gradle b/settings.gradle index 0684d9f..f9b766b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,5 +9,12 @@ plugins { // Polyverse and libGDX projects. include 'polyverse', 'lwjgl3', 'android' -// FlixelGDX projects. -include 'flixelgdx:core', 'flixelgdx:lwjgl3', 'flixelgdx:android' +// Include the FlixelGDX library if its found locally in the same parent folder as Polyverse. +includeBuild('../flixelgdx') { + dependencySubstitution { + // Substitute using the new group ID + substitute module('com.github.stringdotjar:flixelgdx-core') using project(':core') + substitute module('com.github.stringdotjar:flixelgdx-lwjgl3') using project(':lwjgl3') + substitute module('com.github.stringdotjar:flixelgdx-android') using project(':android') + } +} From e9c7d2c0b7a7cd2c5dc04020c31b7241d7047e78 Mon Sep 17 00:00:00 2001 From: String Date: Fri, 27 Feb 2026 05:10:53 -0600 Subject: [PATCH 2/3] Update settings.gradle --- settings.gradle | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/settings.gradle b/settings.gradle index f9b766b..01919db 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,12 +9,14 @@ plugins { // Polyverse and libGDX projects. include 'polyverse', 'lwjgl3', 'android' -// Include the FlixelGDX library if its found locally in the same parent folder as Polyverse. -includeBuild('../flixelgdx') { - dependencySubstitution { - // Substitute using the new group ID - substitute module('com.github.stringdotjar:flixelgdx-core') using project(':core') - substitute module('com.github.stringdotjar:flixelgdx-lwjgl3') using project(':lwjgl3') - substitute module('com.github.stringdotjar:flixelgdx-android') using project(':android') +// Include the FlixelGDX library only if it's found locally in the same parent folder as Polyverse. +if (file('../flixelgdx').exists()) { + includeBuild('../flixelgdx') { + dependencySubstitution { + // Substitute using the new group ID + substitute module('com.github.stringdotjar:flixelgdx-core') using project(':core') + substitute module('com.github.stringdotjar:flixelgdx-lwjgl3') using project(':lwjgl3') + substitute module('com.github.stringdotjar:flixelgdx-android') using project(':android') + } } } From 26b99407464bb367842792ec28e8085c081080ff Mon Sep 17 00:00:00 2001 From: String Date: Fri, 27 Feb 2026 05:18:20 -0600 Subject: [PATCH 3/3] =?UTF-8?q?Dawg=20let=20me=20pass=20the=20checks=20?= =?UTF-8?q?=F0=9F=99=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yaml | 18 +++++++++++++++++- settings.gradle | 10 +++++++--- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 005304b..6879126 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -17,6 +17,14 @@ jobs: steps: - uses: actions/checkout@v4 + # Check out the flixelgdx library so Gradle can use the local included build + # instead of pulling from JitPack (which avoids 401/authorization issues). + - name: Checkout flixelgdx library + uses: actions/checkout@v4 + with: + repository: stringdotjar/flixelgdx + path: flixelgdx + # LibGDX typically uses Java 11 or newer. - name: Set up JDK 17 uses: actions/setup-java@v4 @@ -42,7 +50,15 @@ jobs: steps: - uses: actions/checkout@v4 - # LibGDX typically uses Java 11 or newer. + # Check out the flixelgdx library so Gradle can use the local included build + # instead of pulling from JitPack (which avoids 401/authorization issues). + - name: Checkout flixelgdx library + uses: actions/checkout@v4 + with: + repository: stringdotjar/flixelgdx + path: flixelgdx + + # LibGDX typically uses Java 11 or newer, however we use Java 17 for modern standards. - name: Set up JDK 17 uses: actions/setup-java@v4 with: diff --git a/settings.gradle b/settings.gradle index 01919db..7c24eeb 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,9 +9,13 @@ plugins { // Polyverse and libGDX projects. include 'polyverse', 'lwjgl3', 'android' -// Include the FlixelGDX library only if it's found locally in the same parent folder as Polyverse. -if (file('../flixelgdx').exists()) { - includeBuild('../flixelgdx') { +// Include the FlixelGDX library only if it's found locally. +// This supports two layouts: +// 1) ../flixelgdx - sibling repo next to Polyverse (typical local dev) +// 2) flixelgdx - nested repo inside this project (used by CI workflow) +def flixelLocalDir = ['../flixelgdx', 'flixelgdx'].collect { file(it) }.find { it.exists() } +if (flixelLocalDir != null) { + includeBuild(flixelLocalDir) { dependencySubstitution { // Substitute using the new group ID substitute module('com.github.stringdotjar:flixelgdx-core') using project(':core')