feat: New scenes

This commit is contained in:
2025-12-21 14:40:36 +01:00
parent 88403993fe
commit fdb6c3075d
12 changed files with 245 additions and 73 deletions

View File

@@ -23,6 +23,8 @@
<version>1.18.38</version>
</path>
</annotationProcessorPaths>
<source>25</source>
<target>25</target>
</configuration>
</plugin>
@@ -152,4 +154,4 @@
<version>6.1.1-1.5.10</version>
</dependency>
</dependencies>
</project>
</project>

View File

@@ -1,11 +0,0 @@
package cz.jzitnik.events;
import cz.jzitnik.utils.events.Event;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public class PlayVideo implements Event {
private String fileName;
}

View File

@@ -1,48 +0,0 @@
package cz.jzitnik.events.handlers;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.screen.Screen;
import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.annotations.EventHandler;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.PlayVideo;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.states.TerminalState;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.events.AbstractEventHandler;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
@EventHandler(PlayVideo.class)
public class PlayVideoHandler extends AbstractEventHandler<PlayVideo> {
public PlayVideoHandler(DependencyManager dm) {
super(dm);
}
@InjectDependency
private ResourceManager resourceManager;
@InjectState
private TerminalState terminalState;
@Override
public void handle(PlayVideo event) {
}
private static TextColor.RGB toColor(int rgb) {
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
return new TextColor.RGB(r, g, b);
}
}

View File

