Compare commits
15 Commits
65880853ac
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| a65cd35cec | |||
| db253e959b | |||
| 2c77f665cb | |||
| 88dbbb4be7 | |||
| dc4abdd578 | |||
| 7800efcf20 | |||
| d045243c7a | |||
| 5eb0554c62 | |||
| bc3dee458c | |||
| b42cd6d263 | |||
| d358ddd042 | |||
| 30766b9235 | |||
| 578ecff8c0 | |||
| 8ce166365a | |||
| 57aecaf002 |
@ -12,6 +12,7 @@ import java.util.ArrayList;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
import org.vibecoders.moongazer.managers.Audio;
|
||||
import org.vibecoders.moongazer.scenes.*;
|
||||
|
||||
public class Game extends ApplicationAdapter {
|
||||
@ -28,6 +29,7 @@ public class Game extends ApplicationAdapter {
|
||||
public Scene mainMenuScene;
|
||||
public Scene settingsScene;
|
||||
public ArrayList<Scene> gameScenes;
|
||||
private boolean usingCustomScene = false;
|
||||
|
||||
@Override
|
||||
public void create() {
|
||||
@ -59,6 +61,9 @@ public class Game extends ApplicationAdapter {
|
||||
stage.draw();
|
||||
return;
|
||||
}
|
||||
|
||||
// Only use state-based scene switching if not using custom scene
|
||||
if (!usingCustomScene) {
|
||||
switch (this.state) {
|
||||
case INTRO:
|
||||
currentScene = introScene;
|
||||
@ -75,18 +80,23 @@ public class Game extends ApplicationAdapter {
|
||||
default:
|
||||
log.warn("Unknown state: {}", state);
|
||||
}
|
||||
}
|
||||
|
||||
for (var scene : gameScenes) {
|
||||
// log.trace("Checking scene visibility: {}", scene.getClass().getSimpleName());
|
||||
if (scene != currentScene && scene.root.isVisible()) {
|
||||
if (scene != currentScene && scene.root != null && scene.root.isVisible()) {
|
||||
log.trace("Hiding scene: {}", scene.getClass().getSimpleName());
|
||||
scene.root.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
// Only render if currentScene is not null
|
||||
if (currentScene != null) {
|
||||
batch.begin();
|
||||
currentScene.render(batch);
|
||||
batch.end();
|
||||
}
|
||||
|
||||
// Handle stage drawing for UI elements
|
||||
stage.act(Gdx.graphics.getDeltaTime());
|
||||
stage.draw();
|
||||
@ -94,11 +104,99 @@ public class Game extends ApplicationAdapter {
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
// Save settings
|
||||
Settings.saveSettings();
|
||||
// Dispose scenes
|
||||
introScene.dispose();
|
||||
mainMenuScene.dispose();
|
||||
// Dispose resources
|
||||
Audio.dispose();
|
||||
Assets.dispose();
|
||||
// Dispose batch and stage
|
||||
batch.dispose();
|
||||
stage.dispose();
|
||||
log.debug("Resources disposed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current scene directly (for VN and other non-state-based scenes).
|
||||
*
|
||||
* @param scene the scene to switch to
|
||||
*/
|
||||
public void setScene(Scene scene) {
|
||||
if (currentScene != null && currentScene.root != null) {
|
||||
currentScene.root.setVisible(false);
|
||||
}
|
||||
currentScene = scene;
|
||||
usingCustomScene = true;
|
||||
if (scene.root != null) {
|
||||
scene.root.setVisible(true);
|
||||
if (!gameScenes.contains(scene)) {
|
||||
gameScenes.add(scene);
|
||||
if (scene.root.getStage() == null) {
|
||||
stage.addActor(scene.root);
|
||||
}
|
||||
}
|
||||
}
|
||||
log.debug("Scene switched to: {}", scene.getClass().getSimpleName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the game state and returns to state-based scene switching.
|
||||
*
|
||||
* @param newState the new game state
|
||||
*/
|
||||
public void setState(State newState) {
|
||||
this.state = newState;
|
||||
this.usingCustomScene = false;
|
||||
// Reset input processor to main stage
|
||||
Gdx.input.setInputProcessor(stage);
|
||||
log.debug("State changed to: {}", newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current scene and state (used by transitions).
|
||||
*
|
||||
* @param scene the scene to set as current
|
||||
* @param newState the new state
|
||||
*/
|
||||
public void setCurrentSceneAndState(Scene scene, State newState) {
|
||||
this.currentScene = scene;
|
||||
this.state = newState;
|
||||
this.usingCustomScene = false;
|
||||
if (scene.root != null) {
|
||||
scene.root.setVisible(true);
|
||||
}
|
||||
Gdx.input.setInputProcessor(stage);
|
||||
log.debug("Current scene set to: {}, state: {}", scene.getClass().getSimpleName(), newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns to main menu from a custom scene.
|
||||
*/
|
||||
public void returnToMainMenu() {
|
||||
// Create transition from DialogueScene to MainMenu instead of direct switch
|
||||
if (currentScene != null && usingCustomScene && currentScene instanceof DialogueScene) {
|
||||
log.debug("Creating transition from DialogueScene to MainMenu");
|
||||
DialogueScene dialogueScene = (DialogueScene) currentScene;
|
||||
dialogueScene.enterTransition(); // Mark as entering transition to stop rendering
|
||||
transition = new DialogueToMenuTransition(this, dialogueScene, mainMenuScene, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback for other custom scenes
|
||||
if (currentScene != null && usingCustomScene) {
|
||||
log.debug("Disposing custom scene: {}", currentScene.getClass().getSimpleName());
|
||||
currentScene.dispose();
|
||||
currentScene = null;
|
||||
}
|
||||
|
||||
setState(State.MAIN_MENU);
|
||||
currentScene = mainMenuScene;
|
||||
if (mainMenuScene.root != null) {
|
||||
mainMenuScene.root.setVisible(true);
|
||||
}
|
||||
Gdx.input.setInputProcessor(stage);
|
||||
log.debug("Returned to main menu, state={}, usingCustomScene={}", state, usingCustomScene);
|
||||
}
|
||||
}
|
||||
65
app/src/main/java/org/vibecoders/moongazer/Settings.java
Normal file
@ -0,0 +1,65 @@
|
||||
package org.vibecoders.moongazer;
|
||||
|
||||
import com.badlogic.gdx.Input;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class Settings {
|
||||
private static final Logger log = org.slf4j.LoggerFactory.getLogger(Settings.class);
|
||||
private static float masterVolume = 1.0f;
|
||||
private static float musicVolume = 1.0f;
|
||||
private static float sfxVolume = 1.0f;
|
||||
public static HashMap<String, Integer> keybinds = new HashMap<>() {
|
||||
{
|
||||
put("p1_left", Input.Keys.LEFT);
|
||||
put("p1_right", Input.Keys.RIGHT);
|
||||
put("p2_left", Input.Keys.A);
|
||||
put("p2_right", Input.Keys.D);
|
||||
}
|
||||
};
|
||||
|
||||
public static int getKeybind(String action) {
|
||||
return keybinds.getOrDefault(action, Input.Keys.UNKNOWN);
|
||||
}
|
||||
|
||||
public static float getMasterVolume() {
|
||||
return masterVolume;
|
||||
}
|
||||
|
||||
public static float getMusicVolume() {
|
||||
return musicVolume;
|
||||
}
|
||||
|
||||
public static float getSfxVolume() {
|
||||
return sfxVolume;
|
||||
}
|
||||
|
||||
public static void setKeybind(String action, int keycode) {
|
||||
keybinds.put(action, keycode);
|
||||
}
|
||||
|
||||
public static void setMasterVolume(float volume) {
|
||||
masterVolume = Math.max(0, Math.min(1, volume));
|
||||
}
|
||||
|
||||
public static void setMusicVolume(float volume) {
|
||||
musicVolume = Math.max(0, Math.min(1, volume));
|
||||
}
|
||||
|
||||
public static void setSfxVolume(float volume) {
|
||||
sfxVolume = Math.max(0, Math.min(1, volume));
|
||||
}
|
||||
|
||||
public static void saveSettings() {
|
||||
log.debug("Saving settings: Master Volume = {}, Music Volume = {}, SFX Volume = {}", masterVolume, musicVolume, sfxVolume);
|
||||
for (java.util.Map.Entry<String, Integer> entry : keybinds.entrySet()) {
|
||||
log.debug("Keybind: {} = {}", entry.getKey(), Input.Keys.toString(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadSettings() {
|
||||
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,7 @@ public class Assets {
|
||||
private static final HashMap<String, FileHandle> loadedFiles = new HashMap<>();
|
||||
private static boolean startLoadAll = false;
|
||||
private static boolean loadedAll = false;
|
||||
private static Thread loadingThread = null;
|
||||
private static Texture textureWhite;
|
||||
private static Texture textureBlack;
|
||||
|
||||
@ -117,8 +118,23 @@ public class Assets {
|
||||
assetManager.load("textures/ui/UI_Icon_Setting.png", Texture.class);
|
||||
assetManager.load("textures/ui/ImgReShaSoundOn.png", Texture.class);
|
||||
assetManager.load("textures/ui/UI_Gcg_Icon_Close.png", Texture.class);
|
||||
assetManager.load("textures/ui/arrow-button-left.png", Texture.class);
|
||||
assetManager.load("textures/ui/arrow-button-right.png", Texture.class);
|
||||
assetManager.load("textures/ui/close.png", Texture.class);
|
||||
assetManager.load("textures/ui/close_hover.png", Texture.class);
|
||||
assetManager.load("textures/ui/close_clicked.png", Texture.class);
|
||||
assetManager.load("textures/ui/UI_SliderKnob.png", Texture.class);
|
||||
assetManager.load("textures/ui/UI_SliderBg.png", Texture.class);
|
||||
assetManager.load("textures/ui/UI_SliderBg2.png", Texture.class);
|
||||
// VN scene textures
|
||||
assetManager.load("textures/vn_scene/char_base.png", Texture.class);
|
||||
assetManager.load("textures/vn_scene/separator.png", Texture.class);
|
||||
// "Load" unsupported file types as FileHandle
|
||||
loadingThread = new Thread(() -> {
|
||||
loadAny("videos/main_menu_background.webm");
|
||||
loadAny("audio/I Once Praised the Day.mp3");
|
||||
});
|
||||
loadingThread.start();
|
||||
}
|
||||
|
||||
public static boolean isLoadedAll() {
|
||||
@ -131,6 +147,14 @@ public class Assets {
|
||||
|
||||
public static void waitUntilLoaded() {
|
||||
assetManager.finishLoading();
|
||||
if (loadingThread != null) {
|
||||
try {
|
||||
loadingThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
loadingThread = null;
|
||||
}
|
||||
if (startLoadAll) {
|
||||
loadedAll = true;
|
||||
}
|
||||
|
||||
@ -0,0 +1,53 @@
|
||||
package org.vibecoders.moongazer.managers;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.vibecoders.moongazer.Settings;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.audio.Music;
|
||||
import com.badlogic.gdx.files.FileHandle;
|
||||
|
||||
public class Audio {
|
||||
private static final Logger log = org.slf4j.LoggerFactory.getLogger(Audio.class);
|
||||
private static boolean initialized = false;
|
||||
private static Music menuMusic = null;
|
||||
|
||||
public static void init() {
|
||||
if (initialized) {
|
||||
return;
|
||||
}
|
||||
initialized = true;
|
||||
menuMusic = Gdx.audio.newMusic(Assets.getAsset("audio/I Once Praised the Day.mp3", FileHandle.class));
|
||||
log.info("Audio manager initialized");
|
||||
}
|
||||
|
||||
public static void menuMusicSetVolume() {
|
||||
menuMusic.setVolume(Settings.getMusicVolume() * Settings.getMasterVolume());
|
||||
}
|
||||
|
||||
public static void musicSetVolume() {
|
||||
menuMusicSetVolume();
|
||||
}
|
||||
|
||||
public static void menuMusicPlay() {
|
||||
if (!menuMusic.isPlaying()) {
|
||||
menuMusic.setLooping(true);
|
||||
menuMusic.play();
|
||||
}
|
||||
}
|
||||
|
||||
public static void menuMusicStop() {
|
||||
if (menuMusic.isPlaying()) {
|
||||
menuMusic.stop();
|
||||
}
|
||||
}
|
||||
|
||||
public static void dispose() {
|
||||
if (menuMusic != null) {
|
||||
menuMusic.dispose();
|
||||
menuMusic = null;
|
||||
}
|
||||
initialized = false;
|
||||
log.info("Audio manager disposed");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
package org.vibecoders.moongazer.scenes;
|
||||
|
||||
import static org.vibecoders.moongazer.Constants.*;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
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.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.Stage;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.utils.viewport.ScreenViewport;
|
||||
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
import org.vibecoders.moongazer.vn.CharacterActor;
|
||||
import org.vibecoders.moongazer.vn.ChoiceBox;
|
||||
import org.vibecoders.moongazer.vn.DialogueBoxTransparent;
|
||||
|
||||
public class DialogueScene extends Scene {
|
||||
private Stage stage;
|
||||
private final CharacterActor character;
|
||||
private final DialogueBoxTransparent dialogue;
|
||||
private ChoiceBox choice;
|
||||
private int step = 0;
|
||||
private float alpha = 1f;
|
||||
private boolean isActive = true;
|
||||
private boolean inTransition = false;
|
||||
|
||||
public DialogueScene(Game game) {
|
||||
super(game);
|
||||
stage = new Stage(new ScreenViewport());
|
||||
Gdx.input.setInputProcessor(stage);
|
||||
|
||||
Texture bgTexture = Assets.getAsset("textures/main_menu/background.png", Texture.class);
|
||||
Image background = new Image(bgTexture);
|
||||
background.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
background.setColor(0.5f, 0.5f, 0.5f, 1f);
|
||||
stage.addActor(background);
|
||||
|
||||
Pixmap overlayPixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
|
||||
overlayPixmap.setColor(0, 0, 0, 0.4f);
|
||||
overlayPixmap.fill();
|
||||
Texture overlayTexture = new Texture(overlayPixmap);
|
||||
overlayPixmap.dispose();
|
||||
Image overlay = new Image(overlayTexture);
|
||||
overlay.setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
stage.addActor(overlay);
|
||||
|
||||
TextureRegion charBase = loadTexture("textures/vn_scene/char_base.png");
|
||||
character = new CharacterActor(charBase);
|
||||
float charX = (WINDOW_WIDTH - character.getWidth()) / 2f;
|
||||
float charY = (WINDOW_HEIGHT - character.getHeight()) / 2f + 100;
|
||||
character.setPosition(charX, charY);
|
||||
stage.addActor(character);
|
||||
|
||||
TextureRegion dialogBg = createDialogBackground();
|
||||
TextureRegion separator = loadTexture("textures/vn_scene/separator.png");
|
||||
|
||||
var font = Assets.getFont("ui", 20);
|
||||
dialogue = new DialogueBoxTransparent(font, dialogBg, separator, WINDOW_WIDTH - 100);
|
||||
dialogue.setPosition(50, 20);
|
||||
stage.addActor(dialogue);
|
||||
|
||||
showStep(0);
|
||||
|
||||
stage.addListener(new InputListener() {
|
||||
@Override
|
||||
public boolean keyDown(InputEvent e, int keycode) {
|
||||
if (choice == null) {
|
||||
nextOrSkip();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean touchDown(InputEvent e, float x, float y, int pointer, int button) {
|
||||
if (choice == null) {
|
||||
nextOrSkip();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TextureRegion loadTexture(String path) {
|
||||
try {
|
||||
Texture tex = Assets.getAsset(path, Texture.class);
|
||||
return new TextureRegion(tex);
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to load texture: {}, creating placeholder", path);
|
||||
Pixmap pixmap = new Pixmap(100, 100, Pixmap.Format.RGBA8888);
|
||||
pixmap.setColor(Color.GRAY);
|
||||
pixmap.fill();
|
||||
Texture tex = new Texture(pixmap);
|
||||
pixmap.dispose();
|
||||
return new TextureRegion(tex);
|
||||
}
|
||||
}
|
||||
|
||||
private TextureRegion createDialogBackground() {
|
||||
Pixmap pixmap = new Pixmap(1, 1, Pixmap.Format.RGBA8888);
|
||||
pixmap.setColor(0, 0, 0, 0.7f);
|
||||
pixmap.fill();
|
||||
Texture tex = new Texture(pixmap);
|
||||
pixmap.dispose();
|
||||
return new TextureRegion(tex);
|
||||
}
|
||||
|
||||
private void nextOrSkip() {
|
||||
if (!dialogue.isDone()) {
|
||||
dialogue.skip();
|
||||
return;
|
||||
}
|
||||
showStep(++step);
|
||||
}
|
||||
|
||||
private void showStep(int s) {
|
||||
if (choice != null) {
|
||||
choice.remove();
|
||||
choice = null;
|
||||
}
|
||||
|
||||
switch (s) {
|
||||
case 0:
|
||||
dialogue.setDialogue("Iuno", "Hmph. Apologies, but I need my rest...");
|
||||
break;
|
||||
case 1:
|
||||
var font = Assets.getFont("ui", 18);
|
||||
choice = new ChoiceBox(font, new String[]{"New game", "Back to Main Menu"}, idx -> {
|
||||
if (idx == 0) {
|
||||
dialogue.setDialogue("Iuno", "Toi yeu tunxd...");
|
||||
step = 2;
|
||||
} else {
|
||||
game.returnToMainMenu();
|
||||
}
|
||||
});
|
||||
choice.setPosition(WINDOW_WIDTH - 260, WINDOW_HEIGHT / 2);
|
||||
stage.addActor(choice);
|
||||
break;
|
||||
case 2:
|
||||
game.returnToMainMenu();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void setAlpha(float alpha) {
|
||||
this.alpha = alpha;
|
||||
if (stage != null && stage.getRoot() != null) {
|
||||
stage.getRoot().setColor(1, 1, 1, alpha);
|
||||
}
|
||||
}
|
||||
|
||||
public void enterTransition() {
|
||||
inTransition = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
if (inTransition || !isActive || stage == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
stage.act(Gdx.graphics.getDeltaTime());
|
||||
stage.draw();
|
||||
} catch (Exception e) {
|
||||
isActive = false;
|
||||
log.warn("Error rendering DialogueScene, marking as inactive", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
if (stage != null) {
|
||||
stage.dispose();
|
||||
stage = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package org.vibecoders.moongazer.scenes;
|
||||
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.State;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
public class DialogueToMenuTransition extends Transition {
|
||||
private final DialogueScene from;
|
||||
private final Scene to;
|
||||
private float totalTime = 0f;
|
||||
private final long duration;
|
||||
|
||||
public DialogueToMenuTransition(Game game, DialogueScene from, Scene to, long duration) {
|
||||
super(game, from, to, State.MAIN_MENU, duration);
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
totalTime += Gdx.graphics.getDeltaTime();
|
||||
float progress = totalTime / (((float) duration) / 1000);
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
game.transition = null;
|
||||
from.dispose();
|
||||
game.setCurrentSceneAndState(to, State.MAIN_MENU);
|
||||
return;
|
||||
}
|
||||
|
||||
float fromOpacity = 1 - progress;
|
||||
from.setAlpha(fromOpacity);
|
||||
from.render(batch);
|
||||
|
||||
float toOpacity = progress;
|
||||
if (to.root != null) {
|
||||
to.root.setVisible(true);
|
||||
to.root.setColor(1, 1, 1, toOpacity);
|
||||
}
|
||||
batch.setColor(1, 1, 1, toOpacity);
|
||||
to.render(batch);
|
||||
|
||||
batch.setColor(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package org.vibecoders.moongazer.scenes;
|
||||
|
||||
import org.vibecoders.moongazer.Game;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
public class DialogueTransition extends Transition {
|
||||
private final Scene from;
|
||||
private final DialogueScene to;
|
||||
private float totalTime = 0f;
|
||||
private final long duration;
|
||||
|
||||
public DialogueTransition(Game game, Scene from, DialogueScene to, long duration) {
|
||||
super(game, from, to, null, duration);
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.duration = duration;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
totalTime += Gdx.graphics.getDeltaTime();
|
||||
float progress = totalTime / (((float) duration) / 1000);
|
||||
|
||||
if (progress >= 1.0f) {
|
||||
game.transition = null;
|
||||
to.setAlpha(1f);
|
||||
game.setScene(to);
|
||||
return;
|
||||
}
|
||||
|
||||
float fromOpacity = 1 - progress;
|
||||
if (from.root != null) {
|
||||
from.root.setVisible(true);
|
||||
from.root.setColor(1, 1, 1, fromOpacity);
|
||||
}
|
||||
batch.setColor(1, 1, 1, fromOpacity);
|
||||
from.render(batch);
|
||||
|
||||
to.setAlpha(progress);
|
||||
batch.setColor(1, 1, 1, 1);
|
||||
to.render(batch);
|
||||
|
||||
batch.setColor(1, 1, 1, 1);
|
||||
}
|
||||
}
|
||||
@ -5,7 +5,9 @@ import static org.vibecoders.moongazer.Constants.*;
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.State;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
import org.vibecoders.moongazer.managers.Audio;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
@ -17,8 +19,8 @@ import com.badlogic.gdx.utils.ScreenUtils;
|
||||
public class Intro extends Scene {
|
||||
private Texture logo;
|
||||
private Game game;
|
||||
private long startTime;
|
||||
private long endTime = 0;
|
||||
private float totalTime = 0;
|
||||
private boolean end = false;
|
||||
|
||||
/**
|
||||
* Initializes the intro scene, starts loading assets.
|
||||
@ -26,23 +28,27 @@ public class Intro extends Scene {
|
||||
public Intro(Game game) {
|
||||
this.game = game;
|
||||
logo = Assets.getAsset("icons/logo.png", Texture.class);
|
||||
startTime = System.currentTimeMillis() + 500;
|
||||
log.info("Starting to load all remaining assets...");
|
||||
Assets.loadAll();
|
||||
Audio.init();
|
||||
Audio.musicSetVolume();
|
||||
// Create scenes
|
||||
game.mainMenuScene = new MainMenu(game);
|
||||
game.settingsScene = new Settings(game);
|
||||
game.settingsScene = new SettingsScene(game);
|
||||
game.gameScenes.add(game.mainMenuScene);
|
||||
game.gameScenes.add(game.settingsScene);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the intro scene.
|
||||
*
|
||||
* @param batch The SpriteBatch to draw with.
|
||||
*/
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
if (System.currentTimeMillis() > endTime + 2000 && endTime != 0) {
|
||||
totalTime += Gdx.graphics.getDeltaTime();
|
||||
log.trace("Intro total time: {}", totalTime);
|
||||
if (totalTime > 4f) {
|
||||
if (game.transition == null) {
|
||||
Assets.waitUntilLoaded();
|
||||
log.info("All assets loaded successfully.");
|
||||
@ -52,13 +58,14 @@ public class Intro extends Scene {
|
||||
return;
|
||||
}
|
||||
ScreenUtils.clear(Color.BLACK);
|
||||
// log.debug("Rendering logo at position: ({}, {})", WINDOW_WIDTH / 2 - logo.getWidth() / 4, WINDOW_HEIGHT / 2 - logo.getHeight() / 4);
|
||||
var currentOpacity = (float) (System.currentTimeMillis() - startTime) / 1000;
|
||||
// log.debug("Rendering logo at position: ({}, {})", WINDOW_WIDTH / 2 -
|
||||
// logo.getWidth() / 4, WINDOW_HEIGHT / 2 - logo.getHeight() / 4);
|
||||
var currentOpacity = totalTime;
|
||||
if (currentOpacity > 1) {
|
||||
if (endTime == 0) {
|
||||
endTime = System.currentTimeMillis() + 2000;
|
||||
if (!end) {
|
||||
end = true;
|
||||
}
|
||||
currentOpacity = 1 - ((float) (System.currentTimeMillis() - endTime) / 1000);
|
||||
currentOpacity = 4f - totalTime;
|
||||
}
|
||||
// Multiply with any externally applied alpha (e.g., Transition)
|
||||
float externalAlpha = batch.getColor().a;
|
||||
|
||||
@ -9,6 +9,7 @@ import java.util.Map;
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.State;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
import org.vibecoders.moongazer.managers.Audio;
|
||||
import org.vibecoders.moongazer.ui.UITextButton;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
@ -18,6 +19,7 @@ import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.utils.TimeUtils;
|
||||
import com.badlogic.gdx.video.VideoPlayer;
|
||||
import com.badlogic.gdx.video.VideoPlayerCreator;
|
||||
|
||||
@ -61,15 +63,17 @@ public class MainMenu extends Scene {
|
||||
var font = Assets.getFont("ui", 24);
|
||||
UITextButton playButton = new UITextButton("Play", font);
|
||||
UITextButton loadButton = new UITextButton("Load", font);
|
||||
UITextButton leaderboardButton = new UITextButton("Leaderboard", font);
|
||||
UITextButton settingsButton = new UITextButton("Settings", font);
|
||||
UITextButton exitButton = new UITextButton("Exit", font);
|
||||
buttons = new UITextButton[] { playButton, loadButton, settingsButton, exitButton };
|
||||
buttons = new UITextButton[] { playButton, loadButton, leaderboardButton, settingsButton, exitButton };
|
||||
|
||||
int buttonWidth = 300;
|
||||
int buttonHeight = 80;
|
||||
|
||||
playButton.setSize(buttonWidth, buttonHeight);
|
||||
loadButton.setSize(buttonWidth, buttonHeight);
|
||||
leaderboardButton.setSize(buttonWidth, buttonHeight);
|
||||
settingsButton.setSize(buttonWidth, buttonHeight);
|
||||
exitButton.setSize(buttonWidth, buttonHeight);
|
||||
|
||||
@ -81,14 +85,24 @@ public class MainMenu extends Scene {
|
||||
playButton.setPosition(centerX, startY);
|
||||
loadButton.setSize(buttonWidth, buttonHeight);
|
||||
loadButton.setPosition(centerX, startY - spacing);
|
||||
leaderboardButton.setSize(buttonWidth, buttonHeight);
|
||||
leaderboardButton.setPosition(centerX, startY - spacing * 2);
|
||||
settingsButton.setSize(buttonWidth, buttonHeight);
|
||||
settingsButton.setPosition(centerX, startY - spacing * 2);
|
||||
settingsButton.setPosition(centerX, startY - spacing * 3);
|
||||
exitButton.setSize(buttonWidth, buttonHeight);
|
||||
exitButton.setPosition(centerX, startY - spacing * 3);
|
||||
exitButton.setPosition(centerX, startY - spacing * 4);
|
||||
|
||||
// Mouse click handlers
|
||||
playButton.onClick(() -> log.debug("Play clicked"));
|
||||
playButton.onClick(() -> {
|
||||
log.debug("Play clicked");
|
||||
// Create transition to DialogueScene
|
||||
if (game.transition == null) {
|
||||
DialogueScene dialogueScene = new DialogueScene(game);
|
||||
game.transition = new DialogueTransition(game, this, dialogueScene, 500);
|
||||
}
|
||||
});
|
||||
loadButton.onClick(() -> log.debug("Load clicked"));
|
||||
leaderboardButton.onClick(() -> log.debug("Leaderboard clicked"));
|
||||
settingsButton.onClick(() -> {
|
||||
log.debug("Settings clicked");
|
||||
if (game.transition == null) {
|
||||
@ -102,6 +116,7 @@ public class MainMenu extends Scene {
|
||||
|
||||
root.addActor(playButton.getActor());
|
||||
root.addActor(loadButton.getActor());
|
||||
root.addActor(leaderboardButton.getActor());
|
||||
root.addActor(settingsButton.getActor());
|
||||
root.addActor(exitButton.getActor());
|
||||
|
||||
@ -133,7 +148,7 @@ public class MainMenu extends Scene {
|
||||
@Override
|
||||
public boolean keyDown(InputEvent event, int keycode) {
|
||||
sKeyDown(event, keycode);
|
||||
currentKeyDown.put(keycode, System.currentTimeMillis());
|
||||
currentKeyDown.put(keycode, TimeUtils.millis());
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@ -156,6 +171,7 @@ public class MainMenu extends Scene {
|
||||
return;
|
||||
videoPlayer.setLooping(true);
|
||||
videoPlayer.play();
|
||||
Audio.menuMusicPlay();
|
||||
videoPrepared = true;
|
||||
}
|
||||
|
||||
@ -169,10 +185,10 @@ public class MainMenu extends Scene {
|
||||
log.trace("Key pressed: {}", keycode);
|
||||
switch (keycode) {
|
||||
case Input.Keys.UP:
|
||||
currentChoice = (currentChoice - 1 + 4) % 4;
|
||||
currentChoice = (currentChoice - 1 + buttons.length) % buttons.length;
|
||||
break;
|
||||
case Input.Keys.DOWN:
|
||||
currentChoice = (currentChoice + 1) % 4;
|
||||
currentChoice = (currentChoice + 1) % buttons.length;
|
||||
break;
|
||||
case Input.Keys.RIGHT:
|
||||
case Input.Keys.ENTER:
|
||||
@ -201,9 +217,9 @@ public class MainMenu extends Scene {
|
||||
for (Map.Entry<Integer, Long> entry : currentKeyDown.entrySet()) {
|
||||
Integer keyCode = entry.getKey();
|
||||
Long timeStamp = entry.getValue();
|
||||
if (System.currentTimeMillis() - timeStamp > 100) {
|
||||
if (TimeUtils.millis() - timeStamp > 100) {
|
||||
sKeyDown(null, keyCode);
|
||||
currentKeyDown.put(keyCode, System.currentTimeMillis());
|
||||
currentKeyDown.put(keyCode, TimeUtils.millis());
|
||||
}
|
||||
}
|
||||
startVideoOnce();
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
package org.vibecoders.moongazer.scenes;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
import static org.vibecoders.moongazer.Constants.*;
|
||||
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.State;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
import org.vibecoders.moongazer.ui.UIImageButton;
|
||||
|
||||
public class Settings extends Scene {
|
||||
public Settings(Game game) {
|
||||
super(game);
|
||||
// WIP
|
||||
UIImageButton settingButton = new UIImageButton("textures/ui/UI_Icon_Setting.png");
|
||||
UIImageButton exitImgButton = new UIImageButton("textures/ui/IconExitGame.png");
|
||||
UIImageButton soundButton = new UIImageButton("textures/ui/ImgReShaSoundOn.png");
|
||||
UIImageButton closeButton = new UIImageButton("textures/ui/UI_Gcg_Icon_Close.png");
|
||||
settingButton.setSize(50, 50);
|
||||
exitImgButton.setSize(50, 50);
|
||||
soundButton.setSize(50, 50);
|
||||
closeButton.setSize(50, 50);
|
||||
settingButton.setPosition(20, WINDOW_HEIGHT - 70);
|
||||
exitImgButton.setPosition(WINDOW_WIDTH - 70, WINDOW_HEIGHT - 70);
|
||||
soundButton.setPosition(20, 20);
|
||||
closeButton.setPosition(WINDOW_WIDTH - 70, 20);
|
||||
|
||||
root.addActor(settingButton.getActor());
|
||||
root.addActor(exitImgButton.getActor());
|
||||
root.addActor(soundButton.getActor());
|
||||
root.addActor(closeButton.getActor());
|
||||
settingButton.onClick(() -> log.debug("Settings clicked"));
|
||||
exitImgButton.onClick(() -> {
|
||||
log.debug("Exit clicked");
|
||||
if (game.transition == null) {
|
||||
game.transition = new Transition(game, this, game.mainMenuScene, State.MAIN_MENU, 350);
|
||||
}
|
||||
});
|
||||
soundButton.onClick(() -> log.debug("Sound clicked"));
|
||||
closeButton.onClick(() -> log.debug("Close clicked"));
|
||||
|
||||
game.stage.addActor(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
batch.draw(Assets.getWhiteTexture(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
package org.vibecoders.moongazer.scenes;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.Input;
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Table;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.State;
|
||||
import org.vibecoders.moongazer.Settings;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
import org.vibecoders.moongazer.managers.Audio;
|
||||
import org.vibecoders.moongazer.ui.UICloseButton;
|
||||
import org.vibecoders.moongazer.ui.UITextButton;
|
||||
import org.vibecoders.moongazer.ui.UISlider;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import static org.vibecoders.moongazer.Constants.*;
|
||||
|
||||
public class SettingsScene extends Scene {
|
||||
private boolean isEditingKeybind = false;
|
||||
private UITextButton currentEditingButton = null;
|
||||
private String currentKeybindAction = "";
|
||||
private HashMap<String, UITextButton> keybindButtons = new HashMap<>();
|
||||
private UISlider masterVolSlider;
|
||||
private UISlider musicSlider;
|
||||
private UISlider sfxSlider;
|
||||
|
||||
public SettingsScene(Game game) {
|
||||
super(game);
|
||||
|
||||
root.setFillParent(true);
|
||||
BitmapFont font = Assets.getFont("ui", 24);
|
||||
Label.LabelStyle labelStyle = new Label.LabelStyle(font, Color.WHITE);
|
||||
|
||||
// Main panel
|
||||
Table mainPanel = new Table();
|
||||
mainPanel.setSize(800, 600);
|
||||
mainPanel.setPosition((Gdx.graphics.getWidth() - 800) / 2f,
|
||||
(Gdx.graphics.getHeight() - 600) / 2f);
|
||||
|
||||
Label title = new Label("Settings", labelStyle);
|
||||
mainPanel.add(title).colspan(2).padTop(60).padBottom(40);
|
||||
mainPanel.row();
|
||||
|
||||
// Volume settings
|
||||
TextureRegionDrawable bg = new TextureRegionDrawable(Assets.getWhiteTexture());
|
||||
var tintedBg = bg.tint(new Color(0.2f, 0.2f, 0.2f, 0.3f));
|
||||
|
||||
String[] volumes = { "Master Volume", "Music Volume", "SFX Volume" };
|
||||
// TODO: implement audio manager
|
||||
for (String volume : volumes) {
|
||||
Table row = new Table();
|
||||
row.setBackground(tintedBg);
|
||||
row.add(new Label(volume, labelStyle)).expandX().left().padLeft(40).pad(15);
|
||||
if (volume.equals("Master Volume")) {
|
||||
masterVolSlider = new UISlider();
|
||||
masterVolSlider.onChanged(() -> {
|
||||
Settings.setMasterVolume(masterVolSlider.getValue());
|
||||
Audio.musicSetVolume();
|
||||
});
|
||||
row.add(masterVolSlider.slider).width(300).right().padRight(40);
|
||||
} else if (volume.equals("Music Volume")) {
|
||||
musicSlider = new UISlider();
|
||||
musicSlider.onChanged(() -> {
|
||||
Settings.setMusicVolume(musicSlider.getValue());
|
||||
Audio.musicSetVolume();
|
||||
});
|
||||
row.add(musicSlider.slider).width(300).right().padRight(40);
|
||||
} else if (volume.equals("SFX Volume")) {
|
||||
sfxSlider = new UISlider();
|
||||
sfxSlider.onChanged(() -> {
|
||||
Settings.setSfxVolume(sfxSlider.getValue());
|
||||
});
|
||||
row.add(sfxSlider.slider).width(300).right().padRight(40);
|
||||
}
|
||||
mainPanel.add(row).width(700).height(60).padBottom(5);
|
||||
mainPanel.row();
|
||||
}
|
||||
|
||||
// Keybind settings
|
||||
Table section = new Table();
|
||||
section.setBackground(tintedBg);
|
||||
section.add(new Label("Keybinds", labelStyle)).colspan(2).expandX().left()
|
||||
.padLeft(20).padTop(15).padBottom(10);
|
||||
section.row();
|
||||
|
||||
// Player keybinds
|
||||
String[] players = { "Player 1", "Player 2" };
|
||||
String[] prefixes = { "p1", "p2" };
|
||||
String[] actions = { "_left", "_right" };
|
||||
String[] labels = { " Move Left", " Move Right" };
|
||||
|
||||
for (int p = 0; p < players.length; p++) {
|
||||
section.add(new Label(" " + players[p], labelStyle)).colspan(2).left()
|
||||
.padLeft(60);
|
||||
section.row();
|
||||
for (int a = 0; a < actions.length; a++) {
|
||||
section.add(new Label(labels[a], labelStyle)).left().padLeft(80);
|
||||
String action = prefixes[p] + actions[a];
|
||||
UITextButton button = new UITextButton(getKeyName(Settings.getKeybind(action)), font);
|
||||
button.button.addListener(new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent event, float x, float y) {
|
||||
if (!isEditingKeybind) {
|
||||
isEditingKeybind = true;
|
||||
currentEditingButton = button;
|
||||
currentKeybindAction = action;
|
||||
((TextButton) (button.button)).setText("Press Key...");
|
||||
}
|
||||
}
|
||||
});
|
||||
keybindButtons.put(action, button);
|
||||
section.add(button.button).width(240).height(60).right().padRight(40);
|
||||
section.row();
|
||||
}
|
||||
}
|
||||
|
||||
mainPanel.add(section).width(700).padBottom(20);
|
||||
mainPanel.row();
|
||||
|
||||
// Back button
|
||||
UICloseButton backButton = new UICloseButton();
|
||||
backButton.setSize(40, 40);
|
||||
backButton.setPosition(Gdx.graphics.getWidth() - 80, Gdx.graphics.getHeight() - 80);
|
||||
backButton.onClick(() -> {
|
||||
if (game.transition == null && !isEditingKeybind) {
|
||||
game.transition = new Transition(game, this, game.mainMenuScene,
|
||||
State.MAIN_MENU, 350);
|
||||
}
|
||||
});
|
||||
|
||||
root.addActor(mainPanel);
|
||||
root.addActor(backButton.getActor());
|
||||
game.stage.addActor(root);
|
||||
|
||||
// Key listener
|
||||
root.addListener(new InputListener() {
|
||||
@Override
|
||||
public boolean keyDown(InputEvent event, int keycode) {
|
||||
if (isEditingKeybind && currentEditingButton != null) {
|
||||
if (keycode == Input.Keys.ESCAPE) {
|
||||
if (currentEditingButton != null) {
|
||||
((TextButton) (currentEditingButton.button))
|
||||
.setText(getKeyName(Settings.getKeybind(currentKeybindAction)));
|
||||
}
|
||||
isEditingKeybind = false;
|
||||
currentEditingButton = null;
|
||||
currentKeybindAction = "";
|
||||
return true;
|
||||
}
|
||||
if (isKeybindAlreadyUsed(keycode, currentKeybindAction)) {
|
||||
((TextButton) (currentEditingButton.button)).setText("Key in use!");
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(1000);
|
||||
Gdx.app.postRunnable(() -> {
|
||||
if (isEditingKeybind && currentEditingButton != null) {
|
||||
((TextButton) (currentEditingButton.button)).setText("Press Key...");
|
||||
}
|
||||
});
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}).start();
|
||||
return true;
|
||||
}
|
||||
Settings.keybinds.put(currentKeybindAction, keycode);
|
||||
((TextButton) (currentEditingButton.button)).setText(getKeyName(keycode));
|
||||
isEditingKeybind = false;
|
||||
currentEditingButton = null;
|
||||
currentKeybindAction = "";
|
||||
return true;
|
||||
} else if (keycode == Input.Keys.ESCAPE) {
|
||||
backButton.click();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String getKeyName(int keycode) {
|
||||
String keyName = Input.Keys.toString(keycode);
|
||||
return keyName != null ? keyName.toUpperCase() : "Unknown";
|
||||
}
|
||||
|
||||
private boolean isKeybindAlreadyUsed(int keycode, String currentAction) {
|
||||
return Settings.keybinds.entrySet().stream()
|
||||
.anyMatch(entry -> !entry.getKey().equals(currentAction) &&
|
||||
entry.getValue().equals(keycode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
game.mainMenuScene.render(batch);
|
||||
batch.setColor(0, 0, 0, 0.8f);
|
||||
batch.draw(Assets.getWhiteTexture(), 0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
|
||||
batch.setColor(Color.WHITE);
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ package org.vibecoders.moongazer.scenes;
|
||||
import org.vibecoders.moongazer.Game;
|
||||
import org.vibecoders.moongazer.State;
|
||||
|
||||
import com.badlogic.gdx.Gdx;
|
||||
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
|
||||
|
||||
/**
|
||||
@ -12,11 +13,12 @@ public class Transition extends Scene {
|
||||
private Scene from;
|
||||
private Scene to;
|
||||
private State targetState;
|
||||
private long startTime;
|
||||
private float totalTime = 0f;
|
||||
private long duration;
|
||||
|
||||
/**
|
||||
* Creates a new transition between two scenes.
|
||||
*
|
||||
* @param from The scene to transition from.
|
||||
* @param to The scene to transition to.
|
||||
* @param targetState The target state of the game after the transition.
|
||||
@ -30,16 +32,17 @@ public class Transition extends Scene {
|
||||
this.to = to;
|
||||
this.targetState = targetState;
|
||||
this.duration = duration;
|
||||
startTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the transition effect.
|
||||
*
|
||||
* @param batch The SpriteBatch to draw with.
|
||||
*/
|
||||
@Override
|
||||
public void render(SpriteBatch batch) {
|
||||
var toOpacity = ((float) (System.currentTimeMillis() - startTime)) / duration;
|
||||
totalTime += Gdx.graphics.getDeltaTime();
|
||||
var toOpacity = totalTime / (((float) duration) / 1000);
|
||||
if (toOpacity >= 0.99) {
|
||||
log.trace("Transition complete to state: {}", targetState);
|
||||
game.state = targetState;
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
package org.vibecoders.moongazer.ui;
|
||||
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
|
||||
public class UICloseButton extends UIButton {
|
||||
public UICloseButton() {
|
||||
Texture closeTexture = Assets.getAsset("textures/ui/close.png", Texture.class);
|
||||
Texture closeHoverTexture = Assets.getAsset("textures/ui/close_hover.png", Texture.class);
|
||||
Texture closeClickTexture = Assets.getAsset("textures/ui/close_clicked.png", Texture.class);
|
||||
TextureRegionDrawable drawable = new TextureRegionDrawable(new TextureRegion(closeTexture));
|
||||
TextureRegionDrawable hoverDrawable = new TextureRegionDrawable(new TextureRegion(closeHoverTexture));
|
||||
TextureRegionDrawable clickDrawable = new TextureRegionDrawable(new TextureRegion(closeClickTexture));
|
||||
ImageButton.ImageButtonStyle style = new ImageButton.ImageButtonStyle();
|
||||
style.imageUp = drawable;
|
||||
style.imageDown = clickDrawable;
|
||||
style.imageOver = hoverDrawable;
|
||||
this.button = new ImageButton(style);
|
||||
this.actor = button;
|
||||
}
|
||||
}
|
||||
61
app/src/main/java/org/vibecoders/moongazer/ui/UISlider.java
Normal file
@ -0,0 +1,61 @@
|
||||
package org.vibecoders.moongazer.ui;
|
||||
|
||||
import org.vibecoders.moongazer.managers.Assets;
|
||||
|
||||
import com.badlogic.gdx.scenes.scene2d.Actor;
|
||||
import com.badlogic.gdx.scenes.scene2d.EventListener;
|
||||
import com.badlogic.gdx.graphics.Texture;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Slider;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Slider.SliderStyle;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
|
||||
|
||||
public class UISlider {
|
||||
public Slider slider;
|
||||
|
||||
public UISlider() {
|
||||
Texture sliderBgTexture = Assets.getAsset("textures/ui/UI_SliderBg2.png", Texture.class);
|
||||
Texture sliderKnobTexture = Assets.getAsset("textures/ui/UI_SliderKnob.png", Texture.class);
|
||||
Texture sliderKnobOverTexture = Assets.getAsset("textures/ui/UI_SliderBg.png", Texture.class);
|
||||
|
||||
SliderStyle sliderStyle = new SliderStyle();
|
||||
sliderStyle.background = new TextureRegionDrawable(new TextureRegion(sliderBgTexture));
|
||||
sliderStyle.knob = new TextureRegionDrawable(new TextureRegion(sliderKnobTexture));
|
||||
sliderStyle.knobBefore = new TextureRegionDrawable(new TextureRegion(sliderKnobOverTexture));
|
||||
sliderStyle.knobAfter = new TextureRegionDrawable(new TextureRegion(sliderBgTexture));
|
||||
|
||||
slider = new Slider(0f, 1f, 0.01f, false, sliderStyle);
|
||||
slider.setValue(1f);
|
||||
slider.setProgrammaticChangeEvents(true);
|
||||
}
|
||||
|
||||
public void setValue(float value) {
|
||||
slider.setValue(value);
|
||||
}
|
||||
|
||||
public float getValue() {
|
||||
return slider.getValue();
|
||||
}
|
||||
|
||||
public void setSize(float width, float height) {
|
||||
slider.setSize(width, height);
|
||||
}
|
||||
|
||||
public void setPosition(float x, float y) {
|
||||
slider.setPosition(x, y);
|
||||
}
|
||||
|
||||
public void addListener(EventListener listener) {
|
||||
slider.addListener(listener);
|
||||
}
|
||||
|
||||
public void onChanged(Runnable action) {
|
||||
slider.addListener(new ChangeListener() {
|
||||
@Override
|
||||
public void changed(ChangeEvent event, Actor actor) {
|
||||
action.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
package org.vibecoders.moongazer.vn;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
|
||||
public class CharacterActor extends Image {
|
||||
public CharacterActor(TextureRegion baseReg) {
|
||||
super(baseReg);
|
||||
float ox = getWidth() * 0.5f;
|
||||
float oy = 0f;
|
||||
setOrigin(ox, oy);
|
||||
}
|
||||
}
|
||||
36
app/src/main/java/org/vibecoders/moongazer/vn/ChoiceBox.java
Normal file
@ -0,0 +1,36 @@
|
||||
package org.vibecoders.moongazer.vn;
|
||||
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.InputEvent;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.TextButton;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
|
||||
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
|
||||
|
||||
public class ChoiceBox extends Group {
|
||||
public interface Listener {
|
||||
void onChoice(int idx);
|
||||
}
|
||||
|
||||
public ChoiceBox(BitmapFont font, String[] options, Listener listener) {
|
||||
TextButton.TextButtonStyle style = new TextButton.TextButtonStyle();
|
||||
style.font = font;
|
||||
|
||||
VerticalGroup col = new VerticalGroup().space(8);
|
||||
|
||||
for (int i = 0; i < options.length; i++) {
|
||||
final int idx = i;
|
||||
TextButton b = new TextButton(options[i], style);
|
||||
b.addListener(new ClickListener() {
|
||||
@Override
|
||||
public void clicked(InputEvent e, float x, float y) {
|
||||
listener.onChoice(idx);
|
||||
}
|
||||
});
|
||||
col.addActor(b);
|
||||
}
|
||||
|
||||
addActor(col);
|
||||
col.pack();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package org.vibecoders.moongazer.vn;
|
||||
|
||||
import com.badlogic.gdx.graphics.Color;
|
||||
import com.badlogic.gdx.graphics.g2d.BitmapFont;
|
||||
import com.badlogic.gdx.graphics.g2d.TextureRegion;
|
||||
import com.badlogic.gdx.scenes.scene2d.Group;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Image;
|
||||
import com.badlogic.gdx.scenes.scene2d.ui.Label;
|
||||
import com.badlogic.gdx.utils.Align;
|
||||
|
||||
/**
|
||||
* Transparent dialogue box with typing effect.
|
||||
* Shows speaker name, separator, and dialogue text.
|
||||
*/
|
||||
public class DialogueBoxTransparent extends Group {
|
||||
private final Label nameLabel;
|
||||
private final Label textLabel;
|
||||
private CharSequence fullText;
|
||||
private float shown = 0f;
|
||||
private boolean done = true;
|
||||
private final float charPerSec = 45f;
|
||||
|
||||
private static final float BOX_HEIGHT = 200f;
|
||||
private static final float TEXT_MARGIN = 20f;
|
||||
|
||||
public DialogueBoxTransparent(BitmapFont font, TextureRegion bgRegion, TextureRegion sepRegion, float width) {
|
||||
Image background = new Image(bgRegion);
|
||||
background.setSize(width, BOX_HEIGHT);
|
||||
background.setColor(1f, 1f, 1f, 0.3f);
|
||||
addActor(background);
|
||||
|
||||
Label.LabelStyle nameStyle = new Label.LabelStyle();
|
||||
nameStyle.font = font;
|
||||
nameStyle.fontColor = Color.GOLD;
|
||||
nameLabel = new Label("", nameStyle);
|
||||
nameLabel.setFontScale(1.2f);
|
||||
nameLabel.setAlignment(Align.center);
|
||||
nameLabel.setWidth(width);
|
||||
nameLabel.setPosition(0, BOX_HEIGHT + 40);
|
||||
addActor(nameLabel);
|
||||
|
||||
Image separator = new Image(sepRegion);
|
||||
float separatorDisplayWidth = width;
|
||||
float aspectRatio = (float) sepRegion.getRegionHeight() / (float) sepRegion.getRegionWidth();
|
||||
float separatorDisplayHeight = separatorDisplayWidth * aspectRatio;
|
||||
|
||||
if (separatorDisplayHeight > 180f) {
|
||||
separatorDisplayHeight = 180f;
|
||||
separatorDisplayWidth = separatorDisplayHeight / aspectRatio;
|
||||
}
|
||||
|
||||
separator.setSize(separatorDisplayWidth, separatorDisplayHeight);
|
||||
float sepX = (width - separatorDisplayWidth) / 2f;
|
||||
float sepY = BOX_HEIGHT - 100;
|
||||
separator.setPosition(sepX, sepY);
|
||||
separator.setColor(1f, 1f, 1f, 1f);
|
||||
addActor(separator);
|
||||
|
||||
Label.LabelStyle textStyle = new Label.LabelStyle();
|
||||
textStyle.font = font;
|
||||
textStyle.fontColor = Color.WHITE;
|
||||
textLabel = new Label("", textStyle);
|
||||
textLabel.setWrap(true);
|
||||
textLabel.setWidth(width - TEXT_MARGIN * 2);
|
||||
textLabel.setAlignment(Align.center);
|
||||
textLabel.setPosition(TEXT_MARGIN, (BOX_HEIGHT / 2) - 20);
|
||||
addActor(textLabel);
|
||||
|
||||
setSize(width, BOX_HEIGHT + 30);
|
||||
}
|
||||
|
||||
public void setDialogue(String speaker, String text) {
|
||||
nameLabel.setText(speaker);
|
||||
fullText = text;
|
||||
shown = 0f;
|
||||
done = false;
|
||||
textLabel.setText("");
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
|
||||
public void skip() {
|
||||
if (fullText != null) {
|
||||
textLabel.setText(fullText);
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void act(float delta) {
|
||||
super.act(delta);
|
||||
if (done || fullText == null) return;
|
||||
|
||||
shown += charPerSec * delta;
|
||||
int n = Math.min(fullText.length(), (int) shown);
|
||||
textLabel.setText(fullText.subSequence(0, n));
|
||||
|
||||
if (n >= fullText.length()) {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
app/src/main/resources/audio/I Once Praised The Day.mp3
Normal file
BIN
app/src/main/resources/textures/ui/UI_Scrollbar_Arrow.png
Normal file
|
After Width: | Height: | Size: 706 B |
BIN
app/src/main/resources/textures/ui/UI_Scrollbar_Handle.png
Normal file
|
After Width: | Height: | Size: 209 B |
BIN
app/src/main/resources/textures/ui/UI_SliderBg.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/resources/textures/ui/UI_SliderBg2.png
Normal file
|
After Width: | Height: | Size: 839 B |
BIN
app/src/main/resources/textures/ui/UI_SliderKnob.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/resources/textures/ui/arrow-button-left.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
BIN
app/src/main/resources/textures/ui/arrow-button-right.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
app/src/main/resources/textures/ui/close.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/resources/textures/ui/close_clicked.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/resources/textures/ui/close_hover.png
Normal file
|
After Width: | Height: | Size: 3.4 KiB |
BIN
app/src/main/resources/textures/vn_scene/char_base.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
app/src/main/resources/textures/vn_scene/separator.png
Normal file
|
After Width: | Height: | Size: 35 KiB |