From 63f226b3fa2c34dba73a53f0ee96b13087cb2206 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Mon, 26 Dec 2022 19:22:38 +0800 Subject: [PATCH 1/5] Added glass bottle --- .../java/minicraft/entity/mob/Player.java | 17 ++++++++++ src/main/java/minicraft/item/Inventory.java | 1 + src/main/java/minicraft/item/PotionItem.java | 8 ++++- src/main/java/minicraft/item/PotionType.java | 31 +++++++++--------- src/main/java/minicraft/item/Recipes.java | 21 ++++++------ .../java/minicraft/item/StackableItem.java | 1 + .../assets/textures/item/glass_bottle.png | Bin 0 -> 177 bytes 7 files changed, 52 insertions(+), 27 deletions(-) create mode 100644 src/main/resources/assets/textures/item/glass_bottle.png diff --git a/src/main/java/minicraft/entity/mob/Player.java b/src/main/java/minicraft/entity/mob/Player.java index eb19e5ed5..8473f1010 100644 --- a/src/main/java/minicraft/entity/mob/Player.java +++ b/src/main/java/minicraft/entity/mob/Player.java @@ -1090,4 +1090,21 @@ public Inventory getInventory() { } public String getDebugHunger() { return hungerStamCnt + "_" + stamHungerTicks; } + + /** + * Trying to add item(s) to the player inventory. + * If no more item(s) can be added to the inventory, drop the item(s) near the player. + */ + public void tryAddToInvOrDrop(@Nullable Item item) { + if (item != null) { + int returned = inventory.add(0, item); + if (item instanceof StackableItem) { + if (((StackableItem)item).count > 0) { + getLevel().dropItem(x, y, item); + } + } else if (returned == 0) { + getLevel().dropItem(x, y, item); + } + } + } } diff --git a/src/main/java/minicraft/item/Inventory.java b/src/main/java/minicraft/item/Inventory.java index 808beb7ca..54622a155 100644 --- a/src/main/java/minicraft/item/Inventory.java +++ b/src/main/java/minicraft/item/Inventory.java @@ -4,6 +4,7 @@ import java.util.List; import java.util.Random; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import minicraft.entity.furniture.Furniture; diff --git a/src/main/java/minicraft/item/PotionItem.java b/src/main/java/minicraft/item/PotionItem.java index fec25f8a7..edb88c0b7 100644 --- a/src/main/java/minicraft/item/PotionItem.java +++ b/src/main/java/minicraft/item/PotionItem.java @@ -34,7 +34,13 @@ public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, if (type.equals(PotionType.Lava)) { AchievementsDisplay.setAchievement("minicraft.achievement.lava",true); } - return super.interactOn(applyPotion(player, type, true)); + return interactOn(applyPotion(player, type, true), player); + } + + protected boolean interactOn(boolean subClassSuccess, Player player) { + if (subClassSuccess) + player.tryAddToInvOrDrop(Items.get("glass bottle")); + return super.interactOn(subClassSuccess); } /// Only ever called to load from file diff --git a/src/main/java/minicraft/item/PotionType.java b/src/main/java/minicraft/item/PotionType.java index 470f11683..0ce361ebd 100644 --- a/src/main/java/minicraft/item/PotionType.java +++ b/src/main/java/minicraft/item/PotionType.java @@ -7,45 +7,45 @@ import minicraft.level.Level; public enum PotionType { - None (Color.get(1, 41, 51, 255), 0), - + Awkward (Color.get(1, 41, 51, 255), 0), + Speed (Color.get(1, 105, 209, 105), 4200) { public boolean toggleEffect(Player player, boolean addEffect) { player.moveSpeed += (double)( addEffect ? 1 : (player.moveSpeed > 1 ? -1 : 0) ); return true; } }, - + Light (Color.get(1, 183, 183, 91), 6000), Swim (Color.get(1, 51, 51, 255), 4800), Energy (Color.get(1, 237, 110, 78), 8400), Regen (Color.get(1, 219, 70, 189), 1800), - + Health (Color.get(1, 194, 56, 84), 0) { public boolean toggleEffect(Player player, boolean addEffect) { if(addEffect) player.heal(5); return true; } }, - + Time (Color.get(1, 163), 1800), Lava (Color.get(1, 199, 58, 58), 7200), Shield (Color.get(1, 84, 84, 204), 5400), Haste (Color.get(1, 201, 71, 201), 4800), - + Escape (Color.get(1, 222, 162, 162), 0) { public boolean toggleEffect(Player player, boolean addEffect) { if (addEffect) { int playerDepth = player.getLevel().depth; - + if (playerDepth == 0) { // player is in overworld Game.notifications.add("You can't escape from here!"); return false; } - + int depthDiff = playerDepth > 0 ? -1 : 1; - + World.scheduleLevelChange(depthDiff, () -> { Level plevel = World.levels[World.lvlIdx(playerDepth + depthDiff)]; if (plevel != null && !plevel.getTile(player.x >> 4, player.y >> 4).mayPass(plevel, player.x >> 4, player.y >> 4, player)) @@ -55,25 +55,24 @@ public boolean toggleEffect(Player player, boolean addEffect) { return true; } }; - + public int dispColor, duration; public String name; - + PotionType(int col, int dur) { dispColor = col; duration = dur; - if(this.toString().equals("None")) name = "Potion"; - else name = this + " Potion"; + name = this + " Potion"; } - + public boolean toggleEffect(Player player, boolean addEffect) { return duration > 0; // If you have no duration and do nothing, then you can't be used. } - + public boolean transmitEffect() { return true; // Any effect which could be duplicated and result poorly should not be sent to the server. // For the case of the Health potion, the player health is not transmitted separately until after the potion effect finishes, so having it send just gets the change there earlier. } - + public static final PotionType[] values = PotionType.values(); } diff --git a/src/main/java/minicraft/item/Recipes.java b/src/main/java/minicraft/item/Recipes.java index 216f1a9bc..2c555f45a 100644 --- a/src/main/java/minicraft/item/Recipes.java +++ b/src/main/java/minicraft/item/Recipes.java @@ -109,6 +109,7 @@ public class Recipes { furnaceRecipes.add(new Recipe("iron_1", "iron Ore_4", "coal_1")); furnaceRecipes.add(new Recipe("gold_1", "gold Ore_4", "coal_1")); furnaceRecipes.add(new Recipe("glass_1", "sand_4", "coal_1")); + furnaceRecipes.add(new Recipe("glass bottle_1", "glass_3")); ovenRecipes.add(new Recipe("cooked pork_1", "raw pork_1", "coal_1")); ovenRecipes.add(new Recipe("steak_1", "raw beef_1", "coal_1")); @@ -117,16 +118,16 @@ public class Recipes { ovenRecipes.add(new Recipe("Baked Potato_1", "Potato_1")); enchantRecipes.add(new Recipe("Gold Apple_1", "apple_1", "gold_8")); - enchantRecipes.add(new Recipe("potion_1", "glass_1", "Lapis_3")); - enchantRecipes.add(new Recipe("speed potion_1", "potion_1", "Cactus_5")); - enchantRecipes.add(new Recipe("light potion_1", "potion_1", "slime_5")); - enchantRecipes.add(new Recipe("swim potion_1", "potion_1", "raw fish_5")); - enchantRecipes.add(new Recipe("haste potion_1", "potion_1", "Wood_5", "Stone_5")); - enchantRecipes.add(new Recipe("lava potion_1", "potion_1", "Lava Bucket_1")); - enchantRecipes.add(new Recipe("energy potion_1", "potion_1", "gem_25")); - enchantRecipes.add(new Recipe("regen potion_1", "potion_1", "Gold Apple_1")); - enchantRecipes.add(new Recipe("Health Potion_1", "potion_1", "GunPowder_2", "Leather Armor_1")); - enchantRecipes.add(new Recipe("Escape Potion_1", "potion_1", "GunPowder_3", "Lapis_7")); + enchantRecipes.add(new Recipe("awkward potion_1", "glass bottle_1", "Lapis_3")); + enchantRecipes.add(new Recipe("speed potion_1", "awkward potion_1", "Cactus_5")); + enchantRecipes.add(new Recipe("light potion_1", "awkward potion_1", "slime_5")); + enchantRecipes.add(new Recipe("swim potion_1", "awkward potion_1", "raw fish_5")); + enchantRecipes.add(new Recipe("haste potion_1", "awkward potion_1", "Wood_5", "Stone_5")); + enchantRecipes.add(new Recipe("lava potion_1", "awkward potion_1", "Lava Bucket_1")); + enchantRecipes.add(new Recipe("energy potion_1", "awkward potion_1", "gem_25")); + enchantRecipes.add(new Recipe("regen potion_1", "awkward potion_1", "Gold Apple_1")); + enchantRecipes.add(new Recipe("Health Potion_1", "awkward potion_1", "GunPowder_2", "Leather Armor_1")); + enchantRecipes.add(new Recipe("Escape Potion_1", "awkward potion_1", "GunPowder_3", "Lapis_7")); enchantRecipes.add(new Recipe("Totem of Air_1", "gold_10", "gem_10", "Lapis_5","Cloud Ore_5")); } } diff --git a/src/main/java/minicraft/item/StackableItem.java b/src/main/java/minicraft/item/StackableItem.java index 69ad20a6a..1ef64ecc2 100644 --- a/src/main/java/minicraft/item/StackableItem.java +++ b/src/main/java/minicraft/item/StackableItem.java @@ -35,6 +35,7 @@ protected static ArrayList getAllInstances() { items.add(new StackableItem("Scale", new LinkedSprite(SpriteType.Item, "scale"))); items.add(new StackableItem("Shard", new LinkedSprite(SpriteType.Item, "shard"))); items.add(new StackableItem("Cloud Ore", new LinkedSprite(SpriteType.Item, "cloud_ore"))); + items.add(new StackableItem("Glass Bottle", new LinkedSprite(SpriteType.Item, "glass_bottle"))); return items; } diff --git a/src/main/resources/assets/textures/item/glass_bottle.png b/src/main/resources/assets/textures/item/glass_bottle.png new file mode 100644 index 0000000000000000000000000000000000000000..caa699594a54b99b4e4ddbcbe5bc4fe404a7f438 GIT binary patch literal 177 zcmeAS@N?(olHy`uVBq!ia0vp^93afW1|*O0@9PFqjKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucK{rnq#}JL+WCczUi_HJ*i5lXfJ~NG2^%~El zHC;;h@bR(xpIsA04;*=*)}Zq9=TFInF%}Kl2h=#33LI0~8y)=o_yYPG85va0$ko{d S*;WHhWbkzLb6Mw<&;$UG?lfiq literal 0 HcmV?d00001 From 0e2a345d7593b3fbba90f530b011b7038249cc42 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Tue, 27 Dec 2022 17:51:41 +0800 Subject: [PATCH 2/5] Made clothing returnable --- src/main/java/minicraft/item/ClothingItem.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/java/minicraft/item/ClothingItem.java b/src/main/java/minicraft/item/ClothingItem.java index dbad75234..8441b2f08 100644 --- a/src/main/java/minicraft/item/ClothingItem.java +++ b/src/main/java/minicraft/item/ClothingItem.java @@ -41,6 +41,13 @@ public boolean interactOn(Tile tile, Level level, int xt, int yt, Player player, if (player.shirtColor == playerCol) { return false; } else { + ClothingItem lastClothing = (ClothingItem) getAllInstances().stream().filter(i -> i instanceof ClothingItem && ((ClothingItem) i).playerCol == player.shirtColor) + .findAny().orElse(null); + if (lastClothing == null) + lastClothing = (ClothingItem) Items.get("Reg Clothes"); + lastClothing = lastClothing.clone(); + lastClothing.count = 1; + player.tryAddToInvOrDrop(lastClothing); player.shirtColor = playerCol; return super.interactOn(true); } From 3d137833e1882c7fc569388766a77518039fd78e Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Fri, 13 Jan 2023 16:20:36 +0800 Subject: [PATCH 3/5] Fixed the unmatched initial color --- src/main/java/minicraft/core/Initializer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/minicraft/core/Initializer.java b/src/main/java/minicraft/core/Initializer.java index bd60accd9..fcf24ad3c 100644 --- a/src/main/java/minicraft/core/Initializer.java +++ b/src/main/java/minicraft/core/Initializer.java @@ -107,6 +107,7 @@ static void run() { static void createAndDisplayFrame() { Renderer.canvas.setMinimumSize(new java.awt.Dimension(1, 1)); Renderer.canvas.setPreferredSize(Renderer.getWindowSize()); + Renderer.canvas.setBackground(Color.WHITE); logoSplash.setMinimumSize(new java.awt.Dimension(1, 1)); logoSplash.setPreferredSize(Renderer.getWindowSize()); JFrame frame = Initializer.frame = new JFrame(NAME); From 8073261feeaf9c2df9f084744bd8005839beccc6 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Fri, 13 Jan 2023 19:59:05 +0800 Subject: [PATCH 4/5] Made the loading screen background black Also, the background of the logo is animated colorful. --- src/main/java/minicraft/core/Initializer.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/main/java/minicraft/core/Initializer.java b/src/main/java/minicraft/core/Initializer.java index fcf24ad3c..d69098b58 100644 --- a/src/main/java/minicraft/core/Initializer.java +++ b/src/main/java/minicraft/core/Initializer.java @@ -107,7 +107,7 @@ static void run() { static void createAndDisplayFrame() { Renderer.canvas.setMinimumSize(new java.awt.Dimension(1, 1)); Renderer.canvas.setPreferredSize(Renderer.getWindowSize()); - Renderer.canvas.setBackground(Color.WHITE); + Renderer.canvas.setBackground(Color.BLACK); logoSplash.setMinimumSize(new java.awt.Dimension(1, 1)); logoSplash.setPreferredSize(Renderer.getWindowSize()); JFrame frame = Initializer.frame = new JFrame(NAME); @@ -177,11 +177,31 @@ private static class LogoSplashCanvas extends JPanel { @Override public void paintComponent(Graphics g) { super.paintComponent(g); - if (transparency < 255) g.drawImage(logo, getWidth()/2 - logo.getWidth(frame)*2, getHeight()/2 - logo.getHeight(frame)*2, logo.getWidth(frame)*4, logo.getHeight(frame)*4, frame); - if (transparency > 0) { - g.setColor(new Color(255, 255, 255, transparency)); - g.fillRect(0, 0, g.getClipBounds().width, g.getClipBounds().height); + final int w = g.getClipBounds().width; + final int h = g.getClipBounds().height; + + // Drawing colorful background. + Graphics2D g2d = (Graphics2D) g; + int n = 6; + float[] fractions = new float[n]; + Color[] colors = new Color[n]; + for (int x = 0; x < n; x++) { + double sin = Math.sin(Math.PI * (255-transparency)/255.0); + double cos = Math.cos(Math.PI * x/n); + double hue = Math.abs((sin*210 + cos*45) % 360) / 360; + double s = 1 - Math.pow(Math.min(Math.sin(Math.cos(Math.abs(sin - cos))), 1), 4); + fractions[x] = (float) Math.sin((double) x/n * Math.PI/2) * x/n; + colors[x] = Color.getHSBColor((float) hue, (float) s, 1); } + g2d.setPaint(new LinearGradientPaint(0, 0, w, 0, fractions, colors)); + g2d.fillRect(0, 0, w, h); + + // Drawing the centered logo. + if (transparency < 255) g.drawImage(logo, w/2 - logo.getWidth(frame)*2, h/2 - logo.getHeight(frame)*2, logo.getWidth(frame)*4, logo.getHeight(frame)*4, frame); + + // Fading effect. + g.setColor(new Color(0, 0, 0, Math.max(Math.min(255 - (int) (Math.cos(transparency/255.0 * Math.PI) * 255), 255), 0))); + g.fillRect(0, 0, w, h); if (inAnimation) { if (display) { From b5e5110543e3ca3c85fd544e471f60a4c1a569d5 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Sat, 14 Jan 2023 00:02:41 +0800 Subject: [PATCH 5/5] Improved the FPS setting --- src/main/java/minicraft/core/Game.java | 2 +- src/main/java/minicraft/core/Initializer.java | 35 +++-- src/main/java/minicraft/core/Renderer.java | 53 ++++--- src/main/java/minicraft/core/Updater.java | 2 +- src/main/java/minicraft/core/io/Settings.java | 132 ++++++++++++++++-- src/main/java/minicraft/saveload/Load.java | 2 +- .../screen/OptionsMainMenuDisplay.java | 2 +- .../minicraft/screen/OptionsWorldDisplay.java | 2 +- .../minicraft/screen/entry/ArrayEntry.java | 4 +- 9 files changed, 178 insertions(+), 56 deletions(-) diff --git a/src/main/java/minicraft/core/Game.java b/src/main/java/minicraft/core/Game.java index b6373542d..78a11d426 100644 --- a/src/main/java/minicraft/core/Game.java +++ b/src/main/java/minicraft/core/Game.java @@ -82,7 +82,7 @@ public static void main(String[] args) { World.resetGame(); // "half"-starts a new game, to set up initial variables player.eid = 0; new Load(true); // This loads any saved preferences. - MAX_FPS = (int) Settings.get("fps"); // DO NOT put this above. + MAX_FPS = Settings.getFPS(); // DO NOT put this above. // Update fullscreen frame if Updater.FULLSCREEN was updated previously if (Updater.FULLSCREEN) { diff --git a/src/main/java/minicraft/core/Initializer.java b/src/main/java/minicraft/core/Initializer.java index d69098b58..5c60a702a 100644 --- a/src/main/java/minicraft/core/Initializer.java +++ b/src/main/java/minicraft/core/Initializer.java @@ -1,25 +1,28 @@ package minicraft.core; -import java.awt.*; +import minicraft.core.io.FileHandler; +import minicraft.core.io.Settings; +import minicraft.util.Logging; +import minicraft.util.TinylogLoggingProvider; +import org.jetbrains.annotations.Nullable; +import org.tinylog.provider.ProviderRegistry; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Image; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.awt.image.BufferedImage; -import java.awt.image.ImageObserver; import java.io.IOException; import java.util.Objects; -import java.util.function.Supplier; import javax.imageio.ImageIO; -import javax.swing.*; - -import minicraft.core.io.FileHandler; -import minicraft.util.Logging; -import minicraft.util.TinylogLoggingProvider; - -import org.jetbrains.annotations.Nullable; -import org.tinylog.provider.ProviderRegistry; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.WindowConstants; public class Initializer extends Game { private Initializer() {} @@ -31,6 +34,7 @@ private Initializer() {} static LogoSplashCanvas logoSplash = new LogoSplashCanvas(); static int fra, tik; // These store the number of frames and ticks in the previous second; used for fps, at least. + public static JFrame getFrame() { return frame; } public static int getCurFps() { return fra; } static void parseArgs(String[] args) { @@ -85,17 +89,18 @@ static void run() { unprocessed--; } - if ((now - lastRender) / 1.0E9 > 1.0 / MAX_FPS) { + if (MAX_FPS == Settings.FPS_UNLIMITED || now - lastRender >= 1E9D / MAX_FPS) { frames++; lastRender = System.nanoTime(); Renderer.render(); } if (System.currentTimeMillis() - lastTimer1 > 1000) { //updates every 1 second - lastTimer1 += 1000; // Adds a second to the timer + long interval = System.currentTimeMillis() - lastTimer1; + lastTimer1 = System.currentTimeMillis(); // Adds a second to the timer - fra = frames; // Saves total frames in last second - tik = ticks; // Saves total ticks in last second + fra = (int) (frames * 1000 / interval); // Saves total frames in last second + tik = (int) (ticks * 1000 / interval); // Saves total ticks in last second frames = 0; // Resets frames ticks = 0; // Resets ticks; ie, frames and ticks only are per second } diff --git a/src/main/java/minicraft/core/Renderer.java b/src/main/java/minicraft/core/Renderer.java index a0b4a9355..702548aed 100644 --- a/src/main/java/minicraft/core/Renderer.java +++ b/src/main/java/minicraft/core/Renderer.java @@ -1,38 +1,22 @@ package minicraft.core; -import java.awt.Canvas; -import java.awt.Graphics; -import java.awt.geom.AffineTransform; -import java.awt.image.AffineTransformOp; -import java.awt.image.BufferStrategy; -import java.awt.image.BufferedImage; -import java.awt.image.DataBufferInt; -import java.io.File; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Objects; - import minicraft.core.CrashHandler.ErrorInfo; import minicraft.core.io.Localization; import minicraft.core.io.Settings; import minicraft.entity.furniture.Bed; import minicraft.entity.mob.Player; import minicraft.gfx.Color; +import minicraft.gfx.Ellipsis; import minicraft.gfx.Ellipsis.DotUpdater.TickUpdater; -import minicraft.gfx.SpriteLinker.LinkedSprite; -import minicraft.gfx.SpriteLinker.SpriteType; import minicraft.gfx.Ellipsis.SmoothEllipsis; -import minicraft.gfx.Ellipsis; import minicraft.gfx.Font; import minicraft.gfx.FontStyle; import minicraft.gfx.MinicraftImage; import minicraft.gfx.Point; import minicraft.gfx.Screen; import minicraft.gfx.SpriteLinker; +import minicraft.gfx.SpriteLinker.LinkedSprite; +import minicraft.gfx.SpriteLinker.SpriteType; import minicraft.item.Items; import minicraft.item.PotionType; import minicraft.item.ToolItem; @@ -47,10 +31,27 @@ import minicraft.screen.entry.StringEntry; import minicraft.util.Quest; import minicraft.util.Quest.QuestSeries; +import org.json.JSONObject; -import javax.imageio.ImageIO; +import java.awt.AWTException; +import java.awt.Canvas; +import java.awt.Graphics; +import java.awt.GraphicsEnvironment; +import java.awt.geom.AffineTransform; +import java.awt.image.AffineTransformOp; +import java.awt.image.BufferStrategy; +import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; +import java.io.File; +import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; -import org.json.JSONObject; +import javax.imageio.ImageIO; public class Renderer extends Game { private Renderer() {} @@ -105,7 +106,15 @@ public static void initScreen() { hudSheet = new LinkedSprite(SpriteType.Gui, "hud"); Initializer.startCanvasRendering(); - canvas.createBufferStrategy(3); + try { // Reference: https://stackoverflow.com/a/61843644. + canvas.createBufferStrategy(3, GraphicsEnvironment.getLocalGraphicsEnvironment() + .getDefaultScreenDevice() + .getDefaultConfiguration() + .getBufferCapabilities()); + } catch (AWTException e) { + CrashHandler.crashHandle(e, new ErrorInfo("Canvas Initialization Failure", ErrorInfo.ErrorType.UNEXPECTED, true, "The canvas is unable to be initialized.")); + } + canvas.requestFocus(); } diff --git a/src/main/java/minicraft/core/Updater.java b/src/main/java/minicraft/core/Updater.java index 1adeefc2e..35febd562 100644 --- a/src/main/java/minicraft/core/Updater.java +++ b/src/main/java/minicraft/core/Updater.java @@ -69,7 +69,7 @@ static void updateFullscreen() { // Dispose is needed to set undecorated value Initializer.frame.dispose(); - GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices()[0]; + GraphicsDevice device = Initializer.frame.getGraphicsConfiguration().getDevice(); if (Updater.FULLSCREEN) { Initializer.frame.setUndecorated(true); device.setFullScreenWindow(Initializer.frame); diff --git a/src/main/java/minicraft/core/io/Settings.java b/src/main/java/minicraft/core/io/Settings.java index 369145026..2d7fbb4a2 100644 --- a/src/main/java/minicraft/core/io/Settings.java +++ b/src/main/java/minicraft/core/io/Settings.java @@ -1,10 +1,12 @@ package minicraft.core.io; +import minicraft.core.Initializer; import minicraft.screen.entry.ArrayEntry; import minicraft.screen.entry.BooleanEntry; import minicraft.screen.entry.RangeEntry; import java.awt.*; +import java.util.ArrayList; import java.util.HashMap; public class Settings { @@ -12,7 +14,7 @@ public class Settings { private static final HashMap> options = new HashMap<>(); static { - options.put("fps", new RangeEntry("minicraft.settings.fps", 10, 300, getRefreshRate())); // Has to check if the game is running in a headless mode. If it doesn't set the fps to 60 + options.put("fps", new FPSEntry("minicraft.settings.fps")); // Has to check if the game is running in a headless mode. If it doesn't set the fps to 60 options.put("screenshot", new ArrayEntry<>("minicraft.settings.screenshot_scale", 1, 2, 5, 10, 15, 20)); // The magnification of screenshot. I would want to see ultimate sized. options.put("diff", new ArrayEntry<>("minicraft.settings.difficulty", "minicraft.settings.difficulty.easy", "minicraft.settings.difficulty.normal", "minicraft.settings.difficulty.hard")); options.get("diff").setSelection(1); @@ -61,19 +63,125 @@ public static void setIdx(String option, int idx) { options.get(option.toLowerCase()).setSelection(idx); } - private static int getRefreshRate() { - if (GraphicsEnvironment.isHeadless()) return 60; + public static final int FPS_UNLIMITED = -1; - int hz; - try { - hz = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getRefreshRate(); - } catch (HeadlessException e) { - return 60; + /** + * Getting the set value for maximum framerate. + * @return The set or device framerate value, or {@link #FPS_UNLIMITED} if unlimited. + */ + public static int getFPS() { + return ((FPSEntry) getEntry("fps")).getFPSValue(); + } + + private static class FPSEntry extends ArrayEntry { + public static class FPSValue { + private int fps = DisplayMode.REFRESH_RATE_UNKNOWN; + public enum ValueType { Normal, VSync, Unlimited; } + private final ValueType type; + + public FPSValue(ValueType type) { + this.type = type == ValueType.Normal ? ValueType.Unlimited : type; + } + public FPSValue(int fps) { + type = ValueType.Normal; + if (fps > 300) fps = 300; + if (fps < 10) fps = 10; + this.fps = fps; + } + + public int getFPS() { + if (type == ValueType.VSync) { + // There is currently no actual way to do VSync by only Swing/AWT. + // Instead, the device refresh rate is used. + int rate = Initializer.getFrame().getGraphicsConfiguration().getDevice().getDisplayMode().getRefreshRate(); + return rate == DisplayMode.REFRESH_RATE_UNKNOWN ? FPS_UNLIMITED : rate; + } else if (type == ValueType.Unlimited) { + return FPS_UNLIMITED; + } else { + return fps; + } + } + + @Override + public String toString() { + return type == ValueType.Normal ? String.valueOf(fps) : type.toString(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) return false; + if (this == obj) return true; + if (obj instanceof FPSValue) + return type == ((FPSValue) obj).type && + fps == ((FPSValue) obj).fps; + return false; + } + + @Override + public int hashCode() { + return type.ordinal() * fps + fps; + } + } + + private static FPSValue[] getArray() { + ArrayList list = new ArrayList<>(); + for (int i = 10; i <= 300; i += 10) { + list.add(new FPSValue(i)); + } + + list.add(new FPSValue(FPSValue.ValueType.VSync)); + list.add(new FPSValue(FPSValue.ValueType.Unlimited)); + + return list.toArray(new FPSValue[0]); } - if (hz == DisplayMode.REFRESH_RATE_UNKNOWN) return 60; - if (hz > 300) return 60; - if (10 > hz) return 60; - return hz; + public FPSEntry(String label) { + super(label, false, getArray()); + } + + /** + * Getting the set value for maximum framerate. + * @return The set or device framerate value, or {@link #FPS_UNLIMITED} if unlimited. + */ + public int getFPSValue() { + return getValue().getFPS(); + } + + @Override + public void setValue(Object value) { + if (value instanceof FPSValue) { + super.setValue(value); + } else { + if (!(value instanceof FPSValue.ValueType)) try { // First tests if it is an integral value. + int v; + if (value instanceof Integer) v = (int) value; + else v = Integer.parseInt(value.toString()); + if (v < 10) v = 10; + if (v > 300) v = 300; + v -= v % 10; + for (FPSValue val : options) { + if (val.fps == v) { + super.setValue(val); + return; + } + } + } catch (NumberFormatException ignored) {} + + try { // Then falls back to enums. + FPSValue.ValueType type; + if (value instanceof FPSValue.ValueType) type = (FPSValue.ValueType) value; + else type = FPSValue.ValueType.valueOf(value.toString()); + if (type != FPSValue.ValueType.Normal) for (FPSValue val : options) { + if (val.type == type) { + super.setValue(val); + return; + } + } + } catch (IllegalArgumentException ignored) {} + + // Finally VSync is applied. + super.setValue(FPSValue.ValueType.VSync); + } + } } } diff --git a/src/main/java/minicraft/saveload/Load.java b/src/main/java/minicraft/saveload/Load.java index 255170125..c5716d966 100644 --- a/src/main/java/minicraft/saveload/Load.java +++ b/src/main/java/minicraft/saveload/Load.java @@ -358,7 +358,7 @@ private void loadPrefs(String filename) { // Settings Settings.set("sound", json.getBoolean("sound")); Settings.set("autosave", json.getBoolean("autosave")); - Settings.set("fps", json.getInt("fps")); + Settings.set("fps", json.getString("fps")); Settings.set("showquests", json.optBoolean("showquests", true)); if (json.has("lang")) { diff --git a/src/main/java/minicraft/screen/OptionsMainMenuDisplay.java b/src/main/java/minicraft/screen/OptionsMainMenuDisplay.java index 4bffa026d..6ba7987cb 100644 --- a/src/main/java/minicraft/screen/OptionsMainMenuDisplay.java +++ b/src/main/java/minicraft/screen/OptionsMainMenuDisplay.java @@ -33,6 +33,6 @@ public OptionsMainMenuDisplay() { public void onExit() { Localization.changeLanguage(((LocaleInformation)Settings.get("language")).locale.toLanguageTag()); new Save(); - Game.MAX_FPS = (int)Settings.get("fps"); + Game.MAX_FPS = Settings.getFPS(); } } diff --git a/src/main/java/minicraft/screen/OptionsWorldDisplay.java b/src/main/java/minicraft/screen/OptionsWorldDisplay.java index e798d4ab9..b9e0c6074 100644 --- a/src/main/java/minicraft/screen/OptionsWorldDisplay.java +++ b/src/main/java/minicraft/screen/OptionsWorldDisplay.java @@ -87,6 +87,6 @@ private List getEntries() { public void onExit() { Localization.changeLanguage(((LocaleInformation)Settings.get("language")).locale.toLanguageTag()); new Save(); - Game.MAX_FPS = (int)Settings.get("fps"); + Game.MAX_FPS = Settings.getFPS(); } } diff --git a/src/main/java/minicraft/screen/entry/ArrayEntry.java b/src/main/java/minicraft/screen/entry/ArrayEntry.java index ba576daaf..97cd3d64b 100644 --- a/src/main/java/minicraft/screen/entry/ArrayEntry.java +++ b/src/main/java/minicraft/screen/entry/ArrayEntry.java @@ -9,8 +9,8 @@ public class ArrayEntry extends ListEntry { - private String label; - private T[] options; + private final String label; + protected T[] options; private boolean[] optionVis; private int selection;