@@ -5,7 +5,6 @@ import cz.jzitnik.annotations.EventHandler;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.FullRoomDraw;
import cz.jzitnik.events.PlayVideo;
import cz.jzitnik.events.TerminalResizeEvent;
import cz.jzitnik.game.GameState;
import cz.jzitnik.states.ScreenBuffer;
@@ -32,6 +31,8 @@ public class TerminalResizeEventHandler extends AbstractEventHandler<TerminalRes
@InjectState
private GameState gameState;
private boolean screenRerendering = false;
@Override
public void handle(TerminalResizeEvent event) {
TerminalSize size = event.getNewSize();
@@ -47,7 +48,13 @@ public class TerminalResizeEventHandler extends AbstractEventHandler<TerminalRes
screenBuffer.setRenderedBuffer(buffer);
if (gameState.getScreen() != null) {
gameState.getScreen().fullRender();
if (screenRerendering) {
return;
} else {
screenRerendering = true;
gameState.getScreen().fullRender();
screenRerendering = false;
}
} else {
eventManager.emitEvent(new FullRoomDraw(true));
}

View File

@@ -60,6 +60,19 @@ public class ResourceManager {
}
}
public BufferedImage getResource(String path) {
InputStream is = classLoader.getResourceAsStream(path);
if (is == null) {
throw new RuntimeException("Image not found in resources!");
}
try {
return ImageIO.read(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
public InputStream getResourceAsStream(String path) {
return classLoader.getResourceAsStream(path);
}

View File

@@ -0,0 +1,77 @@
package cz.jzitnik.game.setup;
import com.googlecode.lanterna.input.KeyType;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.FullRoomDraw;
import cz.jzitnik.events.KeyboardPressEvent;
import cz.jzitnik.events.MouseAction;
import cz.jzitnik.game.GameState;
import cz.jzitnik.screens.Screen;
import cz.jzitnik.screens.scenes.BasicImageScene;
import cz.jzitnik.screens.scenes.Scene;
import cz.jzitnik.sound.SoundPlayer;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.events.EventManager;
public class GameMenuScene extends Scene {
private static class GameMenuScreen extends Screen {
protected final SoundPlayer soundPlayer = new SoundPlayer();
protected boolean play = true;
@Override
public void fullRender() {
// No render here just basic audio playback
new Thread(() -> {
while (play) {
soundPlayer.playSound("audio/menu.ogg", 30, 100);
}
}).start();
}
@Override
public void handleMouseAction(MouseAction event) {
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
}
}
private static final class ImageScene extends BasicImageScene {
@InjectState
private GameState gameState;
@InjectDependency
private EventManager eventManager;
private final GameMenuScreen gameMenuScreen;
public ImageScene(String filePath, GameMenuScreen gameMenuScreen) {
super(filePath);
this.gameMenuScreen = gameMenuScreen;
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
if (event.getKeyStroke().getKeyType() == KeyType.Enter) {
gameMenuScreen.play = false;
gameMenuScreen.soundPlayer.stopCurrentSound();
gameState.setScreen(null);
eventManager.emitEvent(new FullRoomDraw(true));
}
}
}
public GameMenuScene(DependencyManager dependencyManager) {
GameMenuScreen gameMenuScreen = new GameMenuScreen();
ImageScene basicImageScene = new ImageScene("menu.png", gameMenuScreen);
super(new Screen[]{gameMenuScreen, basicImageScene}, new OnEndAction.Repeat());
dependencyManager.inject(this);
dependencyManager.inject(basicImageScene);
}
}

View File

@@ -11,7 +11,6 @@ import cz.jzitnik.game.items.GameItem;
import cz.jzitnik.game.items.WoodenSword;
import cz.jzitnik.game.objects.Chest;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.screens.scenes.IntroScene;
import cz.jzitnik.utils.DependencyManager;
@Dependency
@@ -31,9 +30,13 @@ public class GameSetup {
GameRoom mainRoom = new GameRoom(ResourceManager.Resource.ROOM1);
GameRoom rightRoom = new GameRoom(ResourceManager.Resource.ROOM2);
GameRoom topRightRoom = new GameRoom(ResourceManager.Resource.ROOM3);
GameRoom topRightTop = new GameRoom(ResourceManager.Resource.ROOM4);
GameRoom topRightTopLeft = new GameRoom(ResourceManager.Resource.ROOM_FROZEN);
mainRoom.setRight(rightRoom);
rightRoom.setUp(topRightRoom);
topRightRoom.setUp(topRightTop);
topRightTop.setLeft(topRightTopLeft);
Chest chest = new Chest(dependencyManager, resourceManager, new RoomCords(100, 45), new GameItem[]{
new WoodenSword(resourceManager),

View File

@@ -1,6 +1,8 @@
package cz.jzitnik.screens.scenes;
package cz.jzitnik.game.setup;
import cz.jzitnik.screens.Screen;
import cz.jzitnik.screens.scenes.Scene;
import cz.jzitnik.screens.scenes.VideoSceneWithAudio;
import cz.jzitnik.utils.DependencyManager;
public class IntroScene extends Scene {
@@ -9,7 +11,7 @@ public class IntroScene extends Scene {
new Screen[]{
new VideoSceneWithAudio("video.mp4", "audio.ogg")
},
OnEndAction.SWITCH_TO_GAME
new OnEndAction.SwitchToScreen(new GameMenuScene(dependencyManager))
);
dependencyManager.inject(this);
}

View File

@@ -0,0 +1,105 @@
package cz.jzitnik.screens.scenes;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.KeyboardPressEvent;
import cz.jzitnik.events.MouseAction;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.screens.Screen;
import cz.jzitnik.states.TerminalState;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class BasicImageScene extends Screen {
@InjectDependency
private ResourceManager resourceManager;
@InjectState
private TerminalState terminalState;
private final String imagePath;
public BasicImageScene(String filePath) {
imagePath = filePath;
}
@Override
public void fullRender() {
BufferedImage image = resourceManager.getResource(imagePath);
TerminalScreen screen = terminalState.getTerminalScreen();
screen.clear();
var tg = terminalState.getTextGraphics();
int termWidth = screen.getTerminalSize().getColumns();
int termHeight = screen.getTerminalSize().getRows();
int imgWidth = image.getWidth();
int imgHeight = image.getHeight();
// Terminal pixel space (because 1 cell = 2 vertical pixels)
int termPixelWidth = termWidth;
int termPixelHeight = termHeight * 2;
// Scale while preserving aspect ratio
double scale = Math.min(
(double) termPixelWidth / imgWidth,
(double) termPixelHeight / imgHeight
);
int scaledWidth = (int) (imgWidth * scale);
int scaledHeight = (int) (imgHeight * scale);
// Centering offsets (in pixel space)
int xOffset = (termPixelWidth - scaledWidth) / 2;
int yOffset = (termPixelHeight - scaledHeight) / 2;
for (int y = 0; y < scaledHeight / 2; y++) {
int imgYTop = (int) ((y * 2) / scale);
int imgYBottom = (int) ((y * 2 + 1) / scale);
int termY = y + (yOffset / 2);
if (termY < 0 || termY >= termHeight) continue;
for (int x = 0; x < scaledWidth; x++) {
int termX = x + xOffset;
if (termX < 0 || termX >= termWidth) continue;
int imgX = (int) (x / scale);
int topPixel = image.getRGB(imgX, imgYTop);
int bottomPixel = image.getRGB(imgX, imgYBottom);
tg.setBackgroundColor(toColor(topPixel));
tg.setForegroundColor(toColor(bottomPixel));
tg.setCharacter(termX, termY, '▄');
}
}
try {
screen.refresh(com.googlecode.lanterna.screen.Screen.RefreshType.COMPLETE);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private TextColor.RGB toColor(int rgb) {
int r = (rgb >> 16) & 0xFF;
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;
return new TextColor.RGB(r, g, b);
}
@Override
public void handleMouseAction(MouseAction event) {
}
@Override
public void handleKeyboardAction(KeyboardPressEvent event) {
}
}

View File

@@ -9,6 +9,8 @@ import cz.jzitnik.game.GameState;
import cz.jzitnik.screens.Screen;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.events.EventManager;
import lombok.AllArgsConstructor;
import lombok.Getter;
public abstract class Scene extends Screen {
private final Screen[] parts;
@@ -26,8 +28,16 @@ public abstract class Scene extends Screen {
@InjectDependency
private EventManager eventManager;
public enum OnEndAction {
SWITCH_TO_GAME
public static class OnEndAction {
public static class SwitchToGame extends OnEndAction {}
public static class Repeat extends OnEndAction {}
public static class None extends OnEndAction {}
@Getter
@AllArgsConstructor
public static class SwitchToScreen extends OnEndAction {
private final Screen screen;
}
}
public Scene(Screen[] parts, OnEndAction onEndAction) {
@@ -41,6 +51,8 @@ public abstract class Scene extends Screen {
if (!isRenderedAlready) {
isRenderedAlready = true;
render();
} else if (currentPart != null && !onEndAction.getClass().equals(OnEndAction.Repeat.class)) {
currentPart.fullRender();
}
}
@@ -52,18 +64,28 @@ public abstract class Scene extends Screen {
try {
currentPart = parts[++currentIndex];
} catch (ArrayIndexOutOfBoundsException e) {
currentPart = null;
if (!onEndAction.getClass().equals(OnEndAction.Repeat.class)) {
currentPart = null;
} else {
break;
}
}
}
switch (onEndAction) {
case SWITCH_TO_GAME -> {
gameState.setScreen(null);
eventManager.emitEvent(new FullRoomDraw(true));
}
if (onEndAction.getClass().equals(OnEndAction.SwitchToGame.class)) {
switchToGame();
} else if (onEndAction.getClass().equals(OnEndAction.SwitchToScreen.class)) {
OnEndAction.SwitchToScreen switchToScreen = (OnEndAction.SwitchToScreen) onEndAction;
gameState.setScreen(switchToScreen.getScreen());
eventManager.emitEvent(new FullRoomDraw(true));
}
}
protected void switchToGame() {
gameState.setScreen(null);
eventManager.emitEvent(new FullRoomDraw(true));
}
@Override
public void handleMouseAction(MouseAction event) {
currentPart.handleMouseAction(event);

Binary file not shown.

BIN
src/main/resources/menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 MiB