From 60f59ae503600bb61e5e00d6e4abe99ce3d5364b Mon Sep 17 00:00:00 2001 From: Siphalor Date: Mon, 20 Jul 2020 22:24:53 +0200 Subject: [PATCH] The whole logic documentation --- src/main/java/de/siphalor/was/Start.java | 7 + .../java/de/siphalor/was/WhatAStorage.java | 193 +++++++++++++++++- .../de/siphalor/was/assets/AssetsManager.java | 13 ++ .../siphalor/was/content/ContentManager.java | 46 ++++- .../de/siphalor/was/content/lang/I18n.java | 48 +++++ .../de/siphalor/was/content/lang/Lang.java | 31 +++ .../was/content/pack/ContentPack.java | 22 +- .../was/content/pack/FileContentPack.java | 35 +++- .../was/content/pack/JarContentPack.java | 48 ++++- .../siphalor/was/content/product/Product.java | 53 +++++ .../was/content/product/ProductManager.java | 27 +++ .../was/content/product/ProductType.java | 26 ++- .../product/dynamic/DynamicProduct.java | 62 +++++- .../product/dynamic/DynamicProductType.java | 88 +++++++- .../de/siphalor/was/content/quest/Quest.java | 35 ++++ .../was/content/quest/QuestGenerator.java | 3 + .../was/content/quest/QuestManager.java | 19 ++ .../content/quest/RandomQuestGenerator.java | 18 ++ .../content/quest/StaticQuestGenerator.java | 30 +++ .../content/resource/CallbackResource.java | 15 ++ .../was/content/resource/FileResource.java | 15 ++ .../was/content/resource/Resource.java | 18 ++ .../java/de/siphalor/was/game/Balance.java | 43 ++++ .../java/de/siphalor/was/game/Options.java | 35 ++++ .../java/de/siphalor/was/game/Storage.java | 30 +++ .../de/siphalor/was/game/StorageSlot.java | 44 +++- .../de/siphalor/was/game/Transaction.java | 47 +++++ src/main/java/de/siphalor/was/util/Pair.java | 45 ++++ .../was/util/PersistentInputStream.java | 12 ++ .../de/siphalor/was/util/ResourceManager.java | 12 ++ src/main/java/de/siphalor/was/util/Util.java | 36 +++- .../java/de/siphalor/was/visual/Visual.java | 42 ++++ .../siphalor/was/dummy/DummyContentPack.java | 6 +- .../de/siphalor/was/dummy/DummyProduct.java | 3 +- 34 files changed, 1182 insertions(+), 25 deletions(-) diff --git a/src/main/java/de/siphalor/was/Start.java b/src/main/java/de/siphalor/was/Start.java index 80f7376..137d54b 100644 --- a/src/main/java/de/siphalor/was/Start.java +++ b/src/main/java/de/siphalor/was/Start.java @@ -7,7 +7,14 @@ import de.siphalor.was.visual.JFXVisual; import java.util.logging.Level; +/** + * The main entry point class + */ public class Start { + /** + * The main program entry point + * @param args The args given from the console + */ public static void main(String[] args) { WhatAStorage was = WhatAStorage.getInstance(); was.reload(); diff --git a/src/main/java/de/siphalor/was/WhatAStorage.java b/src/main/java/de/siphalor/was/WhatAStorage.java index 6335a9c..b24aaab 100644 --- a/src/main/java/de/siphalor/was/WhatAStorage.java +++ b/src/main/java/de/siphalor/was/WhatAStorage.java @@ -26,33 +26,88 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; +/** + * The main game class. This is a singleton class. + */ public class WhatAStorage { + /** + * The maximum number of quests at a time + */ private static final int MAX_QUESTS = 3; + /** + * The size of the storage grid + */ private static final int GRID_SIZE = 3; + /** + * The window title + */ public static final String TITLE = "What a Storage"; + /** + * The singleton instance + */ private static final WhatAStorage INSTANCE = new WhatAStorage(); + /** + * Gets the singleton instance. + * @return The instance + */ public static WhatAStorage getInstance() { return INSTANCE; } + /** + * The main content manager + */ private final ContentManager contentManager; + /** + * The internal content pack + */ private final JarContentPack mainPack; + /** + * The main product manager + */ private final ProductManager productManager; + /** + * The main quest manager + */ private final QuestManager questManager; + /** + * The game's options + */ private final Options options; + /** + * The used visual + */ private Visual visual; + /** + * Whether the game has been requested to stop + */ private boolean stopScheduled; + /** + * The current balance + */ private Balance balance; + /** + * The current quest generator + */ private QuestGenerator questGenerator; + /** + * The current quest list + */ private final List quests = new ArrayList<>(MAX_QUESTS); + /** + * The current storage + */ private Storage storage; + /** + * Constructs a new instance. + */ private WhatAStorage() { contentManager = new ContentManager(); mainPack = new JarContentPack("", "content"); @@ -62,26 +117,49 @@ public class WhatAStorage { options = new Options(); } + /** + * Sets the visual to use. Must be called before {@link WhatAStorage#setup()} + * @param visual The visual to use + */ public void setVisual(Visual visual) { this.visual = visual; } + /** + * Gets the content manager. + * @return The content manager + */ public ContentManager getContentManager() { return contentManager; } + /** + * Gets the product manager. + * @return The product manager + */ public ProductManager getProductManager() { return productManager; } + /** + * Gets the quest manager. + * @return The quest manager + */ public QuestManager getQuestManager() { return questManager; } + /** + * Gets the game's options + * @return The game's options + */ public Options getOptions() { return options; } + /** + * Reloads all resources from the packs. + */ public void reload() { contentManager.clear(); @@ -105,6 +183,9 @@ public class WhatAStorage { Util.LOGGER.log(Level.INFO, "Reloaded game content"); } + /** + * Setups the game. + */ public void setup() { if (visual == null) { throw new IllegalStateException("No visual set for WhatAStorage!"); @@ -112,10 +193,16 @@ public class WhatAStorage { visual.setup(this); } + /** + * Runs the game. Hands over control to the visual. + */ public void run() { visual.run(); } + /** + * Start a new game. + */ public void startGame() { //questGenerator = questManager.get("example.test"); questGenerator = new RandomQuestGenerator(); @@ -132,19 +219,27 @@ public class WhatAStorage { } } + /** + * Request a game stop. + */ public void scheduleStop() { stopScheduled = true; visual.onScheduleStop(); } - public boolean isStopScheduled() { - return stopScheduled; - } - + /** + * Gets the balance. + * @return The balance + */ public Balance getBalance() { return balance; } + /** + * Gets all available language codes. + * @return The language codes. + */ + @NotNull public String[] getLangs() { return contentManager.getResources("lang", "properties").map(resource -> { int index = resource.getId().lastIndexOf('.'); @@ -152,15 +247,27 @@ public class WhatAStorage { }).toArray(String[]::new); } + /** + * Add a new transaction and propagate to the visual. + * @param type The transaction type + * @param change The money change + * @param product The reference product if any + */ private void addTransaction(@NotNull Transaction.Type type, int change, @Nullable Product product) { Transaction transaction = balance.add(type, change, product); visual.onBalanceChanged(balance.getBudget(), transaction, balance.getTotalIncome(), balance.getTotalLoss()); } + /** + * Indicate that the language has been changed. + */ public void invalidateI18n() { visual.invalidateI18n(); } + /** + * Requests that the main visual data (storage and budget) should be resend to the visual. + */ public void resendVisualGameData() { for (int x = 0; x < storage.getWidth(); x++) { for (int y = 0; y < storage.getHeight(); y++) { @@ -175,6 +282,10 @@ public class WhatAStorage { visual.onBalanceChanged(balance.getBudget(), new Transaction(Transaction.Type.NOOP, 0, ""), balance.getTotalIncome(), balance.getTotalLoss()); } + /** + * Gets and adds the next quest if possible. + * @return Returns whether a quest has been added. + */ private boolean nextQuest() { if (quests.size() >= MAX_QUESTS) return false; @@ -189,10 +300,18 @@ public class WhatAStorage { return true; } + /** + * Indicate that the user requested the next quest. + * @return Returns whether a quest has been added + */ public boolean userRequestQuest() { return nextQuest(); } + /** + * Indicate that the user abandoned a quest. + * @param index The quest's index in the quest list + */ public void userAbandonQuest(int index) { if (index >= quests.size()) { Util.LOGGER.log(Level.SEVERE, "Attempted to abandon non-existent quest!"); @@ -207,18 +326,46 @@ public class WhatAStorage { if (options.getAutoRefillQuests()) nextQuest(); } + /** + * Checks whether a product can be stored at the given storage location. + * @param product The product to check + * @param x The storage x position + * @param y The storage y position + * @return Returns whether the product is legal at that position + */ public boolean canStoreProduct(@Nullable Product product, int x, int y) { return product != null && storage.get(x, y).fits(product) && product.testY(y); } + /** + * Checks whether a quest's product can be stored at the given storage location. + * @param questIndex The quest's index in the quest list + * @param x The storage x position + * @param y The storage y position + * @return Returns whether the operation is legal + */ public boolean canStoreProduct(int questIndex, int x, int y) { return canStoreProduct(quests.get(questIndex).getProduct(), x, y); } + /** + * Checks whether a product can be moved from one storage position to the other. + * @param x1 The origin x position + * @param y1 The origin y position + * @param x2 The destination x position + * @param y2 The destination y position + * @return Returns whether the operation is legal + */ public boolean canMoveProduct(int x1, int y1, int x2, int y2) { return canStoreProduct(storage.get(x1, y1).front(), x2, y2); } + /** + * Indicate that the user wants to store a quest's product. + * @param questIndex The quest's index in the quest list + * @param x The storage x position + * @param y The storage y position + */ public void userStoreProduct(int questIndex, int x, int y) { Quest quest = quests.get(questIndex); if (quest != null && quest.getType() == Quest.Type.IN) { @@ -236,6 +383,13 @@ public class WhatAStorage { } } + /** + * Checks whether a product can fulfill a quest. + * @param questIndex The quest's index in the quest list + * @param x The storage x position of the product origin + * @param y The storage y position of the product origin + * @return Returns whether the operation is legal + */ public boolean canDeliverProduct(int questIndex, int x, int y) { Quest quest = quests.get(questIndex); if (quest != null && quest.getType() == Quest.Type.OUT) { @@ -244,6 +398,13 @@ public class WhatAStorage { return false; } + /** + * Indicate that that the user wants to deliver a product + * @param questIndex The quest's index in the quest list + * @param x The storage x position of the product origin + * @param y The storage y position of the product origin + * @return Returns whether the operation was successful + */ public boolean userDeliverProduct(int questIndex, int x, int y) { if (canDeliverProduct(questIndex, x, y)) { Product product = storage.get(x, y).front(); @@ -259,6 +420,12 @@ public class WhatAStorage { return false; } + /** + * Checks whether two quests can resolve against each other. + * @param in The first quest's index in the quest list + * @param out The first quest's index in the quest list + * @return Returns whether the operation is legal + */ public boolean canQuestsResolve(int in, int out) { int s = quests.size(); if (in < s && in >= 0 && out < s && out >= 0) { @@ -270,6 +437,12 @@ public class WhatAStorage { return false; } + /** + * Indicate that the user wants to resolve two quests. + * @param in The first quest's index in the quest list + * @param out The first quest's index in the quest list + * @return Returns whether the operation was successful + */ public boolean userResolveQuests(int in, int out) { if (options.getAllowQuestResolving()) { int s = quests.size(); @@ -298,6 +471,13 @@ public class WhatAStorage { return false; } + /** + * Indicate that the user want to move a product. + * @param x1 The origin x position + * @param y1 The origin y position + * @param x2 The destination x position + * @param y2 The destination y position + */ public void userMoveProduct(int x1, int y1, int x2, int y2) { Product product = storage.get(x1, y1).front(); if (product != null && product.testY(y2)) { @@ -312,6 +492,11 @@ public class WhatAStorage { } } + /** + * Indicate that the user wants to destroy a product. + * @param x The storage x position + * @param y The storage y position + */ public void userDestroyProduct(int x, int y) { Product product = storage.get(x, y).front(); int z = storage.get(x, y).pop(); diff --git a/src/main/java/de/siphalor/was/assets/AssetsManager.java b/src/main/java/de/siphalor/was/assets/AssetsManager.java index 4ae3c26..0b0167c 100644 --- a/src/main/java/de/siphalor/was/assets/AssetsManager.java +++ b/src/main/java/de/siphalor/was/assets/AssetsManager.java @@ -6,13 +6,26 @@ import java.io.InputStream; import java.net.URL; import java.util.Optional; +/** + * This class controls the game's assets. Assets are all the file in the assets package in the jar. It can mostly be safely assumed that the certain asset files exist. + */ public class AssetsManager { + /** + * Returns a new InputStream to the requested asset. + * @param path The path beginning from the assets package + * @return An optional with the input stream if the asset exists + */ @NotNull public static Optional getStream(@NotNull String path) { return Optional.ofNullable(Thread.currentThread().getContextClassLoader().getResourceAsStream("assets/" + path)); } + /** + * Returns an URL to the requested asset. + * @param path The path beginning from the assets package + * @return An optional with the url if the asset exists + */ @NotNull public static Optional getURL(@NotNull String path) { return Optional.ofNullable(Thread.currentThread().getContextClassLoader().getResource(path)); diff --git a/src/main/java/de/siphalor/was/content/ContentManager.java b/src/main/java/de/siphalor/was/content/ContentManager.java index 43d0433..08e6100 100644 --- a/src/main/java/de/siphalor/was/content/ContentManager.java +++ b/src/main/java/de/siphalor/was/content/ContentManager.java @@ -10,34 +10,66 @@ import java.util.List; import java.util.Optional; import java.util.stream.Stream; +/** + * The main manager that for holding and accessing resources in {@link ContentPack}s. + */ public class ContentManager { + /** + * All contained content packs + */ private final List packs = new LinkedList<>(); + /** + * Constructs a new, empty content manager. + */ public ContentManager() { } + /** + * Forgets all known content packs. + */ public void clear() { packs.clear(); } + /** + * Adds a new content pack to this manager. + * @param pack The pack to add + */ public void addPack(ContentPack pack) { packs.add(pack); } + /** + * Gets all contained content packs. + * @return A collection of all packs + */ public Collection getPacks() { return packs; } + /** + * Gets all resources that exist in all content packs in the given location with the given extension. Finds nested resources. + * @param location The base location where to find resources in + * @param type The file extension to look for (without the dot) + * @return A stream of all matching resources + */ @NotNull public Stream getResources(@NotNull String location, @NotNull String type) { return packs.stream().flatMap(pack -> pack.getResources(location, type)).distinct(); } + /** + * Gets the first resource with the given identifier. + * @param identifier The resource identifier + * @return The found resource or empty if none of the packs contains such a resource + * @see ContentManager#getAllOfResource(String) + */ @NotNull - public Optional getResource(@NotNull String location) { + public Optional getResource(@NotNull String identifier) { Resource resource; for (ContentPack pack : packs) { - resource = pack.getResource(location); + resource = pack.getResource(identifier); if (resource != null) { return Optional.of(resource); } @@ -45,8 +77,14 @@ public class ContentManager { return Optional.empty(); } + /** + * Gets all resources with the given identifier in the content packs. + * @param identifier The resource identifier to look for + * @return A stream of all those resources + * @see ContentManager#getResource(String) + */ @NotNull - public Stream getAllOfResource(@NotNull String location) { - return packs.stream().flatMap(pack -> Stream.ofNullable(pack.getResource(location))); + public Stream getAllOfResource(@NotNull String identifier) { + return packs.stream().flatMap(pack -> Stream.ofNullable(pack.getResource(identifier))); } } diff --git a/src/main/java/de/siphalor/was/content/lang/I18n.java b/src/main/java/de/siphalor/was/content/lang/I18n.java index 6be24fe..7b7d3c5 100644 --- a/src/main/java/de/siphalor/was/content/lang/I18n.java +++ b/src/main/java/de/siphalor/was/content/lang/I18n.java @@ -6,26 +6,55 @@ import org.jetbrains.annotations.Nullable; import java.util.*; +/** + * A singleton class handling all of the translation actions and the currently active language with a fallback language + */ public class I18n extends ResourceBundle { + /** + * The language code of the fallback language + */ public static final String DEFAULT = "en_us"; + /** + * The fallback language + */ private final Lang DEFAULT_LANG = new Lang(DEFAULT); + /** + * The class' instance + */ private static final I18n INSTANCE = new I18n(); + /** + * Gets the singleton instance + * @return the singleton instance + */ public static I18n getInstance() { return INSTANCE; } + /** + * the current language + */ @Nullable private Lang lang; + /** + * Returns the current language + * @return the currently selected language or the fallback + */ public Lang getLang() { if (lang != null) return lang; return DEFAULT_LANG; } + /** + * Sets the current language to the language identified by code. It loads the language using the given {@link ContentManager} + * @param code The language's identification code + * @param contentManager The content manager to use + * @return Returns whether the language was updated + */ public boolean setLang(@NotNull String code, @NotNull ContentManager contentManager) { if (lang == null || !lang.getCode().equals(code)) { Lang l = new Lang(code); @@ -37,6 +66,10 @@ public class I18n extends ResourceBundle { return false; } + /** + * Reloads the current and the fallback language language using the given content manager. + * @param contentManager The content manager to use + */ public void reload(@NotNull ContentManager contentManager) { DEFAULT_LANG.load(contentManager); if (lang != null) { @@ -44,11 +77,22 @@ public class I18n extends ResourceBundle { } } + /** + * Translates and formats with the translation string. + * @param key The translation key + * @param args The arguments to use for formatting + * @return The formatted string + */ @NotNull public String format(@NotNull String key, @Nullable Object... args) { return String.format(getString(key), args); } + /** + * Loads the given data by the translation key. + * @param key The translation key + * @return The associated data or null if none + */ @Override @NotNull protected Object handleGetObject(@NotNull String key) { @@ -68,6 +112,10 @@ public class I18n extends ResourceBundle { return val; } + /** + * Returns all valid keys existing in the current language or the fallback language. + * @return An enumeration of all of these keys + */ @NotNull @Override public Enumeration getKeys() { diff --git a/src/main/java/de/siphalor/was/content/lang/Lang.java b/src/main/java/de/siphalor/was/content/lang/Lang.java index fd72806..6f88094 100644 --- a/src/main/java/de/siphalor/was/content/lang/Lang.java +++ b/src/main/java/de/siphalor/was/content/lang/Lang.java @@ -11,19 +11,41 @@ import java.util.Enumeration; import java.util.Properties; import java.util.logging.Level; +/** + * A class for holding a language's data + */ public class Lang { + /** + * The language's data + */ private final String code; + /** + * The internally used properties file + */ private final Properties properties = new Properties(); + /** + * Constructs a new language by its identification code. + * @param code The identifying code + */ public Lang(@NotNull String code) { this.code = code; } + /** + * Gets the language's code. + * @return The identifying code for this language + */ @NotNull public String getCode() { return code; } + /** + * Loads the language's data using the given content manager. + * @param contentManager The content manager to use + * @return Returns whether the load was successful + */ public boolean load(@NotNull ContentManager contentManager) { properties.clear(); contentManager.getAllOfResource("lang/" + code + ".properties").forEachOrdered(resource -> { @@ -50,11 +72,20 @@ public class Lang { return true; } + /** + * Gets the translation string + * @param key The key for the translation + * @return Returns the translation data + */ @Nullable public String get(@NotNull String key) { return (String) properties.get(key); } + /** + * Gets all of the translation keys + * @return Returns all of the translation keys + */ @NotNull public Enumeration getKeys() { // This is really ugly but we know™ that the keys will always be strings diff --git a/src/main/java/de/siphalor/was/content/pack/ContentPack.java b/src/main/java/de/siphalor/was/content/pack/ContentPack.java index 5678b01..fb2d338 100644 --- a/src/main/java/de/siphalor/was/content/pack/ContentPack.java +++ b/src/main/java/de/siphalor/was/content/pack/ContentPack.java @@ -6,11 +6,31 @@ import org.jetbrains.annotations.Nullable; import java.util.stream.Stream; +/** + * An interface for some kind of content pack + */ public interface ContentPack { + /** + * Returns a stream to all of the resources in the given location and the given file extension. Search is performed recursively. + * @param location The location to search + * @param type The file extension + * @return Returns a stream of all of the resources + */ @NotNull Stream getResources(@NotNull String location, @NotNull String type); + + /** + * Gets the resource with the specified identifier. + * @param identifier + * @return The resource or null if it doesn't exist + */ @Nullable - Resource getResource(@NotNull String location); + Resource getResource(@NotNull String identifier); + + /** + * Returns the id of the content pack + * @return The unique identifier + */ @NotNull String getId(); } diff --git a/src/main/java/de/siphalor/was/content/pack/FileContentPack.java b/src/main/java/de/siphalor/was/content/pack/FileContentPack.java index e668f08..46484cd 100644 --- a/src/main/java/de/siphalor/was/content/pack/FileContentPack.java +++ b/src/main/java/de/siphalor/was/content/pack/FileContentPack.java @@ -11,15 +11,35 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.stream.Stream; +/** + * An implementation of a {@link ContentPack} as a directory. + */ public class FileContentPack implements ContentPack { + /** + * The pack's id + */ private final String id; + /** + * The base path where the pack is located + */ private final Path base; + /** + * Constructs a new file content pack. + * @param id The pack id + * @param base The path to the pack directory + */ public FileContentPack(@NotNull String id, @NotNull Path base) { this.id = id; this.base = base; } + /** + * Gets all of the resources in the specified location with the specified type + * @param location The location to search + * @param type The file extension + * @return Returns a stream of these resources + */ @Override public @NotNull Stream getResources(@NotNull String location, @NotNull String type) { final String extension = "." + type; @@ -36,15 +56,24 @@ public class FileContentPack implements ContentPack { return Stream.empty(); } + /** + * Gets a single file resource with the given identifier. + * @param identifier The resource identifier + * @return Returns the file resource or null if it doesn't exist + */ @Override - public Resource getResource(@NotNull String location) { - File file = base.resolve(Path.of(location)).toFile(); + public Resource getResource(@NotNull String identifier) { + File file = base.resolve(Path.of(identifier)).toFile(); if (file.isFile()) { - return new FileResource(Util.pathToId(id, location), file); + return new FileResource(Util.pathToId(id, identifier), file); } return null; } + /** + * Get the pack's id + * @return The pack's id + */ @Override @NotNull public String getId() { diff --git a/src/main/java/de/siphalor/was/content/pack/JarContentPack.java b/src/main/java/de/siphalor/was/content/pack/JarContentPack.java index 77e9044..db38a0e 100644 --- a/src/main/java/de/siphalor/was/content/pack/JarContentPack.java +++ b/src/main/java/de/siphalor/was/content/pack/JarContentPack.java @@ -20,25 +20,58 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.stream.Stream; +/** + * An implementation for a {@link ContentPack} in a loaded jar file. + */ public class JarContentPack implements ContentPack { + /** + * The pack's id + */ private final String id; + /** + * The base package path + */ private final String baseLocation; + /** + * The class loader that's used for resource loading + */ private final ClassLoader classLoader; + /** + * Constructs a new jar content pack based at the root package with the current classloader. + * @param id The pack's id + */ public JarContentPack(String id) { this(id, ""); } + /** + * Constructs a new jar content pack based at the given package with the current classloader. + * @param id The pack's id + * @param baseLocation The base package to load resources from + */ public JarContentPack(String id, String baseLocation) { this(id, baseLocation, Thread.currentThread().getContextClassLoader()); } + /** + * Constructs a new jar content pack based at the given package with the given classloader. + * @param id The pack's id + * @param baseLocation The base package to load resources from + * @param classLoader The classloader to use + */ public JarContentPack(String id, String baseLocation, ClassLoader classLoader) { this.id = id; this.baseLocation = baseLocation; this.classLoader = classLoader; } + /** + * Finds all the resources in the given package with the given file extension. + * @param location The location to search + * @param type The file extension + * @return A stream of these resources + */ // Inspired from this: https://stackoverflow.com/a/48190582/7582022 @Override @NotNull @@ -83,16 +116,25 @@ public class JarContentPack implements ContentPack { return Stream.empty(); } + /** + * Gets the resource identified by the given id. + * @param identifier The resource's id + * @return Returns the resource or null if it doesn't exist + */ @Override @Nullable - public Resource getResource(@NotNull String location) { - URL url = classLoader.getResource(baseLocation + "/" + location); + public Resource getResource(@NotNull String identifier) { + URL url = classLoader.getResource(baseLocation + "/" + identifier); if (url != null) { - return new CallbackResource(Util.pathToId(id, location), () -> classLoader.getResourceAsStream(baseLocation + "/" + location)); + return new CallbackResource(Util.pathToId(id, identifier), () -> classLoader.getResourceAsStream(baseLocation + "/" + identifier)); } return null; } + /** + * Gets the id of this pack. + * @return The pack's id + */ @NotNull @Override public String getId() { diff --git a/src/main/java/de/siphalor/was/content/product/Product.java b/src/main/java/de/siphalor/was/content/product/Product.java index 270fa9c..9f9ca67 100644 --- a/src/main/java/de/siphalor/was/content/product/Product.java +++ b/src/main/java/de/siphalor/was/content/product/Product.java @@ -4,29 +4,74 @@ import org.jetbrains.annotations.NotNull; import java.util.ResourceBundle; +/** + * An interface for a generic product + */ public interface Product { + /** + * The property specifier. Should contain all property values separated by low dashes. + * @return The specifier + */ @NotNull String getPropertySpecifier(); + + /** + * Gets all the property values + * @return The values as array + */ String[] getProperties(); + /** + * Gets this product's depth + * @return The depth + */ int getDepth(); + + /** + * Checks whether the product is allowed to go into the specified y layer. + * @param y The y layer to test + * @return Whether the product is allowed to go into that y layer + */ boolean testY(int y); + /** + * Gets the products type. + * @return Its type + */ @NotNull ProductType getType(); + /** + * Checks the two products for equality. + * @param product A product to check + * @return Whether they match + */ boolean equals(Product product); + /** + * Gets the translation key for this product. Usually the same as the products specific. + * @return The translation key + */ @NotNull default String getTranslationKey() { return "products." + getType().getId(); } + /** + * Gets the name of this product. This will + * @param i18n The resources to load the translation from + * @return The localized name + */ @NotNull default String getName(@NotNull ResourceBundle i18n) { return i18n.getString(getTranslationKey()); } + /** + * Gets the localized description for this product. This includes all the properties their values. + * @param i18n The resources to load the translations from + * @return The localized description + */ @NotNull default String getDescription(@NotNull ResourceBundle i18n) { String[] props = getType().getProperties(); @@ -46,11 +91,19 @@ public interface Product { return res.toString(); } + /** + * Gets the resource location for this product + * @return The resource location + */ @NotNull default String getTextureLocation() { return "textures/products/" + getType().getId() + "/" + getPropertySpecifier() + ".png"; } + /** + * Return the placeholder texture for products. Why isn't this static? + * @return The resource location + */ @NotNull static String getPlaceholderTextureLocation() { return "textures/products/package.png"; diff --git a/src/main/java/de/siphalor/was/content/product/ProductManager.java b/src/main/java/de/siphalor/was/content/product/ProductManager.java index 2cac27b..3213b9b 100644 --- a/src/main/java/de/siphalor/was/content/product/ProductManager.java +++ b/src/main/java/de/siphalor/was/content/product/ProductManager.java @@ -10,14 +10,27 @@ import java.io.IOException; import java.io.InputStream; import java.util.*; +/** + * A class which manages the loading and access to products. + */ public class ProductManager implements ResourceManager> { + /** + * Map with all {@link ProductType}s mapped by their ids. + */ private final Map> productTypes = new HashMap<>(); + /** + * Removes all loaded products + */ @Override public void clear() { productTypes.clear(); } + /** + * Loads all products from the given content manager. + * @param contentManager The content manager to load from + */ public void reload(@NotNull ContentManager contentManager) { contentManager.getResources("products", "properties").forEach(resource -> { InputStream inputStream = resource.getInputStream(); @@ -32,17 +45,31 @@ public class ProductManager implements ResourceManager> { }); } + /** + * Gets a single product type by its id. + * @param id The id + * @return The associated product type or null if no product type with this id exists + */ @Override @Nullable public ProductType get(String id) { return productTypes.get(id); } + /** + * Gets all loaded product types. + * @return All loaded product types + */ @NotNull public Collection> getTypes() { return productTypes.values(); } + /** + * Returns a random product out of all product types. Randomness is distributed equally over all types not over the product variants. + * @param random The random instance to use + * @return A random product or null if no products are loaded + */ @Nullable public Product randomProduct(Random random) { if (!productTypes.isEmpty()) { diff --git a/src/main/java/de/siphalor/was/content/product/ProductType.java b/src/main/java/de/siphalor/was/content/product/ProductType.java index 4d75671..c8116a2 100644 --- a/src/main/java/de/siphalor/was/content/product/ProductType.java +++ b/src/main/java/de/siphalor/was/content/product/ProductType.java @@ -5,11 +5,35 @@ import org.jetbrains.annotations.Nullable; import java.util.Random; +/** + * Interface for product types. + * @param The base class vor all products of this type + */ public interface ProductType { + /** + * Gets a product of this type with the given values as properties. + * @param values Some properties + * @return The product or null if no such product exists + */ @Nullable T getProduct(@NotNull String[] values); + + /** + * Gets the id of this type. + * @return The id + */ @NotNull String getId(); + + /** + * Gets all property ids. + * @return An array of the property ids + */ @NotNull String[] getProperties(); - @NotNull Product randomProduct(Random random); + /** + * Gets a random product of this type. + * @param random The random instance to use + * @return A random product + */ + @NotNull T randomProduct(Random random); } diff --git a/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java b/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java index b904ab0..6b2da08 100644 --- a/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java +++ b/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProduct.java @@ -8,62 +8,122 @@ import org.jetbrains.annotations.NotNull; import java.util.Arrays; import java.util.function.IntPredicate; +/** + * An implementation of {@link Product} that dynamically loads its data from a properties file + */ public class DynamicProduct implements Product { + /** + * The product's type + */ private ProductType type; + /** + * The property values + */ private final String[] properties; + /** + * The depth of this product + */ private int depth = 1; + /** + * A function that checks if the product can go into the given y location + */ @NotNull private IntPredicate yPredicate = Util.dummyIntPredicate(); + /** + * Constructs a new product with the given values as properties. + * @param properties The property values + */ public DynamicProduct(String[] properties) { this.properties = properties; } + /** + * Gets an identifier that uniquely represents the property values + * @return A concatenated list of the property values + */ @Override @NotNull public String getPropertySpecifier() { return String.join("_", properties); } + /** + * Gets the property values + * @return The property values + */ @Override public String[] getProperties() { return properties; } + /** + * Sets the depth of this product + * @param depth The depth + */ public void setDepth(int depth) { this.depth = depth; } + /** + * Gets the product's depth. + * @return The depth + */ @Override public int getDepth() { return depth; } + /** + * Test if the product can go into the given y layer. + * @param y The given y layer + * @return Whether the product is allowed to go into it + */ @Override public boolean testY(int y) { return yPredicate.test(y); } - // This function exists to resolve the circular references of ProductType <-> Product on creation + /** + * Sets the type of this product. This is needed because the {@link DynamicProductType} needs to know all of its products and all products need to know their type + * @param type The products type + */ void setType(ProductType type) { this.type = type; } + /** + * Gets the product's type. + * @return Returns the type. + */ @Override @NotNull public ProductType getType() { return type; } + /** + * Checks if both products match. + * @param product The other product + * @return Returns whether both products match + */ @Override public boolean equals(Product product) { return super.equals(product); } + /** + * Sets the function for checking the y layer. + * @param yPredicate The function + */ public void setYPredicate(@NotNull IntPredicate yPredicate) { this.yPredicate = yPredicate; } + /** + * A toString function. Nothing to explain + * @return The object and its data converted to a string + */ @Override public String toString() { return "DynamicProduct{" + diff --git a/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProductType.java b/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProductType.java index a447e84..c5863bc 100644 --- a/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProductType.java +++ b/src/main/java/de/siphalor/was/content/product/dynamic/DynamicProductType.java @@ -4,6 +4,7 @@ import de.siphalor.was.content.product.Product; import de.siphalor.was.content.product.ProductType; import de.siphalor.was.util.Util; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; import java.io.InputStream; @@ -11,11 +12,30 @@ import java.util.*; import java.util.function.IntPredicate; import java.util.stream.Collectors; +/** + * An implementation for {@link ProductType} that dynamically reads its data and products from a properties file. + */ public class DynamicProductType implements ProductType { + /** + * The type's id + */ private final String id; + /** + * All of the products mapped by their property specifiers. + * @see Product#getPropertySpecifier() + */ private final Map variations; + /** + * The property ids + */ private final String[] properties; + /** + * Constructs a new dynamic product type. + * @param id The id + * @param variations All of the products that are derived of this type + * @param properties The property ids + */ public DynamicProductType(String id, List variations, String[] properties) { this.id = id; this.variations = new HashMap<>(); @@ -26,6 +46,12 @@ public class DynamicProductType implements ProductType { this.properties = properties; } + /** + * Deserializes a product type from an input stream with a properties file + * @param id The type's id + * @param inputStream The input stream containing a properties file + * @return Returns the constructed product type + */ @NotNull public static DynamicProductType from(@NotNull String id, @NotNull InputStream inputStream) { Properties propertiesFile = new Properties(); @@ -83,6 +109,13 @@ public class DynamicProductType implements ProductType { return new DynamicProductType(id, List.of(new DynamicProduct(Util.emptyArray())), Util.emptyArray()); } + /** + * Creates a new product prototype for the given property value. + * @param propertiesFile The properties file to load from + * @param property The property id to target + * @param variant The property value to target. + * @return Returns a new prototype + */ private static ProductPrototype makePrototype(Properties propertiesFile, String property, String variant) { ProductPrototype prototype = new ProductPrototype(variant); String base = property + "." + variant + "."; @@ -111,41 +144,85 @@ public class DynamicProductType implements ProductType { return prototype; } + /** + * Gets the product with the given values. + * @param values The properties' values + * @return Returns the associated product or null if it's nonexistent + */ @Override + @Nullable public DynamicProduct getProduct(@NotNull String[] values) { return variations.get(String.join("_", values)); } + /** + * Gets the type's id + * @return The id + */ @Override public @NotNull String getId() { return id; } + /** + * Gets the property ids + * @return The property ids + */ @Override public @NotNull String[] getProperties() { return properties; } + /** + * Retrieves a random product from the variations. + * @param random An instance of {@link Random} to use for the calculation + * @return A random product + */ @Override - public @NotNull Product randomProduct(Random random) { + public @NotNull DynamicProduct randomProduct(Random random) { List products = new ArrayList<>(variations.values()); return products.get(random.nextInt(products.size())); } + /** + * Gets all of the products mapped by their property specifier. + * @return A map of those + * @see DynamicProduct#getPropertySpecifier() + */ public Map getVariations() { return variations; } + /** + * A prototype used for building the product variations + */ private static class ProductPrototype { + /** + * The property specifier with all properties that already have been used + */ String value; + /** + * The depth of the prototype. null if it should be inherited + */ Integer depth = null; + /** + * The y tester of the prototype. null if it should be inherited + */ IntPredicate yPredicate = null; + /** + * Constructs a new prototype with the given property specifier + * @param value The property specifier + */ public ProductPrototype(String value) { this.value = value; } + /** + * Applies the prototypes' data to the given product + * @param product The product which shall get adapted + */ public void apply(DynamicProduct product) { if (depth != null) product.setDepth(depth); @@ -153,6 +230,11 @@ public class DynamicProductType implements ProductType { product.setYPredicate(yPredicate); } + /** + * Merges two prototypes into each other. The result has the properties of both. The other prototype is recessive + * @param prototype The other prototype + * @return A newly constructed prototype with the calculated properties. + */ public ProductPrototype combine(ProductPrototype prototype) { ProductPrototype result = new ProductPrototype(value + ";" + prototype.value); if (prototype.depth != null) @@ -167,6 +249,10 @@ public class DynamicProductType implements ProductType { } } + /** + * Converts to string. That's all. + * @return The stringified product type. + */ @Override public String toString() { return "DynamicProductType{" + diff --git a/src/main/java/de/siphalor/was/content/quest/Quest.java b/src/main/java/de/siphalor/was/content/quest/Quest.java index f9493ec..85dd941 100644 --- a/src/main/java/de/siphalor/was/content/quest/Quest.java +++ b/src/main/java/de/siphalor/was/content/quest/Quest.java @@ -3,33 +3,68 @@ package de.siphalor.was.content.quest; import de.siphalor.was.content.product.Product; import org.jetbrains.annotations.NotNull; +/** + * Represents a quest. + */ public class Quest { + /** + * The type of this quest. + */ @NotNull private final Type type; + /** + * The quest reward/punishment + */ private final int reward; + /** + * The product to store/deliver + */ @NotNull private final Product product; + /** + * Constructs a new quest. + * @param type The type + * @param reward The reward/punishment + * @param product The product to store/deliver + */ public Quest(@NotNull Type type, int reward, @NotNull Product product) { this.product = product; this.reward = reward; this.type = type; } + /** + * Gets the product to store/deliver. + * @return The product + */ @NotNull public Product getProduct() { return product; } + /** + * Gets the type of this quest. + * @return The type + */ @NotNull public Type getType() { return type; } + /** + * Gets the reward/punishment for this quest. + * @return The reward/punishment + */ public int getReward() { return reward; } + /** + * Represents the type of a quest.
+ * IN for incoming (store)
+ * OUT for outgoing (deliver) + */ public enum Type { IN, OUT } diff --git a/src/main/java/de/siphalor/was/content/quest/QuestGenerator.java b/src/main/java/de/siphalor/was/content/quest/QuestGenerator.java index ce4b73a..5feb21a 100644 --- a/src/main/java/de/siphalor/was/content/quest/QuestGenerator.java +++ b/src/main/java/de/siphalor/was/content/quest/QuestGenerator.java @@ -2,6 +2,9 @@ package de.siphalor.was.content.quest; import java.util.Enumeration; +/** + * A class that provides the game with quests and optionally finishes. + */ public interface QuestGenerator extends Enumeration { /** * Restarts this generator diff --git a/src/main/java/de/siphalor/was/content/quest/QuestManager.java b/src/main/java/de/siphalor/was/content/quest/QuestManager.java index 6e57257..48b04d8 100644 --- a/src/main/java/de/siphalor/was/content/quest/QuestManager.java +++ b/src/main/java/de/siphalor/was/content/quest/QuestManager.java @@ -10,14 +10,28 @@ import java.io.InputStream; import java.util.HashMap; import java.util.Map; +/** + * This class handles the loading and access of quest generators. + */ public class QuestManager implements ResourceManager { + /** + * All of the quest generators mapped by their ids + */ private final Map questGenerators = new HashMap<>(); + /** + * Clears all loaded quest generators. + */ @Override public void clear() { questGenerators.clear(); } + /** + * Loads all quest generators from the given content manager + * @param contentManager The content manager to load from + * @param productManager The product manager to check product definitions against + */ public void reload(@NotNull ContentManager contentManager, @NotNull ProductManager productManager) { contentManager.getResources("quests", "csv").forEach(resource -> { InputStream inputStream = resource.getInputStream(); @@ -32,6 +46,11 @@ public class QuestManager implements ResourceManager { }); } + /** + * Gets a quest generator by its id. + * @param id The id + * @return The associated quest generator + */ @Override public QuestGenerator get(String id) { return questGenerators.get(id); diff --git a/src/main/java/de/siphalor/was/content/quest/RandomQuestGenerator.java b/src/main/java/de/siphalor/was/content/quest/RandomQuestGenerator.java index e3e0e25..bf9daac 100644 --- a/src/main/java/de/siphalor/was/content/quest/RandomQuestGenerator.java +++ b/src/main/java/de/siphalor/was/content/quest/RandomQuestGenerator.java @@ -5,18 +5,36 @@ import de.siphalor.was.content.product.Product; import java.util.Random; +/** + * An implementation of {@link QuestGenerator} that randomly spits out all kinds of quests with all kinds of products loaded. + */ public class RandomQuestGenerator implements QuestGenerator { + /** + * The internal random instance + */ private static final Random RANDOM = new Random(); + + /** + * Does nothing. + */ @Override public void restart() { } + /** + * This runs infinitely, so always true. + * @return true + */ @Override public boolean hasMoreElements() { return true; } + /** + * Gets the next quest. + * @return Another quest + */ @Override public Quest nextElement() { Product product = WhatAStorage.getInstance().getProductManager().randomProduct(RANDOM); diff --git a/src/main/java/de/siphalor/was/content/quest/StaticQuestGenerator.java b/src/main/java/de/siphalor/was/content/quest/StaticQuestGenerator.java index 829efb7..24987fb 100644 --- a/src/main/java/de/siphalor/was/content/quest/StaticQuestGenerator.java +++ b/src/main/java/de/siphalor/was/content/quest/StaticQuestGenerator.java @@ -17,15 +17,34 @@ import java.util.List; import java.util.Locale; import java.util.logging.Level; +/** + * A quest generator that follows a static list of quests. + */ public class StaticQuestGenerator implements QuestGenerator { + /** + * The list of quests to follow + */ @NotNull private final List quests; + /** + * The current index in the quest list + */ private int index = 0; + /** + * Constructs a new quest generator with the given quest list. + * @param quests The quest list to use + */ public StaticQuestGenerator(@NotNull List quests) { this.quests = quests; } + /** + * Loads a new static quest generator from the given input stream. The input stream should contain CSV format. + * @param inputStream An input stream with csv format + * @param productManager The product manager to check product definitions against + * @return The newly loaded quest generator or null if the laoding failed + */ @Nullable public static StaticQuestGenerator fromCsv(@NotNull InputStream inputStream, @NotNull ProductManager productManager) { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); @@ -69,16 +88,27 @@ public class StaticQuestGenerator implements QuestGenerator { return null; } + /** + * Restarts this generator. + */ @Override public void restart() { index = 0; } + /** + * Gets the next quest and advances. + * @return The next quest + */ @Override public Quest nextElement() { return quests.get(index++); } + /** + * Gets whether there are still new quests in the list. + * @return Whether there are quests left + */ @Override public boolean hasMoreElements() { return index < quests.size(); diff --git a/src/main/java/de/siphalor/was/content/resource/CallbackResource.java b/src/main/java/de/siphalor/was/content/resource/CallbackResource.java index ea92eb9..cc76e18 100644 --- a/src/main/java/de/siphalor/was/content/resource/CallbackResource.java +++ b/src/main/java/de/siphalor/was/content/resource/CallbackResource.java @@ -5,14 +5,29 @@ import org.jetbrains.annotations.Nullable; import java.io.InputStream; import java.util.function.Supplier; +/** + * An implementation for {@link Resource} that runs a callback to get the input stream. + */ public class CallbackResource extends Resource { + /** + * The callback to run for the input stream + */ Supplier supplier; + /** + * Constructs a new callback resource. + * @param id The id + * @param supplier The supplier that will provide the input stream + */ public CallbackResource(String id, Supplier supplier) { super(id); this.supplier = supplier; } + /** + * Runs the callback to get the input stream. + * @return An input stream or null if the callback failed + */ @Override public @Nullable InputStream getInputStream() { return supplier.get(); diff --git a/src/main/java/de/siphalor/was/content/resource/FileResource.java b/src/main/java/de/siphalor/was/content/resource/FileResource.java index d65d431..dc1f037 100644 --- a/src/main/java/de/siphalor/was/content/resource/FileResource.java +++ b/src/main/java/de/siphalor/was/content/resource/FileResource.java @@ -5,14 +5,29 @@ import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; +/** + * A file based implementation for {@link Resource}. + */ public class FileResource extends Resource { + /** + * The file that this resource points to + */ private final File file; + /** + * Constructs a new file resource + * @param id The id to use + * @param file The file to point to + */ public FileResource(String id, File file) { super(id); this.file = file; } + /** + * Opens a new {@link FileInputStream} to the contained file. + * @return The input stream or null if opening the stream failed + */ @Override public InputStream getInputStream() { try { diff --git a/src/main/java/de/siphalor/was/content/resource/Resource.java b/src/main/java/de/siphalor/was/content/resource/Resource.java index fd44db1..08e913f 100644 --- a/src/main/java/de/siphalor/was/content/resource/Resource.java +++ b/src/main/java/de/siphalor/was/content/resource/Resource.java @@ -4,16 +4,34 @@ import org.jetbrains.annotations.Nullable; import java.io.InputStream; +/** + * A representation for some kind of resource. + */ public abstract class Resource { + /** + * The resource's id + */ private final String id; + /** + * Constructs a new resource with the given id. + * @param id The id + */ protected Resource(String id) { this.id = id; } + /** + * Gets an input stream for this resource. + * @return An input stream to the resource + */ @Nullable public abstract InputStream getInputStream(); + /** + * Gets the id of this resource. Usually namespaced + * @return The id + */ public String getId() { return id; } diff --git a/src/main/java/de/siphalor/was/game/Balance.java b/src/main/java/de/siphalor/was/game/Balance.java index 90a458c..7e465e0 100644 --- a/src/main/java/de/siphalor/was/game/Balance.java +++ b/src/main/java/de/siphalor/was/game/Balance.java @@ -7,16 +7,43 @@ import org.jetbrains.annotations.Nullable; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +/** + * Represents the current budget and a transaction history. + */ public class Balance { + /** + * The current budget + */ private int budget = 0; + /** + * The total money gained + */ private int totalIncome = 0; + /** + * The total money lost + */ private int totalLoss = 0; + /** + * The transaction history + */ private final Queue history = new ConcurrentLinkedQueue<>(); + /** + * Adds a new transaction to the history. + * @param type The transaction type + * @param change The amount of money received/lost + * @param product The product that this was about or null if none + * @return The added transaction + */ public Transaction add(@NotNull Transaction.Type type, int change, @Nullable Product product) { return add(new Transaction(type, change, product != null ? product.getTranslationKey() : "")); } + /** + * Adds a new transaction to the history. + * @param transaction The transaction + * @return the added transaction + */ public Transaction add(@NotNull Transaction transaction) { history.add(transaction); @@ -30,19 +57,35 @@ public class Balance { return transaction; } + /** + * Gets the complete transaction history. + * @return The history + */ @NotNull public Queue getHistory() { return history; } + /** + * Gets the current budget. + * @return The current budget + */ public int getBudget() { return budget; } + /** + * Gets the total amount of money gained. + * @return the total income + */ public int getTotalIncome() { return totalIncome; } + /** + * Gets the total amount of money lost + * @return the total loss + */ public int getTotalLoss() { return totalLoss; } diff --git a/src/main/java/de/siphalor/was/game/Options.java b/src/main/java/de/siphalor/was/game/Options.java index caf843a..8f3898d 100644 --- a/src/main/java/de/siphalor/was/game/Options.java +++ b/src/main/java/de/siphalor/was/game/Options.java @@ -6,30 +6,62 @@ import de.siphalor.was.content.lang.I18n; import java.util.Locale; import java.util.prefs.Preferences; +/** + * Holds the game's options and is responsible for saving and loading them. + */ public class Options { + /** + * Whether quest should get automatically added when fulfilled + */ private boolean autoRefillQuests; + /** + * Allow resolving of two quests that target the same product. One asks to deposit it, the other to deliver it + */ private boolean allowQuestResolving; + /** + * Gets whether quest resolving is enabled. + * @return Whether quest resolving is enabled + */ public boolean getAllowQuestResolving() { return allowQuestResolving; } + /** + * Sets whether quest resolving should be enabled. + * @param allowQuestResolving Whether it should be enabled + */ public void setAllowQuestResolving(boolean allowQuestResolving) { this.allowQuestResolving = allowQuestResolving; } + /** + * Gets whether fulfilled quests should automatically be refilled. + * @return Whether fulfilled quests should automatically be refilled + */ public boolean getAutoRefillQuests() { return autoRefillQuests; } + /** + * Gets whether fulfilled quests should automatically be refilled. + * @param autoRefillQuests Whether fulfilled quests should automatically be refilled + */ public void setAutoRefillQuests(boolean autoRefillQuests) { this.autoRefillQuests = autoRefillQuests; } + /** + * Gets a reference to the preferences object used for session independent storage. + * @return The preferences object + */ private Preferences getPreferences() { return Preferences.userNodeForPackage(WhatAStorage.class); } + /** + * Loads the options from the storage. + */ public void load() { Preferences preferences = getPreferences(); autoRefillQuests = preferences.getBoolean("auto-refill-quests", false); @@ -39,6 +71,9 @@ public class Options { I18n.getInstance().setLang(lang, WhatAStorage.getInstance().getContentManager()); } + /** + * Saves the options data to the storage + */ public void save() { Preferences preferences = getPreferences(); preferences.putBoolean("auto-refill-quests", autoRefillQuests); diff --git a/src/main/java/de/siphalor/was/game/Storage.java b/src/main/java/de/siphalor/was/game/Storage.java index 89603cc..e0bb70f 100644 --- a/src/main/java/de/siphalor/was/game/Storage.java +++ b/src/main/java/de/siphalor/was/game/Storage.java @@ -2,9 +2,22 @@ package de.siphalor.was.game; import org.jetbrains.annotations.NotNull; +/** + * The class holding the stored products. + */ public class Storage { + /** + * The storage slots + */ StorageSlot[][] slots; + /** + * Constructs a new storage with the given lengths. + * @param width The width + * @param height The height + * @param depth The depth of the {@link StorageSlot}s + * @throws IllegalArgumentException if any of the lengths is smaller or equal to zero + */ public Storage(int width, int height, int depth) { if (width <= 0 || height <= 0 || depth <= 0) { throw new IllegalArgumentException("Storage lengths must be bigger than zero"); @@ -18,19 +31,36 @@ public class Storage { } } + /** + * Gets the storage slot at the specified location. Coordinate must be valid! + * @param x The x position (Must be in bounds) + * @param y The y position (Must be in bounds + * @return The storage slot at the given location + */ @NotNull public StorageSlot get(int x, int y) { return slots[x][y]; } + /** + * Gets the width of the storage + * @return The width + */ public int getWidth() { return slots.length; } + /** + * Gets the height of the storage + * @return The height + */ public int getHeight() { return slots[0].length; } + /** + * Clears this storage from all products + */ public void clear() { for (StorageSlot[] row : slots) { for (StorageSlot slot : row) { diff --git a/src/main/java/de/siphalor/was/game/StorageSlot.java b/src/main/java/de/siphalor/was/game/StorageSlot.java index 9b7ef2c..af98679 100644 --- a/src/main/java/de/siphalor/was/game/StorageSlot.java +++ b/src/main/java/de/siphalor/was/game/StorageSlot.java @@ -10,22 +10,43 @@ import java.util.Collection; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; +/** + * Represent a single storage slot. + */ public class StorageSlot { + /** + * The depth of the slot + */ final int depth; + /** + * The products in this slot + */ final Product[] products; + /** + * Constructs a new storage slot. + * @param depth The depth of this slot + */ public StorageSlot(int depth) { this.depth = depth; this.products = new Product[depth]; } + /** + * Gets an array of the products. + * @return The product array. Some entries might be null to indicate empty storage + */ @NotNull public Product[] getProducts() { return products; } + /** + * Gets all stored products indexed by their visual position (depth-dependent). + * @return A collection of indexed products + */ @NotNull - public Collection> getProductsIndexed() { + public Collection> getProductsIndexed() { Queue> queue = new ConcurrentLinkedQueue<>(); int index = 0; for (int i = 0; i < depth; i++) { @@ -39,10 +60,17 @@ public class StorageSlot { return queue; } + /** + * Removes all products from this slot. + */ public void clear() { Arrays.fill(products, null); } + /** + * Gets the visually first product. + * @return The product + */ @Nullable public Product front() { for (int i = products.length - 1; i >= 0; i--) { @@ -54,6 +82,10 @@ public class StorageSlot { return null; } + /** + * Removes the visually first product. + * @return The visual position where this product was removed from or -1 if there was no product to remove + */ public int pop() { if (products[0] == null) return -1; @@ -69,6 +101,11 @@ public class StorageSlot { return d; } + /** + * Returns whether the given product fits into this slot depth-wise. + * @param product The product to check + * @return Returns whether it fits + */ public boolean fits(@NotNull Product product) { int blocked = 0; for (Product p : products) { @@ -79,6 +116,11 @@ public class StorageSlot { return product.getDepth() <= depth - blocked; } + /** + * Adds a new product to the visual front of this slot. + * @param product The product to add + * @return The visual position where the product went or -1 if insertion failed + */ public int add(@NotNull Product product) { int blocked = 0; int i; diff --git a/src/main/java/de/siphalor/was/game/Transaction.java b/src/main/java/de/siphalor/was/game/Transaction.java index 6c697d6..6e20dca 100644 --- a/src/main/java/de/siphalor/was/game/Transaction.java +++ b/src/main/java/de/siphalor/was/game/Transaction.java @@ -4,34 +4,81 @@ import org.jetbrains.annotations.NotNull; import java.util.Locale; +/** + * Represents a transaction. + */ public class Transaction { + /** + * The {@link Type} of this transaction + */ private final Type type; + /** + * The money change of this transaction + */ private final int change; + /** + * An optional translation key for a product. Might be empty + */ private final String productKey; + /** + * Constructs a new transaction. + * @param type The type + * @param change The money change of this transaction + * @param productKey A translation key for a product + */ public Transaction(@NotNull Type type, int change, @NotNull String productKey) { this.type = type; this.change = change; this.productKey = productKey; } + /** + * Gets the type. + * @return The type + */ @NotNull public Type getType() { return type; } + /** + * Gets the money change + * @return The change + */ public int getChange() { return change; } + /** + * Gets the product translation key. + * @return The translation key or "" if none is present + */ @NotNull public String getProductKey() { return productKey; } + /** + * Represents the type of a transaction: + *
    + *
  • ABANDON: Abandoned a quest
  • + *
  • DESTROY: destroyed a product
  • + *
  • MOVE: Moved a product
  • + *
  • NOOP: For internal use. To be used with a change of 0
  • + *
  • DELIVER: Delivered a product
  • + *
  • STORE: Stored a product
  • + *
  • RESOLVE: Resolved two quests against each other
  • + *
+ */ public enum Type { ABANDON, DESTROY, MOVE, NOOP, DELIVER, STORE, RESOLVE; + /** + * Gets the translation key for this type + * @return The translation key + */ + @NotNull public String getTranslationKey() { return "game.balance.history.type." + name().toLowerCase(Locale.ENGLISH); } diff --git a/src/main/java/de/siphalor/was/util/Pair.java b/src/main/java/de/siphalor/was/util/Pair.java index 79caa38..83508e4 100644 --- a/src/main/java/de/siphalor/was/util/Pair.java +++ b/src/main/java/de/siphalor/was/util/Pair.java @@ -1,28 +1,69 @@ package de.siphalor.was.util; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + import java.util.Objects; +/** + * Represents the pair of two values. + * @param The type of the first value + * @param The type of the second value + */ public class Pair { + /** + * The first value + */ private final A first; + /** + * The second value + */ private final B second; + /** + * Constructs a new pair. + * @param first The first value + * @param second The second value + * @param The type of the first value + * @param The type of the second value + * @return The new pair + */ + @NotNull public static Pair of(A first, B second) { return new Pair<>(first, second); } + /** + * Constructs a new pair. + * @param first The first value + * @param second The second value + */ public Pair(A first, B second) { this.first = first; this.second = second; } + /** + * Gets the first value. + * @return The first value + */ public A getFirst() { return first; } + /** + * Gets the second value. + * @return The second value + */ public B getSecond() { return second; } + /** + * Checks for equality. + * @param o The other object + * @return Returns whether both are equal + */ @Override public boolean equals(Object o) { if (this == o) return true; @@ -32,6 +73,10 @@ public class Pair { Objects.equals(second, pair.second); } + /** + * The combined hash code of both values + * @return The hash code + */ @Override public int hashCode() { return Objects.hash(first, second); diff --git a/src/main/java/de/siphalor/was/util/PersistentInputStream.java b/src/main/java/de/siphalor/was/util/PersistentInputStream.java index a7ed25d..fdcfc4a 100644 --- a/src/main/java/de/siphalor/was/util/PersistentInputStream.java +++ b/src/main/java/de/siphalor/was/util/PersistentInputStream.java @@ -7,19 +7,31 @@ import java.io.IOException; import java.io.InputStream; /** + * An input stream that can be reused.
+ * Close with {@link PersistentInputStream#realClose()}
* Taken from here: https://stackoverflow.com/questions/924990/how-to-cache-inputstream-for-multiple-use#1303314 */ public class PersistentInputStream extends BufferedInputStream { + /** + * Constructs a new persistent input stream. + * @param in The base input stream + */ public PersistentInputStream(@NotNull InputStream in) { super(in); super.mark(Integer.MAX_VALUE); } + /** + * Resets this input stream. + */ @Override public void close() throws IOException { super.reset(); } + /** + * Closes this input stream. + */ public void realClose() throws IOException { super.close(); } diff --git a/src/main/java/de/siphalor/was/util/ResourceManager.java b/src/main/java/de/siphalor/was/util/ResourceManager.java index aecbffb..1571891 100644 --- a/src/main/java/de/siphalor/was/util/ResourceManager.java +++ b/src/main/java/de/siphalor/was/util/ResourceManager.java @@ -1,7 +1,19 @@ package de.siphalor.was.util; +/** + * An interface for classes that manage some kind of resources. + * @param The type of the resources + */ public interface ResourceManager { + /** + * Clear this manager's resources. + */ void clear(); + /** + * Gets a resource by the given id. + * @param id The resource's id + * @return The associated resource + */ T get(String id); } diff --git a/src/main/java/de/siphalor/was/util/Util.java b/src/main/java/de/siphalor/was/util/Util.java index 4ace161..12c6cfb 100644 --- a/src/main/java/de/siphalor/was/util/Util.java +++ b/src/main/java/de/siphalor/was/util/Util.java @@ -1,6 +1,5 @@ package de.siphalor.was.util; -import java.awt.image.ImageObserver; import java.io.IOException; import java.util.function.IntPredicate; import java.util.function.Predicate; @@ -9,28 +8,63 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; +/** + * A utility collection. + */ public class Util { + /** + * The main logger + */ public static final Logger LOGGER; + /** + * A predicate that always returns true + */ private static final Predicate DUMMY_PREDICATE = o -> true; + /** + * An int predicate that always returns true + */ private static final IntPredicate DUMMY_INT_PREDICATE = value -> true; + /** + * An empty array + */ private static final Object[] EMPTY_ARRAY = new Object[0]; + /** + * Gets an always true predicate. + * @param The type of the predicate + * @return A predicate that always returns true + */ @SuppressWarnings("unchecked") public static Predicate dummyPredicate() { return (Predicate) DUMMY_PREDICATE; } + /** + * Gets an always true int predicate. + * @return An int predicate that always returns true + */ public static IntPredicate dummyIntPredicate() { return DUMMY_INT_PREDICATE; } + /** + * Gets an empty array of the given type. No memory allocations for this stuff. Also convenient + * @param The array type + * @return The empty array + */ @SuppressWarnings("unchecked") public static T[] emptyArray() { return (T[]) EMPTY_ARRAY; } + /** + * Converts a relative path to a resource id. + * @param packId The pack where the resource lives + * @param path The relative path to the resource + * @return The resource id + */ public static String pathToId(String packId, String path) { int dot = path.lastIndexOf('.'); if (dot >= 0) { diff --git a/src/main/java/de/siphalor/was/visual/Visual.java b/src/main/java/de/siphalor/was/visual/Visual.java index 68996c9..baf9892 100644 --- a/src/main/java/de/siphalor/was/visual/Visual.java +++ b/src/main/java/de/siphalor/was/visual/Visual.java @@ -5,17 +5,59 @@ import de.siphalor.was.content.product.Product; import de.siphalor.was.content.quest.Quest; import de.siphalor.was.game.Transaction; +/** + * An interface for handling the display. + */ public interface Visual { + /** + * Called in the setup stage of the application. + * @param whatAStorage The main instance of the game + */ void setup(WhatAStorage whatAStorage); + + /** + * Starts the display and hands over control to this method. + */ void run(); + + /** + * Called when a game starts. + * @param width The storage width + * @param height The storage height + * @param depth The storage depth + */ void onGameStart(int width, int height, int depth); + + /** + * Called when the a program stop got requested. + */ void onScheduleStop(); + /** + * Called when the localization changed. Should reload all of the localized UIs. + */ void invalidateI18n(); + /** + * Called when a transactions gets added. + * @param budget The new budget + * @param transaction The new transaction + * @param totalIncome The total income + * @param totalLoss The total loss + */ void onBalanceChanged(int budget, Transaction transaction, int totalIncome, int totalLoss); + /** + * Called when a quest gets added. + * @param newQuest The new quest + * @param canCreateMore Whether the user can request more quests + */ void onQuestAdded(Quest newQuest, boolean canCreateMore); + + /** + * Called when a quest gets fulfilled or abandoned. + * @param index The index of the quest to remove + */ void onQuestRemoved(int index); void onProductSet(int x, int y, int z, Product product); diff --git a/src/test/java/de/siphalor/was/dummy/DummyContentPack.java b/src/test/java/de/siphalor/was/dummy/DummyContentPack.java index 343f025..6325883 100644 --- a/src/test/java/de/siphalor/was/dummy/DummyContentPack.java +++ b/src/test/java/de/siphalor/was/dummy/DummyContentPack.java @@ -23,9 +23,9 @@ public class DummyContentPack implements ContentPack { } @Override - public @Nullable Resource getResource(@NotNull String location) { - if (content.containsKey(location)) - return new DummyResource(location, content.get(location)); + public @Nullable Resource getResource(@NotNull String identifier) { + if (content.containsKey(identifier)) + return new DummyResource(identifier, content.get(identifier)); return null; } diff --git a/src/test/java/de/siphalor/was/dummy/DummyProduct.java b/src/test/java/de/siphalor/was/dummy/DummyProduct.java index 6dc6cbf..2f85104 100644 --- a/src/test/java/de/siphalor/was/dummy/DummyProduct.java +++ b/src/test/java/de/siphalor/was/dummy/DummyProduct.java @@ -3,7 +3,6 @@ package de.siphalor.was.dummy; import de.siphalor.was.content.product.Product; import de.siphalor.was.content.product.ProductType; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; import java.util.Arrays; import java.util.Random; @@ -87,7 +86,7 @@ public class DummyProduct implements Product { } @Override - public @NotNull Product randomProduct(Random random) { + public @NotNull DummyProduct randomProduct(Random random) { return null; } }