From 67a12899c6b3508333fe5583ee6a3aac0edfb85d Mon Sep 17 00:00:00 2001 From: Martin Prokoph Date: Tue, 2 Sep 2025 23:48:49 +0200 Subject: [PATCH] docs: add javadocs for nearly everything --- .../eu/midnightdust/yaytris/HighScores.java | 6 ++ .../java/eu/midnightdust/yaytris/Tetris.java | 50 ++++++++++++++ .../eu/midnightdust/yaytris/Translation.java | 12 ++++ .../eu/midnightdust/yaytris/game/Space.java | 32 ++++++++- .../midnightdust/yaytris/game/Tetromino.java | 34 ++++++++++ .../yaytris/game/TetrominoShape.java | 15 ++--- .../midnightdust/yaytris/ui/GameCanvas.java | 13 ++++ .../yaytris/ui/PreviewCanvas.java | 9 ++- .../eu/midnightdust/yaytris/ui/TetrisUI.java | 59 ++++++++++++++-- .../yaytris/util/CatppuccinColor.java | 4 +- .../midnightdust/yaytris/util/NightJson.java | 67 +++++++++++++++++-- .../yaytris/util/SoundEffect.java | 4 ++ .../midnightdust/yaytris/util/SoundUtil.java | 38 ++++++++++- .../eu/midnightdust/yaytris/util/Vec2i.java | 22 ++++-- 14 files changed, 334 insertions(+), 31 deletions(-) diff --git a/src/main/java/eu/midnightdust/yaytris/HighScores.java b/src/main/java/eu/midnightdust/yaytris/HighScores.java index 63d3cb0..13f8ff7 100644 --- a/src/main/java/eu/midnightdust/yaytris/HighScores.java +++ b/src/main/java/eu/midnightdust/yaytris/HighScores.java @@ -9,6 +9,12 @@ public class HighScores { private static final NightJson json = new NightJson(HighScores.class, "tetris_scores.json5"); public static Map scores = new HashMap<>(); + /** + * Saves a new high score for the specified player + * + * @param playerName the player's name, duh + * @param score the score to save + */ public static void addScore(String playerName, int score) { scores.put(playerName, score); write(); diff --git a/src/main/java/eu/midnightdust/yaytris/Tetris.java b/src/main/java/eu/midnightdust/yaytris/Tetris.java index f088fb0..93c4d99 100644 --- a/src/main/java/eu/midnightdust/yaytris/Tetris.java +++ b/src/main/java/eu/midnightdust/yaytris/Tetris.java @@ -19,6 +19,13 @@ public class Tetris { private static TimerTask gravityTask; private static LocalTime startTime; + /** + * The application's entry point. + * Initializes the UI library to match the system style. + * Also loads saved settings, translations, and highscores from JSON. + * + * @param args command line arguments – will be ignored + */ public static void main(String[] args) { try { System.setProperty("java.awt.headless", "false"); @@ -32,10 +39,20 @@ public class Tetris { ui = new TetrisUI(); } + /** + * Get the active game space + * + * @see Space + */ public static Space getSpace() { return space; } + /** + * Resets the game space, preparing it for a new game. + * + * @see Space + */ public static void resetSpace() { SoundUtil.stopMusic("/music/theme.wav"); if (gravityTask != null) gravityTask.cancel(); @@ -43,6 +60,12 @@ public class Tetris { space = new Space(); } + /** + * Starts a new game of Tetris :D + * This involves starting our gravity task, playing music and spawning the first {@link eu.midnightdust.yaytris.game.Tetromino} + * + * @see Space#spawnTetromino() + */ public static void startGame() { SoundUtil.playMusic("/music/theme.wav", true); space.spawnTetromino(); @@ -51,6 +74,13 @@ public class Tetris { timer.scheduleAtFixedRate(gravityTask, 1, Settings.difficulty.getTimerPeriod()); } + /** + * Stops the current game. + * Disables falling, fades out music and handles saving of high scores. + * + * @see ScoreMenu + * @see HighScores + */ public static void stopGame() { SoundUtil.stopMusic("/music/theme.wav"); if (gravityTask != null) gravityTask.cancel(); @@ -60,13 +90,30 @@ public class Tetris { if (HighScores.scores.values().stream().noneMatch(hs -> hs > space.getScore())) ui.showHighscoreDialog(space.getScore()); } + /** + * Updates the displayed score + * + * @param score the new score + * @see ScoreMenu + */ public static void updateScore(int score) { if (ui.getMenuPanel() instanceof ScoreMenu) ((ScoreMenu) ui.getMenuPanel()).updateScore(score); updateLevel(score); } + /** + * Updates the elapsed time + * + * @see ScoreMenu + */ public static void updateTime() { if (ui.getMenuPanel() instanceof ScoreMenu) ((ScoreMenu) ui.getMenuPanel()).updateTime(startTime); } + /** + * Updates the displayed level + * + * @param score the new score, from which the level will be calculated + * @see ScoreMenu + */ public static void updateLevel(int score) { int newLevel = Math.max(0, (int) (score / 1000f)); if (newLevel != space.level) { @@ -80,6 +127,9 @@ public class Tetris { } } + /** + * Defines our custom timer task that handles falling pieces. + */ public static class GravityTimerTask extends TimerTask { @Override public void run() { diff --git a/src/main/java/eu/midnightdust/yaytris/Translation.java b/src/main/java/eu/midnightdust/yaytris/Translation.java index 433ead8..307e6be 100644 --- a/src/main/java/eu/midnightdust/yaytris/Translation.java +++ b/src/main/java/eu/midnightdust/yaytris/Translation.java @@ -8,11 +8,23 @@ import java.util.Map; public class Translation { public static Map jsonMap = new HashMap<>(); + /** + * Loads translation strings from the specified language via NightJSON. + * + * @param locale the language code (i.e. de_de, en_us) + * @see NightJson + */ public static void load(String locale) { NightJson json = new NightJson(Translation.class, String.format("translation/%s.json5", locale)); json.readJson(); } + /** + * Translates the given key to the active language. + * + * @param key translation key + * @return the resolved translation in the current locale, if present. Otherwise, the key is returned. + */ public static String t(String key, Object... args) { return String.format(jsonMap.getOrDefault(key, key), args); } diff --git a/src/main/java/eu/midnightdust/yaytris/game/Space.java b/src/main/java/eu/midnightdust/yaytris/game/Space.java index c60d532..084f79a 100644 --- a/src/main/java/eu/midnightdust/yaytris/game/Space.java +++ b/src/main/java/eu/midnightdust/yaytris/game/Space.java @@ -15,25 +15,40 @@ public class Space { private int score; public int level; + /** + * Space saves the current state of the game map + */ public Space() { - gameMap = new Color[14][8]; + gameMap = new Color[20][12]; nextShape = generateNextShape(); score = 0; } + /** + * Spawn the queued tetromino piece and generate the next one + */ public void spawnTetromino() { currentTetromino = new Tetromino(nextShape); nextShape = generateNextShape(); } + /** + * @return the currently controlled tetromino piece + */ public Tetromino getCurrentTetromino() { return currentTetromino; } + /** + * @return a randomly selected tetromino shape + */ public TetrominoShape generateNextShape() { return TetrominoShape.values()[random.nextInt(TetrominoShape.values().length)]; } + /** + * @return the queued tetromino shape + */ public TetrominoShape getNextShape() { return nextShape; } @@ -46,6 +61,11 @@ public class Space { return gameMap.length; } + /** + * Converts the current state of the space to an array and includes the falling tetromino. + * + * @return a representation of the space with the falling tetromino baked in + */ public Color[][] getGameMapWithTetromino() { Color[][] tempGameMap = new Color[gameMap.length][gameMap[0].length]; for (int y = 0; y < tempGameMap.length; y++) { @@ -61,6 +81,11 @@ public class Space { return tempGameMap; } + /** + * Converts the current state of the space to an array. + * + * @return a representation of the space without the falling tetromino baked in + */ public Color[][] getGameMap() { return gameMap; } @@ -69,6 +94,11 @@ public class Space { return score; } + /** + * Handle line changes. + * Once a line is fully completed, it is removed from the space, the lines above are moved down and the score is increased. + * More lines completed at once increase the combo and therefore the score. + */ public void onLinesChanged(Tetromino tetromino, int... lines) { int combo = 0; Set completedLines = new TreeSet<>(); diff --git a/src/main/java/eu/midnightdust/yaytris/game/Tetromino.java b/src/main/java/eu/midnightdust/yaytris/game/Tetromino.java index e29f8b8..b590883 100644 --- a/src/main/java/eu/midnightdust/yaytris/game/Tetromino.java +++ b/src/main/java/eu/midnightdust/yaytris/game/Tetromino.java @@ -12,12 +12,20 @@ public class Tetromino { private Vec2i centerPos; private int fallLength = 0; + /** + * This class handles the falling tetromino. + * @param shape the tetromino's shape + */ public Tetromino(TetrominoShape shape) { this.shape = shape; this.collision = shape.boundary; this.centerPos = Vec2i.of(Tetris.getSpace().getMapWidth()/2-1, -1); } + /** + * Moves the tetromino vertically. + * Upon hitting solid ground, the piece is released and {@link Space#onLinesChanged(Tetromino, int...)} is triggered. + */ public boolean fall() { Vec2i newPos = centerPos.offset(Vec2i.of(0, 1)); if (collidesVertically(newPos)) { @@ -37,6 +45,10 @@ public class Tetromino { return true; } + /** + * Moves the tetromino horizontally. + * Accounts for collision and may prevent movement. + */ public void move(int xOffset) { Vec2i newPos = centerPos.offset(Vec2i.of(xOffset, 0)); if (collidesHorizontally(newPos, xOffset)) { @@ -45,6 +57,11 @@ public class Tetromino { centerPos = newPos; } + /** + * Checks, whether the piece would overlap with a block that's already present on the map or would be out-of-bounds. + * @param newPos the new position + * @return true if collision is detected, false otherwise + */ private boolean collidesVertically(Vec2i newPos) { if (Tetris.getSpace() == null) return false; @@ -62,6 +79,12 @@ public class Tetromino { return collides; } + /** + * Checks, whether the piece would overlap with a block that's already present on the map or would be out-of-bounds. + * @param newPos the new position + * @param xOffset the direction of movement (-1 == left; 1 == right) + * @return true if collision is detected, false otherwise + */ private boolean collidesHorizontally(Vec2i newPos, int xOffset) { if (Tetris.getSpace() == null) return false; @@ -78,6 +101,10 @@ public class Tetromino { return collides; } + /** + * Rotate the tetromino 90 degrees clockwise. + * Fails if the piece would collide vertically or horizontally. + */ public void rotate() { int M = collision.length; int N = collision[0].length; @@ -98,6 +125,10 @@ public class Tetromino { else this.centerPos = centerPos.offset(Vec2i.of(offset, 0)); } + /** + * Moves the tetromino piece to its world-view position. + * @return an array representing the line + */ public Color[] getLine(int line) { Color[] l = new Color[Tetris.getSpace().getMapWidth()]; for (int i = 0; i < l.length; i++) { @@ -111,6 +142,9 @@ public class Tetromino { return l; } + /** + * Moves the tetromino down until it collides. + */ public void fallToBottom() { while (true) { if (!fall()) break; diff --git a/src/main/java/eu/midnightdust/yaytris/game/TetrominoShape.java b/src/main/java/eu/midnightdust/yaytris/game/TetrominoShape.java index d16a106..573c0a9 100644 --- a/src/main/java/eu/midnightdust/yaytris/game/TetrominoShape.java +++ b/src/main/java/eu/midnightdust/yaytris/game/TetrominoShape.java @@ -5,39 +5,38 @@ import java.awt.Color; public enum TetrominoShape { SQUARE(new int[][]{ {1, 1}, - {1, 2} + {1, 1} }, Color.YELLOW), LINE(new int[][]{ {1}, - {2}, + {1}, {1}, {1} }, Color.BLUE), T(new int[][]{ {0, 1, 0}, - {1, 2, 1} + {1, 1, 1} }, Color.RED), L_LEFT(new int[][]{ {0, 1}, - {0, 2}, + {0, 1}, {1, 1} }, Color.MAGENTA), L_RIGHT(new int[][]{ {1, 0}, - {2, 0}, + {1, 0}, {1, 1} }, Color.GREEN), ZAP_LEFT(new int[][]{ {0, 1}, - {1, 2}, + {1, 1}, {1, 0} }, Color.CYAN), ZAP_RIGHT(new int[][]{ {1, 0}, - {2, 1}, + {1, 1}, {0, 1} }, Color.ORANGE); - ; final int[][] boundary; final Color color; diff --git a/src/main/java/eu/midnightdust/yaytris/ui/GameCanvas.java b/src/main/java/eu/midnightdust/yaytris/ui/GameCanvas.java index c40fd13..b2b36d0 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/GameCanvas.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/GameCanvas.java @@ -16,6 +16,12 @@ public class GameCanvas extends JPanel { this.texture = FileUtil.loadImage("/textures/tetromino.png"); } + /** + * Draw the current state of the game space. + * + * @param graphics the swing graphics instance + */ + @Override public void paintComponent(Graphics graphics) { super.paintComponent(graphics); if (graphics == null) return; @@ -32,6 +38,13 @@ public class GameCanvas extends JPanel { } } + /** + * Replace a color's alpha channel. + * + * @param color the base color + * @param alpha the opacity (0-255) + * @return the color with the specified alpha channel value + */ public static Color withAlpha(Color color, int alpha) { return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); } diff --git a/src/main/java/eu/midnightdust/yaytris/ui/PreviewCanvas.java b/src/main/java/eu/midnightdust/yaytris/ui/PreviewCanvas.java index 36bdd32..bb1f3ec 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/PreviewCanvas.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/PreviewCanvas.java @@ -1,7 +1,6 @@ package eu.midnightdust.yaytris.ui; import eu.midnightdust.yaytris.Tetris; -import eu.midnightdust.yaytris.game.Tetromino; import eu.midnightdust.yaytris.game.TetrominoShape; import eu.midnightdust.yaytris.util.FileUtil; @@ -9,8 +8,6 @@ import javax.swing.*; import java.awt.*; import java.awt.image.BufferedImage; -import static eu.midnightdust.yaytris.game.TetrominoShape.T; - public class PreviewCanvas extends JPanel { final TetrisUI ui; final BufferedImage texture; @@ -20,6 +17,12 @@ public class PreviewCanvas extends JPanel { this.texture = FileUtil.loadImage("/textures/tetromino.png"); } + /** + * Draw a preview of the next tetromino piece. + * + * @param graphics the swing graphics instance + */ + @Override public void paintComponent(Graphics graphics) { super.paintComponent(graphics); if (graphics == null) return; diff --git a/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java b/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java index ecc07dc..fdffc66 100644 --- a/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java +++ b/src/main/java/eu/midnightdust/yaytris/ui/TetrisUI.java @@ -21,6 +21,9 @@ public class TetrisUI extends JFrame implements KeyListener { GameCanvas gamePanel; JPanel menuPanel; + /** + * Initialize the main Tetris GUI based on Swing + */ public TetrisUI() { this.setLayout(null); this.setTitle("Tetris"); @@ -54,6 +57,9 @@ public class TetrisUI extends JFrame implements KeyListener { this.setVisible(true); } + /** + * Resize all elements to match the current GUI scale. + */ private void rescale() { this.setSize((int) (400 * guiScale), (int) (300 * guiScale)); titleLabel.setBounds(scale(225), scale(7), scale(110), scale(30)); @@ -63,6 +69,11 @@ public class TetrisUI extends JFrame implements KeyListener { } } + /** + * Calculate a size/coordinate in relation to the GUI size multiplicator. + * + * @param bound the regular size + */ public static int scale(int bound) { return (int) (bound * guiScale); } @@ -78,6 +89,11 @@ public class TetrisUI extends JFrame implements KeyListener { return menuPanel; } + /** + * Start the game – let's go! + * + * @param actionEvent unnecessary, but allows for more elegant lambda statements :) + */ public void startGame(ActionEvent actionEvent) { Tetris.startGame(); this.openScoreMenu(actionEvent); @@ -85,6 +101,11 @@ public class TetrisUI extends JFrame implements KeyListener { this.repaint(); } + /** + * Open the main menu. + * + * @param actionEvent unnecessary, but allows for more elegant lambda statements :) + */ public void openMainMenu(ActionEvent actionEvent) { if (this.menuPanel != null) this.remove(menuPanel); Tetris.resetSpace(); @@ -98,6 +119,11 @@ public class TetrisUI extends JFrame implements KeyListener { this.repaint(); } + /** + * Open the settings panel. + * + * @param actionEvent unnecessary, but allows for more elegant lambda statements :) + */ public void openSettings(ActionEvent actionEvent) { if (this.menuPanel != null) this.remove(menuPanel); menuPanel = new SettingsMenu(scale(170), scale(40), scale(220), scale(226), this); @@ -107,6 +133,11 @@ public class TetrisUI extends JFrame implements KeyListener { this.repaint(); } + /** + * Open a panel showing information about the current game session. + * + * @param actionEvent unnecessary, but allows for more elegant lambda statements :) + */ public void openScoreMenu(ActionEvent actionEvent) { if (this.menuPanel != null) this.remove(menuPanel); menuPanel = new ScoreMenu(scale(170), scale(40), scale(220), scale(226), this); @@ -116,6 +147,11 @@ public class TetrisUI extends JFrame implements KeyListener { this.repaint(); } + /** + * Open a panel showing a list of highscores. + * + * @param actionEvent unnecessary, but allows for more elegant lambda statements :) + */ public void openHighscores(ActionEvent actionEvent) { if (this.menuPanel != null) this.remove(menuPanel); menuPanel = new HighScoreMenu(scale(170), scale(40), scale(220), scale(226), this); @@ -125,12 +161,24 @@ public class TetrisUI extends JFrame implements KeyListener { this.repaint(); } + /** + * Open a popup asking for the player's name to save a new highscore. + * + * @param score the new highscore + */ public void showHighscoreDialog(int score) { String playerName = JOptionPane.showInputDialog(null, t("dialog.highscore.action"), t("dialog.highscore.title"), JOptionPane.PLAIN_MESSAGE); HighScores.addScore(playerName, score); } - // Source: https://stackoverflow.com/a/19746437 + + /** + * Centers the game window on the given screen. + * Source: Miss Chanandler Bong & Peter Szabo on StackOverflow + * + * @param window the window to center + * @param screen the screen to center it on + */ private void setWindowPosition(JFrame window, int screen) { GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] allDevices = env.getScreenDevices(); @@ -157,12 +205,15 @@ public class TetrisUI extends JFrame implements KeyListener { window.setLocation(windowPosX, windowPosY); } + /** + * Capture keyboard inputs during a game session. + * + * @param e the key event + * @see eu.midnightdust.yaytris.game.Space + */ @Override public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { - case KeyEvent.VK_E: - Tetris.getSpace().spawnTetromino(); - break; case KeyEvent.VK_UP: case KeyEvent.VK_W: Tetris.getSpace().getCurrentTetromino().rotate(); diff --git a/src/main/java/eu/midnightdust/yaytris/util/CatppuccinColor.java b/src/main/java/eu/midnightdust/yaytris/util/CatppuccinColor.java index 1b4d622..480e87f 100644 --- a/src/main/java/eu/midnightdust/yaytris/util/CatppuccinColor.java +++ b/src/main/java/eu/midnightdust/yaytris/util/CatppuccinColor.java @@ -2,8 +2,8 @@ package eu.midnightdust.yaytris.util; import java.awt.*; -/* - Colors based on the [Catppuccin Mocha](https://github.com/catppuccin/catppuccin) color palette +/** + * Color scheme based on the Catppuccin Mocha color palette */ public enum CatppuccinColor { CRUST(0x11111b), MANTLE(0x181825), BASE(0x1e1e2e), SURFACE0(0x313244); diff --git a/src/main/java/eu/midnightdust/yaytris/util/NightJson.java b/src/main/java/eu/midnightdust/yaytris/util/NightJson.java index 0fbc5cc..44894c4 100644 --- a/src/main/java/eu/midnightdust/yaytris/util/NightJson.java +++ b/src/main/java/eu/midnightdust/yaytris/util/NightJson.java @@ -11,10 +11,10 @@ import java.util.function.Function; import java.util.regex.Matcher; import java.util.regex.Pattern; -/* - NightJson v0.2 by Martin Prokoph - Extremely lightweight (and incomplete) JSON library - Concept inspired by GSON +/** + * NightJson v0.2 by Martin Prokoph + * Extremely lightweight (and incomplete) JSON library + * Concept inspired by GSON */ public class NightJson { private static final String KEY_PATTERN = "\"(-?[A-Za-z-_.]*)\":"; @@ -22,6 +22,12 @@ public class NightJson { Field jsonMap; String fileLocation; + /** + * Initialize the NightJSON json reader & writer. + * + * @param jsonClass the java class that should be linked to the json file + * @param fileLocation the location the json file is read from and written to + */ public NightJson(Class jsonClass, String fileLocation) { this.jsonClass = jsonClass; this.fileLocation = fileLocation; @@ -30,6 +36,9 @@ public class NightJson { }); } + /** + * Convert the current state of the java json class to actual json and save it to disk. + */ @SuppressWarnings("unchecked") public void writeJson() { if (fileLocation == null) return; @@ -59,6 +68,9 @@ public class NightJson { } } + /** + * Write the desired element into the file. + */ private void writeElement(FileWriter jsonFile, Object value, Class type, String name, boolean hasNext) throws IOException, IllegalAccessException { jsonFile.write("\t"); if (type == Comment.class) { @@ -70,6 +82,9 @@ public class NightJson { jsonFile.write(hasNext ? ",\n" : "\n"); } + /** + * Converts the specified value object to its json representation. + */ private String objToString(Object value, Class type) { if (type == Map.class) { StringBuilder mapPairs = new StringBuilder(); @@ -88,6 +103,9 @@ public class NightJson { return String.format(type == String.class || type.isEnum() ? "\"%s\"" : "%s", value); } + /** + * Read the json file from disk and overwrite the json class's field values. + */ @SuppressWarnings("unchecked") public void readJson() { if (fileLocation == null) return; @@ -119,6 +137,9 @@ public class NightJson { } } + /** + * Read the json file as key-value pairs and save it as a map. + */ private Map jsonToMap(String jsonString, Function> keyToType) { Map map = new HashMap<>(); Iterator pairIterator = Arrays.stream(jsonString.replaceAll("(//)+.*\n", "").replaceFirst("[{]", "").split(",")).iterator(); @@ -134,10 +155,13 @@ public class NightJson { return map; } + /** + * Read the current key in the json file. + */ private Object getValue(String s, String key, Function> keyToType, Iterator pairIterator) { String val = s.split(KEY_PATTERN, 2)[1]; - if (s.contains("{")) { + if (s.contains("{")) { // Handle maps recursively StringBuilder submapString = new StringBuilder(); int level = charAmount(s, '{'); submapString.append(val); @@ -162,10 +186,19 @@ public class NightJson { } } + /** + * Count the amount of appearances of a char in a string. + * + * @param input the string to search in + * @param c the char to count + */ private int charAmount(String input, char c) { return (int) input.chars().filter(ch -> ch == c).count(); } + /** + * Converts the value from a json string to the actual field type. + */ private Object stringToObj(String value, Class type) { switch (type.getName()) { case "byte": return Byte.parseByte(value); @@ -180,15 +213,28 @@ public class NightJson { else return value; } + /** + * Gets the type arguments of typed data structures, such as lists or maps. + * @param field the associated field + * @param index the type index (relevant for maps) + */ private static Class getTypeArgument(Field field, int index) { return getPrimitiveType((Class) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[index]); } + /** + * Tries to get primitive types from non-primitives (e.g. Boolean -> boolean) + */ public static Class getPrimitiveType(Class rawType) { - try { return (Class) rawType.getField("TYPE").get(null); // Tries to get primitive types from non-primitives (e.g. Boolean -> boolean) + try { return (Class) rawType.getField("TYPE").get(null); } catch (NoSuchFieldException | IllegalAccessException ignored) { return rawType; } } + /** + * Get a field from a class by its name. + * @param name the field specifier + * @return an optional representation of the field + */ private Optional getField(String name) { try { return Optional.of(jsonClass.getField(name)); @@ -197,8 +243,17 @@ public class NightJson { } } + /** + * Add comments to your json files. + * If you decide to use this, it's best to save with the .json5 extension, as regular json does not officially support comments. + */ public static class Comment { final String commentString; + /** + * Add a comment to spice-up the json file :) + * @param commentString the string you want to write as a comment + * @param args optional formatting arguments, calls {@link String#format(String, Object...)} + */ public Comment(String commentString, Object... args) { this.commentString = String.format(commentString, args); } diff --git a/src/main/java/eu/midnightdust/yaytris/util/SoundEffect.java b/src/main/java/eu/midnightdust/yaytris/util/SoundEffect.java index cb29c19..63a987e 100644 --- a/src/main/java/eu/midnightdust/yaytris/util/SoundEffect.java +++ b/src/main/java/eu/midnightdust/yaytris/util/SoundEffect.java @@ -8,6 +8,10 @@ public enum SoundEffect { this.location = location; } + /** + * Play this sound effect aloud + * @see SoundUtil#playSoundClip(String) + */ public void play() { SoundUtil.playSoundClip(location); } diff --git a/src/main/java/eu/midnightdust/yaytris/util/SoundUtil.java b/src/main/java/eu/midnightdust/yaytris/util/SoundUtil.java index 727765c..3f9013f 100644 --- a/src/main/java/eu/midnightdust/yaytris/util/SoundUtil.java +++ b/src/main/java/eu/midnightdust/yaytris/util/SoundUtil.java @@ -11,6 +11,12 @@ import java.util.Map; public class SoundUtil { private static final Map musicThreads = new HashMap<>(); + /** + * Play the long audio file found at the specified location. + * + * @param fileLocation the URI of the desired audio file (should be a .wav file) + * @param looped whether to repeat the song indefinitely after it finishes + */ public static void playMusic(String fileLocation, boolean looped) { if (musicThreads.containsKey(fileLocation)) stopMusic(fileLocation); MusicThread musicThread = new MusicThread(fileLocation, looped); @@ -18,11 +24,22 @@ public class SoundUtil { musicThreads.put(fileLocation, musicThread); } + /** + * Stop the long audio file found at the specified location, if it is playing. + * Also has a nice smooth fade-out effect. + * + * @param fileLocation the URI of the desired audio file (should be a .wav file) + */ public static void stopMusic(String fileLocation) { if (musicThreads.containsKey(fileLocation)) musicThreads.get(fileLocation).stopMusic(); } - // Adapted from: https://www.baeldung.com/java-play-sound + /** + * Play a short audio clip found at the specified location. + * Adapted from: Baeldung + * + * @param fileLocation the URI of the desired audio file (should be a .wav file) + */ public static void playSoundClip(String fileLocation) { try (AudioInputStream stream = AudioSystem.getAudioInputStream(getResource(fileLocation))) { // FIXME: Support audio files from JAR. File streams won't work here for some reason. AudioFormat format = stream.getFormat(); @@ -42,7 +59,13 @@ public class SoundUtil { return SoundUtil.class.getResource(fileLocation); } - // Adapted from: https://stackoverflow.com/a/40698149 + /** + * Set the volume for the audio player. + * Adapted from: Steve Eynon on StackOverflow + * + * @param line the DataLine responsible for audio playback + * @param volume the requested volume in percent (0-100%) + */ private static void setVolume(Line line, int volume) { if (volume < 0 || volume > 100) throw new IllegalArgumentException("Volume not valid: " + volume); @@ -50,6 +73,9 @@ public class SoundUtil { gainControl.setValue(20f * (float) Math.log10(volume/100f)); } + /** + * Handle music in separate threads to not interrupt the main game + */ public static class MusicThread extends Thread { private static final int BUFFER_SIZE = 8192; private final boolean looped; @@ -71,7 +97,13 @@ public class SoundUtil { do {playMusic(fileLocation);} while (looped && playing); } - // Adapted from: https://www.baeldung.com/java-play-sound + /** + * INTERNAL! + * Play the long audio file found at the specified location. + * Fades out after calling {@link #stopMusic()}. + * Adapted from: Baeldung + * @see SoundUtil#playMusic(String, boolean) + */ private void playMusic(String fileLocation) { try (AudioInputStream stream = AudioSystem.getAudioInputStream(getResource(fileLocation))) { AudioFormat format = stream.getFormat(); diff --git a/src/main/java/eu/midnightdust/yaytris/util/Vec2i.java b/src/main/java/eu/midnightdust/yaytris/util/Vec2i.java index 5068204..78eeaaf 100644 --- a/src/main/java/eu/midnightdust/yaytris/util/Vec2i.java +++ b/src/main/java/eu/midnightdust/yaytris/util/Vec2i.java @@ -9,22 +9,36 @@ public class Vec2i { this.y = y; } - public static Vec2i of(int x) { - //noinspection SuspiciousNameCombination - return new Vec2i(x, x); - } + /** + * Creates a vector holding two signed ints. + * + * @param x the x value + * @param y the y value + */ public static Vec2i of(int x, int y) { return new Vec2i(x, y); } + /** + * @return the saved x value + */ public int getX() { return x; } + /** + * @return the saved y value + */ public int getY() { return y; } + /** + * Offset each coordinate of this vector by the same coordinate of another vector + * + * @param other the vector to add + * @return the sum of both vectors + */ public Vec2i offset(Vec2i other) { return new Vec2i(x + other.getX(), y + other.getY()); }