diff --git a/COMPILING.md b/COMPILING.md index 465f4d7..f29f9d6 100644 --- a/COMPILING.md +++ b/COMPILING.md @@ -1,28 +1,23 @@ -# How to Compile FNF:JE +# How to Compile the Polyverse Environment > [!TIP] -> If you want some helpful info about the Gradle tasks (or the framework FNF:JE uses), consider taking a look at [LIBGDX.md](PROJECT.md)! +> If you want some helpful info about the Gradle tasks (or the frameworks Polyverse uses), consider taking a look at [PROJECT.md](PROJECT.md)! -There are two main ways you can download and compile the game's code: with GitHub Desktop or the terminal. - -In this guide, we'll use the GitHub Desktop method, since it the most user-friendly experience, which provides you a nice UI and does all the hard stuff for you! - -# Prerequisites (for all methods) -- A [GitHub](https://github.com) account to download the game's GitHub repository. +# Prerequisites (*REQUIRED*) +- A [GitHub](https://github.com) account to download the game's GitHub repository (only if you're using GitHub Desktop!). - A [Java Development Kit (JDK)](https://www.oracle.com/java/technologies/javase/jdk17-archive-downloads.html) to compile the game's code. -- An integrated development environment. - - We recommend either one of the options listed below: - - [IntelliJ IDEA](https://www.jetbrains.com/idea/download/) - - [Eclipse](https://www.eclipse.org/downloads/) - - [VS Code](https://code.visualstudio.com/) -- Some basic knowledge of programming (especially Gradle) and how to navigate an IDE. +- The [IntelliJ IDEA](https://www.jetbrains.com/idea/download/) IDE. +- Some basic knowledge of programming (especially with using Gradle) and how to navigate an IDE. > [!TIP] -> Although every IDE listed is great for Java, we STRONGLY recommend IntelliJ IDEA, due to how beginner-friendly and integrated it is with FNF:JE! +> Although Eclipse is great for Java as well, we STRONGLY recommend IntelliJ IDEA, due to how beginner-friendly and integrated it is with Polyverse! # Step-by-Step Guide -1. Visit the official [GitHub Desktop website](https://desktop.github.com/download/) and download the app. +1. Download the JDK and run the installer accordingly. You generally don't need to change any settings + +2. Visit the official [GitHub Desktop website](https://desktop.github.com/download/) and download the app. Make sure to run the installer and + sign in with your GitHub account! > [!TIP] > If you're on Linux, don't worry! You can install a community made version by running the following commands in the terminal: @@ -35,19 +30,29 @@ In this guide, we'll use the GitHub Desktop method, since it the most user-frien > sudo apt update && sudo apt install github-desktop > ``` -2. When GitHub Desktop is done installing, sign in with your GitHub account when prompted to do so. +3. When GitHub Desktop is done installing, sign in with your GitHub account when prompted to do so. + +4. Go back to your browser and (on the official home page for Polyverse's repository), click the green `<> Code` button and select `Open with GitHub Desktop`. + You should see a prompt asking if you want to clone the game's code. + +> [!NOTE] +> If you're on Linux, cloning through the GitHub website and desktop app won't work. Although most Linux distros already have Git installed, +> you can check if you have it by running the command `git --version`. If you get an error saying the command doesn't exist, then you will have to install it onto your system. +> You can install Polyverse through Git with the following command (make sure you're running it in the folder you want it to be located in!): -3. Go back to your browser and (on the official home page for FNF:JE's repository), click the green `<> Code` button and select `Open with GitHub Desktop`. You should see a prompt asking if you want to clone the game's code. +```shell +git clone https://github.com/stringdotjar/Polyverse-Funkin.git optional/path/to/clone/into/ +``` -4. Click the blue `Clone` button and wait for the game's code to download. +5. Click the blue `Clone` button and wait for the game's code to download. > [!TIP] > We recommend putting the game's code in a place where you'll easily remember where it's at, such as your `Documents\GitHub` folder if you're on Windows! -5. Launch your IDE and open the game's folder when prompted to do so. +6. Open IntelliJ IDEA and open the game's folder when prompted to do so. > [!IMPORTANT] > When you open the game's folder, your IDE will most likely start running Gradle tasks. Don't worry, this is normal and expected! -6. When the tasks are finished, you can now run the game's code! You can do so by running the applicable task depending on the platform. +7. When the tasks are finished, you can now run the game's code! You can do so by running the applicable task depending on the platform. - For example, you can run the desktop version by executing the task `lwjgl3:run`. diff --git a/LICENSE.md b/LICENSE.md index 4a3c97d..a986765 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,8 +1,8 @@ -# Friday Night Funkin': Java Edition License +# Polyverse Funkin' License The original game (created by Cameron Taylor and the Funkin' Crew) uses a license called the *Apache License*, which is a permissive, free software license that allows users to freely use, modify, and distribute the software for any purpose, including commercially. -Because Friday Night Funkin': Java Edition (FNF:JE) is based off of the base game, we also adapt the same license they have to maintain consistency. +Because Polyverse Funkin' is an environment made for the base game, we also adapt the same license they have to maintain consistency. ## License diff --git a/PROJECT.md b/PROJECT.md index 6681816..50ad7ee 100644 --- a/PROJECT.md +++ b/PROJECT.md @@ -1,11 +1,11 @@ ## This is a [libGDX](https://libgdx.com/) project generated with [gdx-liftoff](https://github.com/libgdx/gdx-liftoff). -# Platforms +# Project Modules -FNF:JE is designed to run on multiple different platforms. Below are the different modules that hold the code for each one. +Polyverse has many different submodules to organize the environment. - `funkin`: The core part of the game's code. This is where all the game logic is implemented. -- `flixelgdx`: Custom framework that bridges [HaxeFlixel](https://haxeflixel.com/) and is based on libGDX. This is where the HaxeFlixel-like API is implemented. +- `flixelgdx`: Custom framework that bridges [HaxeFlixel](https://haxeflixel.com/) to Java and is based on libGDX. This is where the HaxeFlixel-like API is implemented. - `polyverse`: Custom library for adding modding capabilities to the game. - `lwjgl3`: Primary desktop platform using [LWJGL3](https://www.lwjgl.org/). This is what launches the desktop versions of the game! - `android`: Android mobile platform. This requires the Android SDK, which can be downloaded and configured simply by running the universal [setup file](setup/android_setup.sh)! @@ -34,7 +34,7 @@ The Gradle wrapper was included, so you can run Gradle tasks using `gradlew.bat` - `clean`: removes `build` folders, which store compiled classes and built archives. - `eclipse`: generates Eclipse project data. - `idea`: generates IntelliJ project data. -- `funkin:exportModSDK`: Exports the game's API and its dependencies as `.jar` files to the assets folder. +- `funkin:exportModSDK`: exports the game's API and its dependencies as `.jar` files to the assets folder. - `lwjgl3:jar`: builds the game's runnable jar, which can be found at `lwjgl3/build/libs`. - `lwjgl3:run`: starts the desktop version of the game. - `test`: runs unit tests (if any). diff --git a/README.md b/README.md index cdb21a4..36e498d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Friday Night Funkin': Java Edition +# Polyverse Funkin' -Welcome to the official GitHub repository for the Java edition of Friday Night Funkin'! +Welcome to the official GitHub repository for the Java modding environment of Friday Night Funkin'! > [!NOTE] > This is a fan-made project and is not affiliated with the original developers. You can play the original game [here](https://ninja-muffin24.itch.io/funkin). @@ -8,7 +8,7 @@ Welcome to the official GitHub repository for the Java edition of Friday Night F ## Please note that this unofficial version of the game is NOT completed! This has a long way to go, but with YOUR help, we can make this project come alive! <3 # About the Project -Friday Night Funkin': Java Edition is an open-source project that aims to recreate the popular rhythm game [Friday Night Funkin'](https://github.com/FunkinCrew/Funkin) using Java. +Polyverse Funkin' is an open-source project that aims to create the popular rhythm game [Friday Night Funkin'](https://github.com/FunkinCrew/Funkin) using Java. It is built using its own custom framework based on libGDX, called FlixelGDX: a Java port of the HaxeFlixel framework used in the original game. The project is designed to have practically endless modding capabilities, empowering developers to use features for mods diff --git a/android/src/main/java/me/stringdotjar/funkin/android/AndroidLauncher.java b/android/src/main/java/me/stringdotjar/funkin/android/AndroidLauncher.java index 999621a..a1f2eb8 100644 --- a/android/src/main/java/me/stringdotjar/funkin/android/AndroidLauncher.java +++ b/android/src/main/java/me/stringdotjar/funkin/android/AndroidLauncher.java @@ -4,7 +4,7 @@ import com.badlogic.gdx.backends.android.AndroidApplication; import com.badlogic.gdx.backends.android.AndroidApplicationConfiguration; import me.stringdotjar.funkin.FunkinGame; -import me.stringdotjar.funkin.InitScreen; +import me.stringdotjar.funkin.InitState; import me.stringdotjar.funkin.util.FunkinConstants; /** Launches the Android application. */ @@ -17,7 +17,7 @@ protected void onCreate(Bundle savedInstanceState) { FunkinConstants.WINDOW_TITLE, FunkinConstants.WINDOW_WIDTH, FunkinConstants.WINDOW_HEIGHT, - new InitScreen() + new InitState() ); AndroidApplicationConfiguration configuration = new AndroidApplicationConfiguration(); configuration.useImmersiveMode = true; // Recommended, but not required. diff --git a/assets/another_test.groovy b/assets/another_test.groovy index 243bdcb..e7bed07 100644 --- a/assets/another_test.groovy +++ b/assets/another_test.groovy @@ -32,7 +32,7 @@ class AnotherTestClass extends Script { sprite.setPosition(randomPosX, randomPosY) - Flixel.screen.add(sprite) + Flixel.state.add(sprite) } } diff --git a/assets/oml.groovy b/assets/oml.groovy new file mode 100644 index 0000000..3bcee2c --- /dev/null +++ b/assets/oml.groovy @@ -0,0 +1,15 @@ +import me.stringdotjar.flixelgdx.Flixel +import me.stringdotjar.polyverse.script.type.AnotherType + +class Sob extends AnotherType { + + Sob() { + super('Sob') + } + + @Override + void onWindowFocused() { + super.onWindowFocused() + Flixel.info("i hate [insert particular ethnicity here]") + } +} diff --git a/assets/test.groovy b/assets/test.groovy index 0a291d2..78e664b 100644 --- a/assets/test.groovy +++ b/assets/test.groovy @@ -2,7 +2,7 @@ import com.badlogic.gdx.Gdx import com.badlogic.gdx.Input import com.badlogic.gdx.graphics.Color import me.stringdotjar.flixelgdx.Flixel -import me.stringdotjar.flixelgdx.graphics.screen.FlixelScreen +import me.stringdotjar.flixelgdx.graphics.FlixelState import me.stringdotjar.flixelgdx.backend.FlixelPaths import me.stringdotjar.flixelgdx.graphics.sprite.FlixelSprite import me.stringdotjar.polyverse.script.type.SystemScript @@ -24,7 +24,7 @@ class TestScript extends SystemScript { super.onRender(delta) if (Gdx.input.isKeyJustPressed(Input.Keys.Q)) { - Flixel.setScreen(new TestScreen()) + Flixel.switchState(new TestState()) } } @@ -33,26 +33,34 @@ class TestScript extends SystemScript { super.onDispose() Flixel.info("TestClass", "Script has been disposed!") } + + @Override + void onWindowFocused() { + super.onWindowFocused() + Flixel.info("i like em young") + } } -class TestScreen extends FlixelScreen { +class TestState extends FlixelState { private FlixelSprite test @Override - void show() { - super.show() - - test = new FlixelSprite().loadGraphic(FlixelPaths.sharedImageAsset('NOTE_hold_assets')) - add(test) + void create() { + super.create() bgColor = new Color(0, 1, 0, 1) Flixel.playMusic('songs/darnell/Inst.ogg') + +// test.changeX(-30) + + test = new FlixelSprite().loadGraphic(FlixelPaths.sharedImageAsset('NOTE_hold_assets')) + add(test) } @Override - void render(float delta) { - super.render(delta) + void update(float delta) { + super.update(delta) } } diff --git a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/Flixel.java b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/Flixel.java index 615053f..59c8328 100644 --- a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/Flixel.java +++ b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/Flixel.java @@ -3,18 +3,20 @@ 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.graphics.screen.FlixelScreen; -import me.stringdotjar.flixelgdx.util.FlixelConstants; +import me.stringdotjar.flixelgdx.graphics.FlixelState; +import me.stringdotjar.flixelgdx.graphics.FlixelViewport; import me.stringdotjar.flixelgdx.signal.FlixelSignal; import me.stringdotjar.flixelgdx.signal.FlixelSignalData.MusicPlayedSignalData; -import me.stringdotjar.flixelgdx.signal.FlixelSignalData.RenderSignalData; -import me.stringdotjar.flixelgdx.signal.FlixelSignalData.ScreenSwitchSignalData; +import me.stringdotjar.flixelgdx.signal.FlixelSignalData.UpdateSignalData; +import me.stringdotjar.flixelgdx.signal.FlixelSignalData.StateSwitchSignalData; import me.stringdotjar.flixelgdx.signal.FlixelSignalData.SoundPlayedSignalData; +import me.stringdotjar.flixelgdx.util.FlixelConstants; import org.jetbrains.annotations.NotNull; import java.time.LocalDateTime; @@ -27,8 +29,8 @@ */ public final class Flixel { - /** The current {@code FlixelScreen} being displayed. */ - private static FlixelScreen screen; + /** The current {@code FlixelState} being displayed. */ + private static FlixelState state; /** The main audio object used to create, */ private static MiniAudio engine; @@ -61,7 +63,7 @@ public final class Flixel { */ public static void initialize(FlixelGame gameInstance) { if (initialized) { - throw new IllegalStateException("FNF:JE has already been initialized!"); + throw new IllegalStateException("FlixelGDX has already been initialized!"); } game = gameInstance; @@ -78,23 +80,24 @@ public static void initialize(FlixelGame gameInstance) { /** * Sets the current screen to the provided screen. * - * @param newScreen The new {@code FlixelScreen} to set as the current screen. + * @param newState The new {@code FlixelState} to set as the current screen. */ - public static void setScreen(FlixelScreen newScreen) { - Signals.preScreenSwitch.dispatch(new ScreenSwitchSignalData(newScreen)); + public static void switchState(FlixelState newState) { + Signals.preStateSwitch.dispatch(new StateSwitchSignalData(newState)); if (!initialized) { - throw new IllegalStateException("FNF:JE has not been initialized yet!"); + throw new IllegalStateException("Polyverse has not been initialized yet!"); } - if (newScreen == null) { - throw new IllegalArgumentException("Screen cannot be null!"); + if (newState == null) { + throw new IllegalArgumentException("New state cannot be null!"); } - if (Flixel.screen != null) { - Flixel.screen.hide(); - Flixel.screen.dispose(); + if (state != null) { + state.hide(); + state.dispose(); } - Flixel.screen = newScreen; - Flixel.screen.show(); - Signals.postScreenSwitch.dispatch(new ScreenSwitchSignalData(newScreen)); + game.resetViewports(); + state = newState; + state.create(); + Signals.postStateSwitch.dispatch(new StateSwitchSignalData(newState)); } /** @@ -109,8 +112,8 @@ public static void setScreen(FlixelScreen newScreen) { * } * * @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). + * 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) { @@ -128,9 +131,9 @@ public static MASound playSound(String path) { * Flixel.playSound(FlixelPaths.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 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. */ @@ -149,10 +152,10 @@ public static MASound playSound(String path, float volume) { * Flixel.playSound(FlixelPaths.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 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. */ @@ -172,13 +175,13 @@ public static MASound playSound(String path, float volume, boolean looping) { * Flixel.playSound(FlixelPaths.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 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 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) { @@ -199,13 +202,13 @@ public static MASound playSound(String path, float volume, boolean looping, MAGr * Flixel.playSound(FlixelPaths.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 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. */ @@ -231,8 +234,8 @@ public static MASound playSound(@NotNull String path, float volume, boolean loop * } * * @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). + * 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 void playMusic(String path) { playMusic(path, 1, true, false); @@ -249,9 +252,9 @@ public static void playMusic(String path) { * Flixel.playMusic(FlixelPaths.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 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 void playMusic(String path, float volume) { @@ -269,10 +272,10 @@ public static void playMusic(String path, float volume) { * Flixel.playMusic(FlixelPaths.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 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 void playMusic(String path, float volume, boolean looping) { @@ -292,11 +295,11 @@ public static void playMusic(String path, float volume, boolean looping) { * Flixel.playMusic(FlixelPaths.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 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 void playMusic(String path, float volume, boolean looping, boolean external) { @@ -314,9 +317,6 @@ public static void playMusic(String path, float volume, boolean looping, boolean /** * Sets the game master/global volume, which is automatically applied to all current sounds. * - *
(This is just a helper method for creating a faster version of {@code
- * Flixel.getAudioEngine().setMasterVolume(float)}).
- *
* @param volume The new master volume to set.
*/
public static void setMasterVolume(float volume) {
@@ -369,14 +369,18 @@ public static Stage getStage() {
return game.stage;
}
- public static FlixelScreen getScreen() {
- return screen;
+ public static FlixelState getState() {
+ return state;
}
public static MASound getMusic() {
return music;
}
+ public static Vector2 getWindowSize() {
+ return game.viewSize;
+ }
+
public static MiniAudio getAudioEngine() {
return engine;
}
@@ -393,10 +397,18 @@ public static MAGroup getSoundsGroup() {
return soundsGroup;
}
- public static float getDelta() {
+ public static float getElapsed() {
return Gdx.graphics.getDeltaTime();
}
+ public static FlixelViewport getViewport() {
+ return game.getViewport();
+ }
+
+ public static boolean isFullscreen() {
+ return Gdx.graphics.isFullscreen();
+ }
+
/**
* Contains all the global events that get dispatched when something happens in the game.
*
@@ -409,10 +421,12 @@ public static float getDelta() {
*/
public static final class Signals {
- public static final FlixelSignal
+ * Setting the group's position ({@link #setX}, {@link #setY}, {@link #setPosition}) moves all
+ * member sprites by the same delta, mirroring how HaxeFlixel propagates position changes
+ * (except in {@link RotationMode#WHEEL} mode where positions are set absolutely each frame).
+ * Sprites added to the group are automatically offset by the group's current position.
+ *
+ * Rotation behaviour is controlled by {@link #rotationMode}:
+ *
+ * Note that you cannot change the rotation of any specific sprite of the group when in this mode, as it
+ * is locked to the group's rotation.
+ */
+ WHEEL,
+
+ /**
+ * All sprites orbit around the group origin ({@link #x}, {@link #y}) as a rigid body,
+ * like a rotating camera or TV screen. When rotation changes, each sprite's position is
+ * rotated around the center by the delta and its individual rotation is also adjusted
+ * by the same amount.
+ */
+ ORBIT
+ }
+}
diff --git a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java
new file mode 100644
index 0000000..3b522fc
--- /dev/null
+++ b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/input/FlixelKey.java
@@ -0,0 +1,11 @@
+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/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java
index 84f010b..0156008 100644
--- a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java
+++ b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/signal/FlixelSignalData.java
@@ -1,7 +1,7 @@
package me.stringdotjar.flixelgdx.signal;
import games.rednblack.miniaudio.MASound;
-import me.stringdotjar.flixelgdx.graphics.screen.FlixelScreen;
+import me.stringdotjar.flixelgdx.graphics.FlixelState;
import me.stringdotjar.flixelgdx.Flixel;
/**
@@ -10,9 +10,9 @@
*/
public final class FlixelSignalData {
- public record RenderSignalData(float delta) {}
+ public record UpdateSignalData(float delta) {}
- public record ScreenSwitchSignalData(FlixelScreen screen) {}
+ public record StateSwitchSignalData(FlixelState screen) {}
public record SoundPlayedSignalData(MASound sound) {}
diff --git a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java
index 149dda7..3d4a1df 100644
--- a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java
+++ b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelConstants.java
@@ -42,4 +42,6 @@ public static final class AsciiCodes {
private AsciiCodes() {}
}
+
+ private FlixelConstants() {}
}
diff --git a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java
index 2c0ec5c..9fdc8b3 100644
--- a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java
+++ b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelReflectUtil.java
@@ -13,7 +13,9 @@
* Backend utility class for obtaining and manipulating fields on objects through the usage of Java
* reflection.
*/
-public class FlixelReflectUtil {
+public final class FlixelReflectUtil {
+
+ private FlixelReflectUtil() {}
/**
* Checks if a field exists on a given object.
diff --git a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java
index 4d8a48c..7f35c86 100644
--- a/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java
+++ b/flixelgdx/src/main/java/me/stringdotjar/flixelgdx/util/FlixelRuntimeUtil.java
@@ -1,5 +1,11 @@
package me.stringdotjar.flixelgdx.util;
+import me.stringdotjar.flixelgdx.Flixel;
+
+import javax.swing.JOptionPane;
+import java.awt.EventQueue;
+import java.lang.reflect.InvocationTargetException;
+
/**
* Utility class for handling operation related to the runtime environment, including OS detection,
* extracting runtime information, obtaining information from exceptions, and other related tasks.
@@ -52,5 +58,57 @@ public static String getFullExceptionMessage(Throwable exception) {
return messageBuilder.toString();
}
+ /**
+ * Displays a new general info alert window with the given title and message.
+ *
+ * @param title The title of the alert window.
+ * @param message The message content to display in the alert window.
+ */
+ public static void showInfoAlert(String title, Object message) {
+ showAlert(title, message, JOptionPane.INFORMATION_MESSAGE);
+ }
+
+ /**
+ * Displays a new warning alert window with the given title and message.
+ *
+ * @param title The title of the alert window.
+ * @param message The message content to display in the alert window.
+ */
+ public static void showWarningAlert(String title, Object message) {
+ showAlert(title, message, JOptionPane.WARNING_MESSAGE);
+ }
+
+ /**
+ * Displays a new error alert window with the given title and message.
+ *
+ * @param title The title of the alert window.
+ * @param message The message content to display in the alert window.
+ */
+ public static void showErrorAlert(String title, Object message) {
+ showAlert(title, message, JOptionPane.ERROR_MESSAGE);
+ }
+
+ /**
+ * Displays a new alert window with the given title, message, and type.
+ *
+ * @param title The title of the alert window.
+ * @param message The message content to display in the alert window.
+ * @param type The type of alert (e.g., JOptionPane.INFORMATION_MESSAGE).
+ */
+ public static 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) {
+ Flixel.error(FlixelConstants.System.LOG_TAG, "Failed to show alert message.", e);
+ }
+ }
+ }
+
private FlixelRuntimeUtil() {}
}
diff --git a/funkin/src/main/java/me/stringdotjar/funkin/FunkinGame.java b/funkin/src/main/java/me/stringdotjar/funkin/FunkinGame.java
index cfdce50..866df71 100644
--- a/funkin/src/main/java/me/stringdotjar/funkin/FunkinGame.java
+++ b/funkin/src/main/java/me/stringdotjar/funkin/FunkinGame.java
@@ -1,9 +1,10 @@
package me.stringdotjar.funkin;
+import com.badlogic.gdx.Input;
import me.stringdotjar.flixelgdx.Flixel;
import me.stringdotjar.flixelgdx.FlixelGame;
import me.stringdotjar.flixelgdx.backend.FlixelPaths;
-import me.stringdotjar.flixelgdx.graphics.screen.FlixelScreen;
+import me.stringdotjar.flixelgdx.graphics.FlixelState;
import me.stringdotjar.polyverse.Polyverse;
import me.stringdotjar.polyverse.script.type.Script;
import me.stringdotjar.polyverse.script.type.SystemScript;
@@ -15,7 +16,7 @@ public class FunkinGame extends FlixelGame {
private float lastVolume = 1.0f;
- public FunkinGame(String title, int width, int height, FlixelScreen initialScreen) {
+ public FunkinGame(String title, int width, int height, FlixelState initialScreen) {
super(title, width, height, initialScreen);
}
@@ -28,12 +29,18 @@ public void create() {
@Override
public void render() {
super.render();
- Polyverse.forAllScripts(script -> script.onRender(Flixel.getDelta()));
+
+ if (Flixel.keyJustPressed(Input.Keys.F11)) {
+ toggleFullscreen();
+ }
+
+ Polyverse.forAllScripts(script -> script.onRender(Flixel.getElapsed()));
}
@Override
public void dispose() {
super.dispose();
+ Flixel.info("Funkin", "Disposing scripts...");
Polyverse.forAllScripts(Script::onDispose);
}
@@ -41,7 +48,7 @@ public void dispose() {
public void onWindowFocused() {
super.onWindowFocused();
Flixel.setMasterVolume(lastVolume);
- Polyverse.forEachScript(SystemScript.class, SystemScript::onWindowFocused);
+ Polyverse.forEachScriptSuccessor(SystemScript.class, SystemScript::onWindowFocused);
}
@Override
@@ -49,7 +56,7 @@ public void onWindowUnfocused() {
super.onWindowUnfocused();
lastVolume = Flixel.getMasterVolume();
Flixel.setMasterVolume(0.008f);
- Polyverse.forEachScript(SystemScript.class, SystemScript::onWindowUnfocused);
+ Polyverse.forEachScriptSuccessor(SystemScript.class, SystemScript::onWindowUnfocused);
}
@Override
@@ -57,7 +64,7 @@ public void onWindowMinimized(boolean iconified) {
super.onWindowMinimized(iconified);
lastVolume = Flixel.getMasterVolume();
Flixel.setMasterVolume(0);
- Polyverse.forEachScript(SystemScript.class, script -> script.onWindowMinimized(iconified));
+ Polyverse.forEachScriptSuccessor(SystemScript.class, script -> script.onWindowMinimized(iconified));
}
private void configurePolyverse() {
@@ -67,5 +74,6 @@ private void configurePolyverse() {
Polyverse.registerScript(FlixelPaths.asset("test.groovy"));
Polyverse.registerScript(FlixelPaths.asset("another_test.groovy"));
+ Polyverse.registerScript(FlixelPaths.asset("oml.groovy"));
}
}
diff --git a/funkin/src/main/java/me/stringdotjar/funkin/InitScreen.java b/funkin/src/main/java/me/stringdotjar/funkin/InitScreen.java
deleted file mode 100644
index e90d619..0000000
--- a/funkin/src/main/java/me/stringdotjar/funkin/InitScreen.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package me.stringdotjar.funkin;
-
-import me.stringdotjar.flixelgdx.graphics.screen.FlixelScreen;
-import me.stringdotjar.flixelgdx.Flixel;
-import me.stringdotjar.funkin.menus.TitleScreen;
-
-public class InitScreen extends FlixelScreen {
-
- @Override
- public void show() {
- super.show();
- Flixel.setScreen(new TitleScreen());
- }
-}
diff --git a/funkin/src/main/java/me/stringdotjar/funkin/InitState.java b/funkin/src/main/java/me/stringdotjar/funkin/InitState.java
new file mode 100644
index 0000000..412adcc
--- /dev/null
+++ b/funkin/src/main/java/me/stringdotjar/funkin/InitState.java
@@ -0,0 +1,14 @@
+package me.stringdotjar.funkin;
+
+import me.stringdotjar.flixelgdx.graphics.FlixelState;
+import me.stringdotjar.flixelgdx.Flixel;
+import me.stringdotjar.funkin.menus.TitleState;
+
+public class InitState extends FlixelState {
+
+ @Override
+ public void create() {
+ super.create();
+ Flixel.switchState(new TitleState());
+ }
+}
diff --git a/funkin/src/main/java/me/stringdotjar/funkin/menus/TitleScreen.java b/funkin/src/main/java/me/stringdotjar/funkin/menus/TitleScreen.java
deleted file mode 100644
index 99c78e2..0000000
--- a/funkin/src/main/java/me/stringdotjar/funkin/menus/TitleScreen.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package me.stringdotjar.funkin.menus;
-
-import com.badlogic.gdx.Input;
-import games.rednblack.miniaudio.MASound;
-import me.stringdotjar.flixelgdx.Flixel;
-import me.stringdotjar.flixelgdx.backend.FlixelPaths;
-import me.stringdotjar.flixelgdx.graphics.screen.FlixelScreen;
-import me.stringdotjar.flixelgdx.graphics.sprite.FlixelSprite;
-import me.stringdotjar.flixelgdx.tween.FlixelEase;
-import me.stringdotjar.flixelgdx.tween.FlixelTween;
-import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenSettings;
-import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenType;
-
-public class TitleScreen extends FlixelScreen {
-
- private FlixelSprite logo;
-
- private FlixelTween tween;
- private MASound tickleFight;
-
- @Override
- public void show() {
- super.show();
-
- var t = FlixelPaths.sharedImageAsset("noteStrumline");
- var xml = FlixelPaths.shared("images/noteStrumline.xml");
- logo = new FlixelSprite().loadSparrowFrames(t, xml);
- logo.addAnimationByPrefix("test", "confirmDown", 24, false);
- add(logo);
-
- tickleFight = Flixel.playSound("shared/sounds/tickleFight.ogg");
-// Flixel.playMusic("preload/music/freakyMenu/freakyMenu.ogg", 0.5f);
-
- FlixelTweenSettings settings = new FlixelTweenSettings()
- .addGoal("x", 600)
- .addGoal("y", 40)
- .addGoal("rotation", 180)
- .setDuration(0.7f)
- .setEase(FlixelEase::circInOut)
- .setType(FlixelTweenType.PERSIST);
- tween = FlixelTween.tween(logo, settings, values -> {
- logo.setX(values.get("x"));
- logo.setY(values.get("y"));
- logo.setRotation(values.get("rotation"));
- }).stop();
- }
-
- @Override
- public void render(float elapsed) {
- super.render(elapsed);
-
- float speed = 500 * elapsed;
- if (Flixel.keyPressed(Input.Keys.W)) {
- logo.changeY(speed);
- }
- if (Flixel.keyPressed(Input.Keys.S)) {
- logo.changeY(-speed);
- }
- if (Flixel.keyPressed(Input.Keys.A)) {
- logo.changeX(-speed);
- }
- if (Flixel.keyPressed(Input.Keys.D)) {
- logo.changeX(speed);
- }
-
- if (Flixel.keyJustPressed(Input.Keys.SPACE)) {
- logo.playAnimation("test", true);
- }
-
- if (Flixel.keyJustPressed(Input.Keys.T)) {
- tween.start();
- }
-
- if (Flixel.keyJustPressed(Input.Keys.R)) {
- tween.reset();
- }
-
- if (Flixel.keyJustPressed(Input.Keys.Y)) {
- if (tween.paused) {
- tween.resume();
- } else {
- tween.pause();
- }
- }
-
- if (Flixel.keyJustPressed(Input.Keys.Z)) {
- tickleFight.play();
- }
- }
-}
diff --git a/funkin/src/main/java/me/stringdotjar/funkin/menus/TitleState.java b/funkin/src/main/java/me/stringdotjar/funkin/menus/TitleState.java
new file mode 100644
index 0000000..e02411f
--- /dev/null
+++ b/funkin/src/main/java/me/stringdotjar/funkin/menus/TitleState.java
@@ -0,0 +1,165 @@
+package me.stringdotjar.funkin.menus;
+
+import games.rednblack.miniaudio.MASound;
+import me.stringdotjar.flixelgdx.Flixel;
+import me.stringdotjar.flixelgdx.backend.FlixelPaths;
+import me.stringdotjar.flixelgdx.graphics.FlixelState;
+import me.stringdotjar.flixelgdx.graphics.sprite.FlixelSprite;
+import me.stringdotjar.flixelgdx.group.FlixelSpriteGroup;
+import me.stringdotjar.flixelgdx.input.FlixelKey;
+import me.stringdotjar.flixelgdx.tween.FlixelEase;
+import me.stringdotjar.flixelgdx.tween.FlixelTween;
+import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenSettings;
+import me.stringdotjar.flixelgdx.tween.settings.FlixelTweenType;
+
+public class TitleState extends FlixelState {
+
+ private FlixelSprite logo;
+
+ private FlixelSpriteGroup spriteGroup;
+ private FlixelSprite s1;
+ private FlixelSprite s2;
+ private FlixelSprite s3;
+ private FlixelSprite s4;
+
+ private FlixelTween tween;
+ private MASound tickleFight;
+
+ @Override
+ public void create() {
+ super.create();
+
+ var t = FlixelPaths.sharedImageAsset("noteStrumline");
+ var xml = FlixelPaths.shared("images/noteStrumline.xml");
+ logo = new FlixelSprite().loadSparrowFrames(t, xml);
+ logo.addAnimationByPrefix("test", "confirmDown", 24, false);
+ add(logo);
+
+ tickleFight = Flixel.playSound("shared/sounds/tickleFight.ogg");
+// Flixel.playMusic("preload/music/freakyMenu/freakyMenu.ogg", 0.5f);
+
+ spriteGroup = new FlixelSpriteGroup(0, 200f, 0f);
+ s1 = new FlixelSprite().loadGraphic(FlixelPaths.sharedImageAsset("transitionSwag/stickers-set-1/bfSticker1"));
+ s2 = new FlixelSprite().loadGraphic(FlixelPaths.sharedImageAsset("transitionSwag/stickers-set-1/bfSticker2"));
+ s3 = new FlixelSprite().loadGraphic(FlixelPaths.sharedImageAsset("transitionSwag/stickers-set-1/bfSticker3"));
+ s4 = new FlixelSprite().loadGraphic(FlixelPaths.sharedImageAsset("transitionSwag/stickers-set-1/gfSticker1"));
+ s1.setX(200);
+ s2.setX(-200);
+ s3.setX(200);
+ s4.setX(-200);
+ s1.setY(200);
+ s2.setY(200);
+ s3.setY(-200);
+ s4.setY(-200);
+ spriteGroup.add(s1);
+ spriteGroup.add(s2);
+ spriteGroup.add(s3);
+ spriteGroup.add(s4);
+ spriteGroup.setRotationMode(FlixelSpriteGroup.RotationMode.INDIVIDUAL);
+ add(spriteGroup);
+
+ FlixelTweenSettings settings = new FlixelTweenSettings()
+ .addGoal("x", 600)
+ .addGoal("y", 40)
+ .addGoal("rotation", 180)
+ .setDuration(0.7f)
+ .setEase(FlixelEase::circInOut)
+ .setType(FlixelTweenType.PERSIST);
+ tween = FlixelTween.tween(logo, settings, values -> {
+ logo.setX(values.get("x"));
+ logo.setY(values.get("y"));
+ logo.setRotation(values.get("rotation"));
+ }).stop();
+ }
+
+ @Override
+ public void update(float elapsed) {
+ super.update(elapsed);
+
+ float speed = 500 * elapsed;
+ // if (Flixel.keyPressed(FlixelKey.W)) {
+ // logo.changeY(speed);
+ // }
+ // if (Flixel.keyPressed(FlixelKey.S)) {
+ // logo.changeY(-speed);
+ // }
+ // if (Flixel.keyPressed(FlixelKey.A)) {
+ // logo.changeX(-speed);
+ // }
+ // if (Flixel.keyPressed(FlixelKey.D)) {
+ // logo.changeX(speed);
+ // }
+ if (Flixel.keyPressed(FlixelKey.W)) {
+ spriteGroup.changeY(speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.S)) {
+ spriteGroup.changeY(-speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.A)) {
+ spriteGroup.changeX(-speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.D)) {
+ spriteGroup.changeX(speed);
+ }
+
+ if (Flixel.keyPressed(FlixelKey.I)) {
+ s1.changeY(speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.K)) {
+ s1.changeY(-speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.J)) {
+ s1.changeX(-speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.L)) {
+ s1.changeX(speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.U)) {
+ s1.changeRotation(speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.O)) {
+ s1.changeRotation(-speed);
+ }
+
+ if (Flixel.keyJustPressed(FlixelKey.NUM_1)) {
+ spriteGroup.setRotationMode(FlixelSpriteGroup.RotationMode.INDIVIDUAL);
+ }
+ if (Flixel.keyJustPressed(FlixelKey.NUM_2)) {
+ spriteGroup.setRotationMode(FlixelSpriteGroup.RotationMode.WHEEL);
+ }
+ if (Flixel.keyJustPressed(FlixelKey.NUM_3)) {
+ spriteGroup.setRotationMode(FlixelSpriteGroup.RotationMode.ORBIT);
+ }
+
+ if (Flixel.keyPressed(FlixelKey.LEFT)) {
+ spriteGroup.changeRotation(speed);
+ }
+ if (Flixel.keyPressed(FlixelKey.RIGHT)) {
+ spriteGroup.changeRotation(-speed);
+ }
+
+ if (Flixel.keyJustPressed(FlixelKey.SPACE)) {
+ logo.playAnimation("test", true);
+ }
+
+ if (Flixel.keyJustPressed(FlixelKey.T)) {
+ tween.start();
+ }
+
+ if (Flixel.keyJustPressed(FlixelKey.R)) {
+ tween.reset();
+ }
+
+ if (Flixel.keyJustPressed(FlixelKey.Y)) {
+ if (tween.paused) {
+ tween.resume();
+ } else {
+ tween.pause();
+ }
+ }
+
+ if (Flixel.keyJustPressed(FlixelKey.Z)) {
+ tickleFight.play();
+ }
+ }
+}
diff --git a/funkin/src/main/java/me/stringdotjar/funkin/util/FunkinConstants.java b/funkin/src/main/java/me/stringdotjar/funkin/util/FunkinConstants.java
index 8fd5196..1981026 100644
--- a/funkin/src/main/java/me/stringdotjar/funkin/util/FunkinConstants.java
+++ b/funkin/src/main/java/me/stringdotjar/funkin/util/FunkinConstants.java
@@ -8,7 +8,7 @@ public final class FunkinConstants {
/**
* The default title for the game's window.
*/
- public static final String WINDOW_TITLE = "Friday Night Funkin': Java Edition";
+ public static final String WINDOW_TITLE = "Polyverse Funkin'";
/**
* How wide the window's viewport is in pixels. This also affects how wide the window is when
diff --git a/lwjgl3/src/main/java/me/stringdotjar/funkin/lwjgl3/Lwjgl3Launcher.java b/lwjgl3/src/main/java/me/stringdotjar/funkin/lwjgl3/Lwjgl3Launcher.java
index 7222c22..3ad634d 100644
--- a/lwjgl3/src/main/java/me/stringdotjar/funkin/lwjgl3/Lwjgl3Launcher.java
+++ b/lwjgl3/src/main/java/me/stringdotjar/funkin/lwjgl3/Lwjgl3Launcher.java
@@ -5,7 +5,7 @@
import com.badlogic.gdx.backends.lwjgl3.Lwjgl3WindowAdapter;
import me.stringdotjar.flixelgdx.Flixel;
import me.stringdotjar.funkin.FunkinGame;
-import me.stringdotjar.funkin.InitScreen;
+import me.stringdotjar.funkin.InitState;
import me.stringdotjar.funkin.util.FunkinConstants;
/** Launches the desktop (LWJGL3) application. */
@@ -23,7 +23,7 @@ private static void createApplication() {
FunkinConstants.WINDOW_TITLE,
FunkinConstants.WINDOW_WIDTH,
FunkinConstants.WINDOW_HEIGHT,
- new InitScreen()
+ new InitState()
);
Flixel.initialize(game); // This is VERY important to do before creating the application!
var size = game.getWindowSize();
diff --git a/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java b/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java
index a8383af..8e7942d 100644
--- a/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java
+++ b/polyverse/src/main/java/me/stringdotjar/polyverse/Polyverse.java
@@ -3,6 +3,7 @@
import com.badlogic.gdx.files.FileHandle;
import groovy.lang.GroovyClassLoader;
import me.stringdotjar.flixelgdx.Flixel;
+import me.stringdotjar.flixelgdx.util.FlixelRuntimeUtil;
import me.stringdotjar.polyverse.script.type.Script;
import java.util.ArrayList;
@@ -81,10 +82,7 @@ public static void registerScript(FileHandle handle) {
var typeScripts = scripts.get(mostSpecificType);
if (!typeScripts.contains(script)) {
typeScripts.add(script);
- Flixel.info(
- "Polyverse",
- "Registered Polyverse script \""
- + script.getClass().getSimpleName()
+ Flixel.info("Polyverse", "Registered Polyverse script \"" + script.getClass().getSimpleName()
+ "\" of script type \""
+ mostSpecificType.getSimpleName()
+ "\".");
@@ -93,12 +91,17 @@ public static void registerScript(FileHandle handle) {
script.onCreate();
}
} catch (Exception e) {
- Flixel.error("Polyverse", "Failed to load script: " + handle.path(), e);
+ StringBuilder errorWindowMessage = new StringBuilder();
+ errorWindowMessage.append("There was an uncaught exception for a script during compilation.\n");
+ errorWindowMessage.append("Location: ").append(handle.path()).append("\n");
+ errorWindowMessage.append("Exception: ").append(e);
+ Flixel.error("Polyverse", "Failed to compile script: " + handle.path(), e);
+ FlixelRuntimeUtil.showErrorAlert("Polyverse Script Exception", errorWindowMessage);
}
}
/**
- * Executes an action for each script of a certain type, with error handling.
+ * Executes a function that pertains to a specific type for each script.
*
* @param type The class type of scripts to iterate over.
* @param action The action to perform on each script.
@@ -107,6 +110,21 @@ public static
+ *
+ */
+public class FlixelSpriteGroup extends FlixelGroup
+ *
+ */
+ private float rotation;
+
+ private static final Random RANDOM = new Random();
+
+ /**
+ * Creates a new FlixelSpriteGroup with default parameters.
+ */
+ public FlixelSpriteGroup() {
+ this(0, 100f, 0f);
+ }
+
+ /**
+ * Creates a new FlixelSpriteGroup with the given max size.
+ *
+ * @param maxSize Maximum members allowed; 0 for unlimited.
+ */
+ public FlixelSpriteGroup(int maxSize) {
+ this(maxSize, 100f, 0f);
+ }
+
+ /**
+ * Creates a new FlixelSpriteGroup with the given parameters.
+ *
+ * @param maxSize Maximum members allowed; 0 for unlimited.
+ * @param rotationRadius Distance of each sprite from the center when in {@link RotationMode#WHEEL}.
+ * @param rotation Initial rotation in degrees.
+ */
+ public FlixelSpriteGroup(int maxSize, float rotationRadius, float rotation) {
+ super();
+ this.maxSize = Math.max(0, maxSize);
+ this.rotationRadius = rotationRadius;
+ this.rotation = rotation;
+ }
+
+ public float getX() {
+ return x;
+ }
+
+ /**
+ * Sets the group's X position. Every member sprite is moved by the delta ({@code newX - oldX}),
+ * unless the rotation mode is {@link RotationMode#WHEEL} (positions are set absolutely each frame).
+ */
+ public void setX(float x) {
+ float dx = x - this.x;
+ this.x = x;
+
+ if (rotationMode != RotationMode.WHEEL) {
+ transformMembersX(dx);
+ }
+ }
+
+ /**
+ * Adds the given amount to the group's X position. Equivalent to {@code setX(getX() + x)}.
+ */
+ public void changeX(float x) {
+ setX(this.x + x);
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ /**
+ * Sets the group's Y position. Every member sprite is moved by the delta ({@code newY - oldY}),
+ * unless the rotation mode is {@link RotationMode#WHEEL} (positions are set absolutely each frame).
+ */
+ public void setY(float y) {
+ float dy = y - this.y;
+ this.y = y;
+
+ if (rotationMode != RotationMode.WHEEL) {
+ transformMembersY(dy);
+ }
+ }
+
+ /**
+ * Adds the given amount to the group's Y position. Equivalent to {@code setY(getY() + y)}.
+ */
+ public void changeY(float y) {
+ setY(this.y + y);
+ }
+
+ /**
+ * Sets both X and Y in a single call, applying the deltas to all members in one pass
+ * to avoid iterating twice.
+ */
+ public void setPosition(float x, float y) {
+ float dx = x - this.x;
+ float dy = y - this.y;
+ this.x = x;
+ this.y = y;
+
+ if (rotationMode != RotationMode.WHEEL) {
+ transformMembersPosition(dx, dy);
+ }
+ }
+
+
+ /**
+ * Returns the current rotation mode.
+ */
+ public RotationMode getRotationMode() {
+ return rotationMode;
+ }
+
+ /**
+ * Sets the rotation mode. See {@link RotationMode} for the available options.
+ */
+ public void setRotationMode(RotationMode rotationMode) {
+ this.rotationMode = rotationMode;
+ }
+
+ public float getRotationRadius() {
+ return rotationRadius;
+ }
+
+ public void setRotationRadius(float rotationRadius) {
+ this.rotationRadius = rotationRadius;
+ }
+
+ /**
+ * Returns the group's rotation in degrees.
+ */
+ public float getRotation() {
+ return rotation;
+ }
+
+ /**
+ * Sets the group's rotation in degrees. The behaviour depends on {@link #rotationMode}:
+ *
+ *
+ */
+ public void setRotation(float rotation) {
+ float delta = rotation - this.rotation;
+ this.rotation = rotation;
+
+ switch (rotationMode) {
+ case INDIVIDUAL:
+ transformMembersIndividualRotation(delta);
+ break;
+ case ORBIT:
+ orbitMembersAroundCenter(delta);
+ break;
+ case WHEEL:
+ break;
+ }
+ }
+
+ /**
+ * Adds the given amount to the group's rotation. Equivalent to
+ * {@code setRotation(getRotation() + degrees)}.
+ */
+ public void changeRotation(float degrees) {
+ setRotation(this.rotation + degrees);
+ }
+
+ /**
+ * Number of members. Prefer this over {@code members.size} for consistency.
+ */
+ public int getLength() {
+ return members.size;
+ }
+
+ public int getMaxSize() {
+ return maxSize;
+ }
+
+ public void setMaxSize(int maxSize) {
+ this.maxSize = Math.max(0, maxSize);
+ }
+
+
+ /**
+ * Adds a sprite to the group. The sprite's position is automatically offset by
+ * the group's current ({@link #x}, {@link #y}), matching HaxeFlixel's {@code preAdd} behaviour.
+ */
+ @Override
+ public void add(FlixelSprite sprite) {
+ if (sprite == null) {
+ return;
+ }
+
+ if (maxSize > 0 && members.size >= maxSize) {
+ return;
+ }
+
+ preAdd(sprite);
+ super.add(sprite);
+ }
+
+ /**
+ * Adds a sprite and returns it for chaining.
+ */
+ public FlixelSprite addAndReturn(FlixelSprite sprite) {
+ add(sprite);
+ return sprite;
+ }
+
+ /**
+ * Inserts a new sprite at the given index. The sprite is offset by the group's position.
+ */
+ 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, restoring it to "local" coordinates, matching HaxeFlixel's {@code remove} behaviour.
+ */
+ @Override
+ public void remove(FlixelSprite sprite) {
+ if (sprite == null) {
+ return;
+ }
+
+ super.remove(sprite);
+ sprite.setX(sprite.getX() - x);
+ sprite.setY(sprite.getY() - y);
+ }
+
+ /**
+ * Replaces an existing sprite with a new one. The new sprite is offset by the group's position.
+ *
+ * @return the new 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;
+ }
+
+ /**
+ * Sorts members using the given comparator.
+ */
+ public void sort(Comparator