From 6862aaba8c4c7ae47bb1ce495474fae344d71c2c Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Wed, 26 Jul 2023 22:25:22 +0800 Subject: [PATCH 1/9] Add ability to horizontally scroll entries --- src/client/java/minicraft/gfx/Font.java | 15 +- .../minicraft/screen/ControlsDisplay.java | 25 ++- src/client/java/minicraft/screen/Menu.java | 121 +++++++++++++-- .../minicraft/screen/OptionsWorldDisplay.java | 1 + .../java/minicraft/screen/PopupDisplay.java | 25 ++- .../minicraft/screen/ResourcePackDisplay.java | 8 +- .../minicraft/screen/WorldGenDisplay.java | 31 ++-- .../minicraft/screen/entry/BlankEntry.java | 3 +- .../minicraft/screen/entry/InputEntry.java | 52 ++++++- .../minicraft/screen/entry/ItemEntry.java | 5 +- .../minicraft/screen/entry/ListEntry.java | 71 ++++++++- .../minicraft/screen/entry/RecipeEntry.java | 5 +- .../screen/entry/SelectableStringEntry.java | 146 ++++++++++++++++++ .../minicraft/screen/entry/StringEntry.java | 23 +-- 14 files changed, 456 insertions(+), 75 deletions(-) create mode 100644 src/client/java/minicraft/screen/entry/SelectableStringEntry.java diff --git a/src/client/java/minicraft/gfx/Font.java b/src/client/java/minicraft/gfx/Font.java index ff141eea8..a08c4bd6f 100644 --- a/src/client/java/minicraft/gfx/Font.java +++ b/src/client/java/minicraft/gfx/Font.java @@ -3,6 +3,8 @@ import minicraft.core.Renderer; import minicraft.core.io.Localization; import minicraft.gfx.SpriteLinker.SpriteType; +import minicraft.screen.entry.ListEntry; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -22,19 +24,22 @@ public class Font { public static void draw(String msg, Screen screen, int x, int y) { draw(msg, screen, x, y, -1); } /** Draws the message to the x & y coordinates on the screen. */ - public static void - draw(String msg, Screen screen, int x, int y, int whiteTint) { + public static void draw(String msg, Screen screen, int x, int y, int whiteTint) { draw(msg, screen, x, y, whiteTint, null); } + public static void draw(String msg, Screen screen, int x, int y, int whiteTint, @Nullable ListEntry.IntRange bounds) { msg = msg.toUpperCase(Localization.getSelectedLocale()); //makes all letters uppercase. for (int i = 0; i < msg.length(); i++) { // Loops through all the characters that you typed int ix = chars.indexOf(msg.charAt(i)); // The current letter in the message loop if (ix >= 0) { // If that character's position is larger than or equal to 0, then render the character on the screen. - screen.render(x + i * textWidth(msg.substring(i, i+1)), y, ix % 32, ix / 32, 0, Renderer.spriteLinker.getSheet(SpriteType.Gui, "font"), whiteTint); + int xx = x + i * textWidth(msg.substring(i, i+1)); + if (bounds == null || xx >= bounds.lower && xx + MinicraftImage.boxWidth <= bounds.upper) + screen.render(xx, y, ix % 32, ix / 32, 0, Renderer.spriteLinker.getSheet(SpriteType.Gui, "font"), whiteTint); } } } - public static void drawColor(String message, Screen screen, int x, int y) { + public static void drawColor(String message, Screen screen, int x, int y) { drawColor(message, screen, x, y, null); } + public static void drawColor(String message, Screen screen, int x, int y, @Nullable ListEntry.IntRange bounds) { // Set default color message if it doesn't have initially if (message.charAt(0) != Color.COLOR_CHAR) { message = Color.WHITE_CODE + message; @@ -58,7 +63,7 @@ public static void drawColor(String message, Screen screen, int x, int y) { color = Color.WHITE_CODE; } - Font.draw(text, screen, x + leading, y, Color.get(color)); + Font.draw(text, screen, x + leading, y, Color.get(color), bounds); leading += Font.textWidth(text); } } diff --git a/src/client/java/minicraft/screen/ControlsDisplay.java b/src/client/java/minicraft/screen/ControlsDisplay.java index 842105ba0..f50ecc8bf 100644 --- a/src/client/java/minicraft/screen/ControlsDisplay.java +++ b/src/client/java/minicraft/screen/ControlsDisplay.java @@ -5,9 +5,11 @@ import minicraft.core.io.Localization; import minicraft.gfx.Color; import minicraft.gfx.Font; +import minicraft.gfx.MinicraftImage; import minicraft.gfx.Point; import minicraft.gfx.Screen; import minicraft.screen.entry.ListEntry; +import minicraft.screen.entry.SelectableStringEntry; import minicraft.screen.entry.StringEntry; import java.util.ArrayList; @@ -20,10 +22,11 @@ public class ControlsDisplay extends Display { private int displaying; // 0 for keyboard; 1 for controller. public ControlsDisplay() { - super(true, true, new Menu.Builder(false, 0, RelPos.CENTER) + super(true, true, new Menu.Builder(false, 0, RelPos.LEFT) .setSelectable(true) - .setPositioning(new Point(Screen.w/2, 20), RelPos.BOTTOM) + .setPositioning(new Point(0, 20), RelPos.BOTTOM_RIGHT) .setDisplayLength(17) + .setSize(Screen.w, 17 * 8) .createMenu() ); @@ -36,17 +39,23 @@ public ControlsDisplay() { private void initKeyControls() { ArrayList entries = new ArrayList<>(23); - for (int i = 0; i < 23; i++) - entries.addAll(Arrays.asList(StringEntry.useLines(String.format("minicraft.displays.controls.display.keyboard.%02d", i)))); - entries.forEach(e -> e.setSelectable(true)); + for (int i = 0; i < 23; i++) { + SelectableStringEntry entry = new SelectableStringEntry(String.format("minicraft.displays.controls.display.keyboard.%02d", i)); + entry.setScrollingTextRenderTicker( + new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2), MinicraftImage.boxWidth * 2); + entries.add(entry); + } keyControls = entries.toArray(new ListEntry[0]); } private void initControllerControls() { ArrayList entries = new ArrayList<>(16); - for (int i = 0; i < 16; i++) - entries.addAll(Arrays.asList(StringEntry.useLines(String.format("minicraft.displays.controls.display.controller.%02d", i)))); - entries.forEach(e -> e.setSelectable(true)); + for (int i = 0; i < 16; i++) { + SelectableStringEntry entry = new SelectableStringEntry(String.format("minicraft.displays.controls.display.controller.%02d", i)); + entry.setScrollingTextRenderTicker( + new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2), MinicraftImage.boxWidth * 2); + entries.add(entry); + } controllerControls = entries.toArray(new ListEntry[0]); } diff --git a/src/client/java/minicraft/screen/Menu.java b/src/client/java/minicraft/screen/Menu.java index 21738dbad..687fc69a4 100644 --- a/src/client/java/minicraft/screen/Menu.java +++ b/src/client/java/minicraft/screen/Menu.java @@ -34,6 +34,7 @@ public class Menu { private int spacing = 0; private Rectangle bounds = null; private Rectangle entryBounds = null; + private Rectangle renderingBounds = null; private RelPos entryPos = RelPos.CENTER; // the x part of this is re-applied per entry, while the y part is calculated once using the cumulative height of all entries and spacing. private String title = ""; @@ -76,6 +77,7 @@ protected Menu(Menu m) { spacing = m.spacing; bounds = m.bounds == null ? null : new Rectangle(m.bounds); entryBounds = m.entryBounds == null ? null : new Rectangle(m.entryBounds); + renderingBounds = m.renderingBounds; entryPos = m.entryPos; title = m.title; titleColor = m.titleColor; @@ -170,11 +172,16 @@ public void tick(InputHandler input) { if(!selectable || entries.size() == 0) return; int prevSel = selection; - if (input.inputPressed("cursor-up")) selection--; - if (input.inputPressed("cursor-down")) selection++; - if (input.getKey("shift-cursor-up").clicked && selectionSearcher == 0) selectionSearcher -= 2; - if (input.getKey("shift-cursor-down").clicked && selectionSearcher == 0) selectionSearcher += 2; - if (prevSel != selection && selectionSearcher != 0) selection = prevSel; + if (input.getKey("ALT").down) { + if (input.inputPressed("cursor-left")) entries.get(selection).displaceRight(entryBounds.getWidth()); + if (input.inputPressed("cursor-right")) entries.get(selection).displaceLeft(entryBounds.getWidth()); + } else { + if (input.inputPressed("cursor-up")) selection--; + if (input.inputPressed("cursor-down")) selection++; + if (input.getKey("shift-cursor-up").clicked && selectionSearcher == 0) selectionSearcher -= 2; + if (input.getKey("shift-cursor-down").clicked && selectionSearcher == 0) selectionSearcher += 2; + if (prevSel != selection && selectionSearcher != 0) selection = prevSel; + } if (useSearcherBar) { if (input.getKey("searcher-bar").clicked) { @@ -336,6 +343,7 @@ public void render(Screen screen) { int extra = diff*(ListEntry.getHeight() + spacing) / 2; y += extra; } + ListEntry.IntRange renderingXBounds = new ListEntry.IntRange(renderingBounds.getLeft(), renderingBounds.getRight()); for(int i = offset; i < (wrap ? offset + displayLength : Math.min(offset + displayLength, entries.size())); i++) { if(special && i-offset >= entries.size()) break; @@ -343,17 +351,21 @@ public void render(Screen screen) { ListEntry entry = entries.get(idx); if(!(entry instanceof BlankEntry)) { - Point pos = entryPos.positionRect(new Dimension(entry.getWidth(), ListEntry.getHeight()), new Rectangle(entryBounds.getLeft(), y, entryBounds.getWidth(), ListEntry.getHeight(), Rectangle.CORNER_DIMS)); + int entryWidth = entry.getWidth(); + Point pos = entryPos.positionRect(new Dimension(entryWidth, ListEntry.getHeight()), new Rectangle(entryBounds.getLeft(), y, entryBounds.getWidth(), ListEntry.getHeight(), Rectangle.CORNER_DIMS)); boolean selected = idx == selection; + int xDisplacement = selected ? entry.getXDisplacement() : 0; + boolean hideOverflow = entry.hideWhenOverflow(); if (searcherBarActive && useSearcherBar) { - entry.render(screen, pos.x, pos.y, selected, typingSearcher, Color.YELLOW); + entry.render(screen, pos.x + xDisplacement, pos.y, selected, hideOverflow ? renderingXBounds : null, typingSearcher, Color.YELLOW); } else { - entry.render(screen, pos.x, pos.y, selected); + entry.render(screen, pos.x + xDisplacement, pos.y, selected, hideOverflow ? renderingXBounds : null); } if (selected && entry.isSelectable()) { // draw the arrows - Font.draw("> ", screen, pos.x - Font.textWidth("> "), y, ListEntry.COL_SLCT); - Font.draw(" <", screen, pos.x + entry.getWidth(), y, ListEntry.COL_SLCT); + boolean hiddenOverflow = hideOverflow && entryWidth > entryBounds.getWidth(); + Font.draw("> ", screen, hiddenOverflow ? entryBounds.getLeft() - Font.textWidth("> ") : pos.x + xDisplacement - Font.textWidth("> "), y, ListEntry.COL_SLCT); + Font.draw(" <", screen, hiddenOverflow ? entryBounds.getRight() : pos.x + xDisplacement + entryWidth, y, ListEntry.COL_SLCT); } } @@ -426,6 +438,9 @@ public static class Builder { @NotNull private Point anchor = center; @NotNull private RelPos menuPos = RelPos.CENTER; private Dimension menuSize = null; + private Rectangle limitingBounds = null; + private Rectangle renderingBounds = null; + private boolean removeEntryPeaks = false; private boolean searcherBar; @@ -462,6 +477,31 @@ public Builder setBounds(Rectangle rect) { public Builder setDisplayLength(int numEntries) { menu.displayLength = numEntries; return this; } + /** This restricts the maximum bounds this menu could be. */ + public Builder setMaxBounds(Rectangle rect) { + limitingBounds = rect; + return this; + } + + public Builder setMaxBoundsAsMaxMenuBounds() { + limitingBounds = new Rectangle(0, 0, Screen.w, Screen.h, Rectangle.CORNERS); + return this; + } + + public Builder setRemoveEntryPeaks(boolean removeEntryPeaks) { + this.removeEntryPeaks = removeEntryPeaks; + return this; + } + + public Builder setRenderingBounds(Rectangle rect) { + renderingBounds = rect; + return this; + } + + public Builder setMaxBoundsAsRenderingBounds() { + renderingBounds = new Rectangle(0, 0, Screen.w, Screen.h, Rectangle.CORNERS); + return this; + } public Builder setTitlePos(RelPos rp) { titlePos = (rp == null ? RelPos.TOP : rp); return this; } @@ -587,11 +627,46 @@ else if (c.xIndex == 2) // must be center right if(menuSize == null) { int width = titleDim.width; - for(ListEntry entry: menu.entries) { - int entryWidth = entry.getWidth(); - if(menu.isSelectable() && !entry.isSelectable()) - entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); - width = Math.max(width, entryWidth); + // There should be valid peaks to be handled when the number of entries is greater than 1. + if (removeEntryPeaks && menu.entries.size() > 1) { + // First write into an array to more easily find peaks. + int[] entryWidths = new int[menu.entries.size()]; + for (int i = 0; i < menu.entries.size(); ++i) { + entryWidths[i] = menu.entries.get(i).getWidth(); + } + + // Reference: https://www.geeksforgeeks.org/print-all-the-peaks-and-troughs-in-an-array-of-integers/ + boolean handled = false; // For security check. + ArrayList peaks = new ArrayList<>(); + for (int i = 0; i < entryWidths.length; ++i) { + // Checks if the element is greater than the neighbours + if ((i == 0 || entryWidths[i] > entryWidths[i - 1]) && (i == entryWidths.length - 1 || entryWidths[i] > entryWidths[i + 1])) { + peaks.add(entryWidths[i]); + } else { + int entryWidth = entryWidths[i]; + if (menu.isSelectable() && !menu.entries.get(i).isSelectable()) + entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); + width = Math.max(width, entryWidth); + handled = true; + } + } + + if (!handled) width = Math.max(width, Arrays.stream(entryWidths).max().getAsInt()); + else { + if (peaks.size() > 0) { // Count in the peaks into the resultant width with small ratio. + double peaksAvg = (double) peaks.stream().mapToInt(a -> a).sum() / peaks.size(); + double ratio = .07 / Math.max(peaksAvg / width - 1, .07); // max is used to prevent extreme case. + width = (int) (width * ratio + (1 - ratio) * peaksAvg); + width -= width % MinicraftImage.boxWidth; // strip extra pixels + } + } + } else { + for(ListEntry entry: menu.entries) { + int entryWidth = entry.getWidth(); + if(menu.isSelectable() && !entry.isSelectable()) + entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); + width = Math.max(width, entryWidth); + } } if(menu.displayLength > 0) { // has been set; use to determine entry bounds @@ -632,10 +707,23 @@ else if(menuPos.yIndex == 2) // based on the menu centering, and the anchor, determine the upper-left point from which to draw the menu. menu.bounds = menuPos.positionRect(menuSize, anchor, new Rectangle()); // reset to a value that is actually useful to the menu + if (limitingBounds != null) { + // Exceeded areas are cut off from the bounds. + if (!limitingBounds.intersects(menu.bounds)) + throw new IndexOutOfBoundsException("limitingBounds does not intersect bounds"); + int x = Math.max(limitingBounds.getLeft(), menu.bounds.getLeft()); // Get the rightmost left bound + int y = Math.max(limitingBounds.getTop(), menu.bounds.getTop()); // Get the bottommost top bound + int x1 = Math.min(limitingBounds.getBottom(), menu.bounds.getBottom()); // Get the topmost bottom bound + int y1 = Math.min(limitingBounds.getRight(), menu.bounds.getRight()); // Get the leftmost right bount + menu.bounds = new Rectangle(x, y, x1, y1, Rectangle.CORNERS); // Reset to the bounds after cut off the exceeded bounds + } + menu.entryBounds = border.subtractFrom(menu.bounds); menu.titleLoc = titlePos.positionRect(titleDim, menu.bounds); + menu.renderingBounds = renderingBounds == null ? menu.entryBounds : renderingBounds; + if(titlePos.xIndex == 0 && titlePos.yIndex != 1) menu.titleLoc.x += MinicraftImage.boxWidth; if(titlePos.xIndex == 2 && titlePos.yIndex != 1) @@ -679,6 +767,9 @@ public Builder copy() { b.setTitleColor = setTitleColor; b.titleCol = titleCol; b.searcherBar = searcherBar; + b.limitingBounds = limitingBounds; + b.renderingBounds = renderingBounds; + b.removeEntryPeaks = removeEntryPeaks; return b; } diff --git a/src/client/java/minicraft/screen/OptionsWorldDisplay.java b/src/client/java/minicraft/screen/OptionsWorldDisplay.java index 6a71e13d6..1c4687ac5 100644 --- a/src/client/java/minicraft/screen/OptionsWorldDisplay.java +++ b/src/client/java/minicraft/screen/OptionsWorldDisplay.java @@ -52,6 +52,7 @@ public OptionsWorldDisplay() { menus = new Menu[] { new Menu.Builder(false, 6, RelPos.LEFT, entries) .setTitle("minicraft.displays.options_world") + .setRemoveEntryPeaks(true) .createMenu() }; } diff --git a/src/client/java/minicraft/screen/PopupDisplay.java b/src/client/java/minicraft/screen/PopupDisplay.java index 80142eeee..83bd9e20b 100644 --- a/src/client/java/minicraft/screen/PopupDisplay.java +++ b/src/client/java/minicraft/screen/PopupDisplay.java @@ -3,6 +3,8 @@ import com.studiohartman.jamepad.ControllerButton; import minicraft.core.Game; import minicraft.core.io.InputHandler; +import minicraft.gfx.MinicraftImage; +import minicraft.gfx.Rectangle; import minicraft.gfx.Screen; import minicraft.screen.entry.InputEntry; import minicraft.screen.entry.ListEntry; @@ -17,6 +19,7 @@ public class PopupDisplay extends Display { // Using Color codes for coloring in title and plain text messages. private final ArrayList callbacks; + private final Menu.Builder builder; public PopupDisplay(@Nullable PopupConfig config, String... messages) { this(config, false, messages); } public PopupDisplay(@Nullable PopupConfig config, boolean clearScreen, String... messages) { this(config, clearScreen, true, messages); } @@ -26,7 +29,7 @@ public class PopupDisplay extends Display { public PopupDisplay(@Nullable PopupConfig config, boolean clearScreen, boolean menuFrame, ListEntry... entries) { super(clearScreen, true); - Menu.Builder builder = new Menu.Builder(menuFrame, 0, RelPos.CENTER, entries); + builder = new Menu.Builder(menuFrame, 0, RelPos.CENTER, entries); if (config != null) { if (config.title != null) @@ -43,6 +46,26 @@ public PopupDisplay(@Nullable PopupConfig config, boolean clearScreen, boolean m menus = new Menu[] { builder.createMenu() }; else menus = new Menu[] { onScreenKeyboardMenu, builder.createMenu() }; + + Rectangle menuBounds = menus[onScreenKeyboardMenu == null ? 0 : 1].getBounds(); + for (ListEntry entry : entries) { + if (entry instanceof InputEntry) { + ((InputEntry) entry).setChangeListener(v -> update()); + ((InputEntry) entry).setRenderingBounds(new ListEntry.IntRange(MinicraftImage.boxWidth * 3, Screen.w - MinicraftImage.boxWidth * 3)) + .setEntryPos(RelPos.CENTER) + .setBounds(new ListEntry.IntRange(menuBounds.getLeft() + MinicraftImage.boxWidth * 3, menuBounds.getRight() - MinicraftImage.boxWidth * 3)); + } + } + } + + private void update() { + menus[onScreenKeyboardMenu == null ? 0 : 1] = builder.createMenu(); + Rectangle menuBounds = menus[onScreenKeyboardMenu == null ? 0 : 1].getBounds(); + for (ListEntry entry : menus[onScreenKeyboardMenu == null ? 0 : 1].getEntries()) { + if (entry instanceof InputEntry) { + ((InputEntry) entry).setBounds(new ListEntry.IntRange(menuBounds.getLeft() + MinicraftImage.boxWidth * 3, menuBounds.getRight() - MinicraftImage.boxWidth * 3)); + } + } } OnScreenKeyboardMenu onScreenKeyboardMenu; diff --git a/src/client/java/minicraft/screen/ResourcePackDisplay.java b/src/client/java/minicraft/screen/ResourcePackDisplay.java index e7fd1822a..48a818b12 100644 --- a/src/client/java/minicraft/screen/ResourcePackDisplay.java +++ b/src/client/java/minicraft/screen/ResourcePackDisplay.java @@ -18,6 +18,7 @@ import minicraft.saveload.Save; import minicraft.screen.entry.ListEntry; import minicraft.screen.entry.SelectEntry; +import minicraft.screen.entry.StringEntry; import minicraft.util.BookData; import minicraft.util.Logging; import org.jetbrains.annotations.NotNull; @@ -189,6 +190,7 @@ public ResourcePackDisplay() { menus[1].translate(menus[0].getBounds().getRight() - menus[1].getBounds().getLeft() + padding, 0); fileWatcher = new WatcherThread(); + helpPositionTextEntry.setAlternatingScrollingTextRenderTicker(new ListEntry.IntRange(0, Screen.w), (Screen.w - helpPositionTextEntry.getWidth()) / 2); } @Override @@ -321,6 +323,8 @@ public void onExit() { @Override public void tick(InputHandler input) { + helpPositionTextEntry.tick(input); // For rendering purpose + // Overrides the default tick handler. if (input.getKey("right").clicked) { // Move cursor to the second list. if (selection == 0) { @@ -379,6 +383,8 @@ public void tick(InputHandler input) { super.tick(input); } + private final StringEntry helpPositionTextEntry = new StringEntry("minicraft.displays.resource_packs.display.help.position", Color.DARK_GRAY); + @Override public void render(Screen screen) { super.render(screen); @@ -390,7 +396,7 @@ public void render(Screen screen) { if (Game.input.anyControllerConnected()) Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.keyboard_needed"), screen, Screen.h - 33, Color.DARK_GRAY); Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.move", Game.input.getMapping("cursor-down"), Game.input.getMapping("cursor-up")), screen, Screen.h - 25, Color.DARK_GRAY); Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.select", Game.input.getMapping("SELECT")), screen, Screen.h - 17, Color.DARK_GRAY); - Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.position"), screen, Screen.h - 9, Color.DARK_GRAY); + helpPositionTextEntry.render(screen, (Screen.w - helpPositionTextEntry.getWidth()) / 2 + helpPositionTextEntry.getXDisplacement(), Screen.h - 9, false, null); ArrayList packs = selection == 0 ? resourcePacks : loadedPacks; if (packs.size() > 0) { // If there is any pack that can be selected. diff --git a/src/client/java/minicraft/screen/WorldGenDisplay.java b/src/client/java/minicraft/screen/WorldGenDisplay.java index 660bccafb..d09c1764a 100644 --- a/src/client/java/minicraft/screen/WorldGenDisplay.java +++ b/src/client/java/minicraft/screen/WorldGenDisplay.java @@ -8,10 +8,14 @@ import minicraft.core.io.Settings; import minicraft.gfx.Color; import minicraft.gfx.Font; +import minicraft.gfx.MinicraftImage; +import minicraft.gfx.Rectangle; import minicraft.gfx.Screen; import minicraft.screen.entry.InputEntry; +import minicraft.screen.entry.ListEntry; import minicraft.screen.entry.SelectEntry; import minicraft.util.Logging; +import org.jetbrains.annotations.Nullable; import java.nio.file.InvalidPathException; import java.nio.file.Paths; @@ -127,19 +131,20 @@ public String getUserInput() { return super.getUserInput().toLowerCase(Localization.getSelectedLocale()); } - @Override - public void render(Screen screen, int x, int y, boolean isSelected) { - super.render(screen, isGen? - (getUserInput().length() > 11? x - (getUserInput().length()-11) * 8: x): - x, y, isSelected); - } +// @Override +// public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { +// super.render(screen, isGen? +// (getUserInput().length() > 11? x - (getUserInput().length()-11) * 8: x): +// x, y, isSelected, bounds); +// } }; } public WorldGenDisplay() { super(true); - InputEntry nameField = makeWorldNameInput("minicraft.displays.world_gen.enter_world", WorldSelectDisplay.getWorldNames(), "", true); + InputEntry nameField = makeWorldNameInput("minicraft.displays.world_gen.enter_world", WorldSelectDisplay.getWorldNames(), "", true) + .setRenderingBounds(new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2)).setEntryPos(RelPos.LEFT); SelectEntry nameHelp = new SelectEntry("minicraft.displays.world_gen.troublesome_input", () -> Game.setDisplay(new PopupDisplay(null, "minicraft.displays.world_gen.troublesome_input.msg"))) { @Override @@ -163,7 +168,7 @@ public int getColor(boolean isSelected) { worldSeed = new InputEntry("minicraft.displays.world_gen.world_seed", "[-!\"#%/()=+,a-zA-Z0-9]+", 20) { @Override public boolean isValid() { return true; } - }; + }.setRenderingBounds(new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2)).setEntryPos(RelPos.LEFT); Menu mainMenu = new Menu.Builder(false, 10, RelPos.LEFT, @@ -178,8 +183,8 @@ public int getColor(boolean isSelected) { Game.setDisplay(new LoadingDisplay()); }) { @Override - public void render(Screen screen, int x, int y, boolean isSelected) { - Font.draw(toString(), screen, x, y, Color.CYAN); + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { + Font.draw(toString(), screen, x, y, Color.CYAN, bounds); } }, @@ -193,8 +198,14 @@ public void render(Screen screen, int x, int y, boolean isSelected) { .setDisplayLength(5) .setScrollPolicies(0.8f, false) .setTitle("minicraft.displays.world_gen.title") + .setMaxBoundsAsRenderingBounds() .createMenu(); + Rectangle menuBounds = mainMenu.getBounds(); + ListEntry.IntRange xBounds = new ListEntry.IntRange(menuBounds.getLeft() + MinicraftImage.boxWidth * 2, menuBounds.getRight() - MinicraftImage.boxWidth * 2); + nameField.setBounds(xBounds); + worldSeed.setBounds(xBounds); + onScreenKeyboardMenu = OnScreenKeyboardMenu.checkAndCreateMenu(); if (onScreenKeyboardMenu == null) menus = new Menu[] { mainMenu }; diff --git a/src/client/java/minicraft/screen/entry/BlankEntry.java b/src/client/java/minicraft/screen/entry/BlankEntry.java index 9f42b58ef..e8eb20adc 100644 --- a/src/client/java/minicraft/screen/entry/BlankEntry.java +++ b/src/client/java/minicraft/screen/entry/BlankEntry.java @@ -3,6 +3,7 @@ import minicraft.core.io.InputHandler; import minicraft.gfx.MinicraftImage; import minicraft.gfx.Screen; +import org.jetbrains.annotations.Nullable; public class BlankEntry extends ListEntry { @@ -14,7 +15,7 @@ public BlankEntry() { public void tick(InputHandler input) {} @Override - public void render(Screen screen, int x, int y, boolean isSelected) {} + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) {} @Override public int getWidth() { diff --git a/src/client/java/minicraft/screen/entry/InputEntry.java b/src/client/java/minicraft/screen/entry/InputEntry.java index 0ca8aab3c..12b18aafd 100644 --- a/src/client/java/minicraft/screen/entry/InputEntry.java +++ b/src/client/java/minicraft/screen/entry/InputEntry.java @@ -5,13 +5,19 @@ import minicraft.core.io.Localization; import minicraft.gfx.Color; import minicraft.gfx.Font; +import minicraft.gfx.MinicraftImage; import minicraft.gfx.Screen; +import minicraft.screen.RelPos; +import org.jetbrains.annotations.Nullable; public class InputEntry extends ListEntry { private String prompt; private String regex; private int maxLength; + private IntRange bounds; + private IntRange renderingBounds; + private RelPos entryPos; private String userInput; @@ -37,8 +43,13 @@ public InputEntry(String prompt, String regex, int maxLen, String initValue) { public void tick(InputHandler input) { String prev = userInput; userInput = input.addKeyTyped(userInput, regex); - if (!prev.equals(userInput) && listener != null) - listener.onChange(userInput); + if (!prev.equals(userInput)) { + if (listener != null) { + listener.onChange(userInput); + } + + updateCursorDisplacement(); + } if (maxLength > 0 && userInput.length() > maxLength) userInput = userInput.substring(0, maxLength); // truncates extra @@ -62,8 +73,41 @@ public String toString() { return Localization.getLocalized(prompt) + (prompt.length() == 0 ? "" : ": ") + userInput; } - public void render(Screen screen, int x, int y, boolean isSelected) { - Font.draw(toString(), screen, x, y, isValid() ? isSelected ? Color.GREEN : COL_UNSLCT : Color.RED); + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { + Font.draw(toString(), screen, x, y, isValid() ? isSelected ? Color.GREEN : COL_UNSLCT : Color.RED, bounds); + } + + private void updateCursorDisplacement() { + IntRange renderingBounds = this.renderingBounds == null ? bounds : this.renderingBounds; + if (bounds != null && entryPos != null) { + int spaceWidth = renderingBounds.upper - bounds.lower; + int width = getWidth(); + if (width <= spaceWidth) { + xDisplacement = 0; + } else { + if (entryPos.xIndex == 0) { // Left + xDisplacement = spaceWidth - width; + } else if (entryPos.xIndex == 1) { // Center + // Assume that the menu is rebuilt when updated. + xDisplacement = (spaceWidth - width) / 2; + } // xIndex == 2 is not handled. + } + } + } + + public InputEntry setBounds(IntRange bounds) { + this.bounds = bounds; + return this; + } + + public InputEntry setEntryPos(RelPos entryPos) { + this.entryPos = entryPos; + return this; + } + + public InputEntry setRenderingBounds(IntRange bounds) { + this.renderingBounds = bounds; + return this; } public boolean isValid() { diff --git a/src/client/java/minicraft/screen/entry/ItemEntry.java b/src/client/java/minicraft/screen/entry/ItemEntry.java index 4da6d2a48..8c10c5193 100644 --- a/src/client/java/minicraft/screen/entry/ItemEntry.java +++ b/src/client/java/minicraft/screen/entry/ItemEntry.java @@ -3,6 +3,7 @@ import minicraft.core.io.InputHandler; import minicraft.gfx.Screen; import minicraft.item.Item; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -25,8 +26,8 @@ public static ItemEntry[] useItems(List items) { public void tick(InputHandler input) {} @Override - public void render(Screen screen, int x, int y, boolean isSelected) { - super.render(screen, x, y, true); + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { + super.render(screen, x, y, true, bounds); screen.render(x, y, item.sprite); } diff --git a/src/client/java/minicraft/screen/entry/ListEntry.java b/src/client/java/minicraft/screen/entry/ListEntry.java index 9b60eda8d..6d01a0b2a 100644 --- a/src/client/java/minicraft/screen/entry/ListEntry.java +++ b/src/client/java/minicraft/screen/entry/ListEntry.java @@ -3,7 +3,9 @@ import minicraft.core.io.InputHandler; import minicraft.gfx.Color; import minicraft.gfx.Font; +import minicraft.gfx.MinicraftImage; import minicraft.gfx.Screen; +import org.jetbrains.annotations.Nullable; import java.util.Locale; @@ -14,18 +16,20 @@ public abstract class ListEntry { private boolean selectable = true, visible = true; + protected int xDisplacement = 0; // Displacing to the left for negative values, right for positive values. + /** * Ticks the entry. Used to handle input from the InputHandler * @param input InputHandler used to get player input. */ public abstract void tick(InputHandler input); - public void render(Screen screen, int x, int y, boolean isSelected, String contain, int containColor) { + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds, String contain, int containColor) { if (!visible) { return; } - render(screen, x, y, isSelected); + render(screen, x, y, isSelected, bounds); if (contain == null || contain.isEmpty()) { return; } @@ -36,6 +40,15 @@ public void render(Screen screen, int x, int y, boolean isSelected, String conta Font.drawColor(string.replace(contain, Color.toStringCode(containColor) + contain + Color.WHITE_CODE), screen, x, y); } + public static class IntRange { + public final int lower; + public final int upper; + public IntRange(int lower, int upper) { + this.lower = lower; + this.upper = upper; + } + } + /** * Renders the entry to the given screen. * Coordinate origin is in the top left corner of the entry space. @@ -43,14 +56,15 @@ public void render(Screen screen, int x, int y, boolean isSelected, String conta * @param x X coordinate * @param y Y coordinate * @param isSelected true if the entry is selected, false otherwise + * @param bounds X rendering bounds */ - public void render(Screen screen, int x, int y, boolean isSelected) { + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { if (visible) { String text = toString().replace(Color.WHITE_CODE + Color.GRAY_CODE, Color.toStringCode(getColor(isSelected))); if (text.contains(String.valueOf(Color.COLOR_CHAR))) - Font.drawColor(Color.toStringCode(getColor(isSelected)) + text, screen, x, y); + Font.drawColor(Color.toStringCode(getColor(isSelected)) + text, screen, x, y, bounds); else - Font.draw(text, screen, x, y, getColor(isSelected)); + Font.draw(text, screen, x, y, getColor(isSelected), bounds); } } @@ -77,6 +91,53 @@ public static int getHeight() { return Font.textHeight(); } + /** + * Displaces the entry to the left in rendering, with input right. + * @param boundsWidth the length of space in x-axis bounds + */ + public void displaceLeft(int boundsWidth) { + int width = getWidth(); + if (boundsWidth < width) + xDisplacement = Math.max(xDisplacement - MinicraftImage.boxWidth, boundsWidth - width); + } + + /** + * Displaces the entry to the right in rendering, with input left. + * @param boundsWidth the length of space in x-axis bounds + */ + public void displaceRight(int boundsWidth) { + int width = getWidth(); + if (boundsWidth < width) + xDisplacement = Math.min(xDisplacement + MinicraftImage.boxWidth, width - boundsWidth); + } + + /** + * Calculates the rendering displacement of the entry in the menu. + * @return the x displacement. + */ + public int getXDisplacement() { + return xDisplacement; + } + + /** + * Resets the rendering x displacement to the initial value. + * @return this instance itself + * @see #getXDisplacement() + */ + public ListEntry resetXDisplacement() { + xDisplacement = 0; + return this; + } + + /** + * Returns whether to hide the entry content when the entry exceeds the rendering bounds of the menu. + * If the overflowed content is hidden, the cursor is not displaced the part is not rendered if available. + * @return {@code true} to hide the overflowed parts + */ + public boolean hideWhenOverflow() { + return false; + } + /** * Determines if this entry can be selected. * @return true if it is visible and can be selected, false otherwise. diff --git a/src/client/java/minicraft/screen/entry/RecipeEntry.java b/src/client/java/minicraft/screen/entry/RecipeEntry.java index 85faa2d46..1a1f8d3ba 100644 --- a/src/client/java/minicraft/screen/entry/RecipeEntry.java +++ b/src/client/java/minicraft/screen/entry/RecipeEntry.java @@ -4,6 +4,7 @@ import minicraft.gfx.Font; import minicraft.gfx.Screen; import minicraft.item.Recipe; +import org.jetbrains.annotations.Nullable; import java.util.List; @@ -27,9 +28,9 @@ public RecipeEntry(Recipe r) { public void tick(InputHandler input) {} @Override - public void render(Screen screen, int x, int y, boolean isSelected) { + public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { if (isVisible()) { - Font.draw(toString(), screen, x, y, recipe.getCanCraft() ? COL_SLCT : COL_UNSLCT); + Font.draw(toString(), screen, x, y, recipe.getCanCraft() ? COL_SLCT : COL_UNSLCT, bounds); screen.render(x, y, getItem().sprite); } } diff --git a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java new file mode 100644 index 000000000..56db81bb0 --- /dev/null +++ b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java @@ -0,0 +1,146 @@ +package minicraft.screen.entry; + +import minicraft.core.io.InputHandler; +import minicraft.core.io.Localization; +import minicraft.gfx.Color; +import minicraft.gfx.MinicraftImage; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Range; + +public class SelectableStringEntry extends ListEntry { + + private static final int DEFAULT_COLOR = Color.WHITE; + + private String text; + private final int color; + private final boolean localize; + + private TextRenderTicker ticker; + + public SelectableStringEntry(String text) { + this(text, DEFAULT_COLOR); + } + public SelectableStringEntry(String text, boolean localize) { this(text, DEFAULT_COLOR, localize); } // This might be false as the text might have been localized already. + public SelectableStringEntry(String text, int color) { this(text, color, true); } // This should be always true with the new localization IDs. + public SelectableStringEntry(String text, int color, boolean localize) { + this.text = text; + this.localize = localize; + this.color = color; + } + + protected abstract static class TextRenderTicker { + protected static final int DEFAULT_CYCLING_PERIOD = 90; + + protected int tick = 0; + + public abstract void tick(); + } + + private class HorizontalAlternatingScrollingTextRenderTicker extends TextRenderTicker { + private final IntRange bounds; + private final int originX; + + @Range(from = -1, to = 1) + private int direction = 0; // Number line direction; text movement + + public HorizontalAlternatingScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { + this.bounds = bounds; + this.originX = originX; + } + + @Override + public void tick() { + int width = getWidth(); + if (width > bounds.upper - bounds.lower) { + if (direction != 0) { + if (tick++ == 5) { + xDisplacement += direction * MinicraftImage.boxWidth; + if (originX + xDisplacement == bounds.lower || // Left side of text tips at left bound + originX + xDisplacement + width == bounds.upper) { // Stop if destination is reached + direction = 0; + } + + tick = 0; + } + } else if (tick++ == DEFAULT_CYCLING_PERIOD) { + if (originX + xDisplacement + width == bounds.upper) { // Right side of text tips at right bound + direction = 1; // Right + } else { + direction = -1; // Left + } + + tick = 0; + } + } + } + } + + private class HorizontalScrollingTextRenderTicker extends TextRenderTicker { + private final IntRange bounds; + private final int originX; + + private boolean moving = false; + + public HorizontalScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { + this.bounds = bounds; + this.originX = originX; + } + + @Override + public void tick() { + int width = getWidth(); + if (width > bounds.upper - bounds.lower) { + if (moving) { + if (tick++ == 5) { + if (originX + xDisplacement + width == bounds.lower) { // Right side of text tips at left bound + xDisplacement = bounds.upper - originX; // Moves to the rightmost + } + + xDisplacement -= MinicraftImage.boxWidth; + if (xDisplacement == 0) { // Moves back to the original point + moving = false; // Pauses the scrolling + } + + tick = 0; + } + } else if (tick++ == DEFAULT_CYCLING_PERIOD) { + moving = true; + tick = 0; + } + } + } + } + + @Override + public boolean hideWhenOverflow() { + return true; + } + + public void setAlternatingScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { + ticker = new HorizontalAlternatingScrollingTextRenderTicker(bounds, originX); + } + + public void setScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { + ticker = new HorizontalScrollingTextRenderTicker(bounds, originX); + } + + public void setRenderTicker(TextRenderTicker renderTicker) { + ticker = renderTicker; + } + + public void setText(String text) { + this.text = text; + } + + @Override + public void tick(InputHandler input) { + if (ticker != null) + ticker.tick(); + } + + @Override + public int getColor(boolean isSelected) { return color; } + + @Override + public String toString() { return localize? Localization.getLocalized(text): text; } +} diff --git a/src/client/java/minicraft/screen/entry/StringEntry.java b/src/client/java/minicraft/screen/entry/StringEntry.java index de22241aa..419f120fb 100644 --- a/src/client/java/minicraft/screen/entry/StringEntry.java +++ b/src/client/java/minicraft/screen/entry/StringEntry.java @@ -9,14 +9,10 @@ import java.util.ArrayList; // an unselectable line. -public class StringEntry extends ListEntry { +public class StringEntry extends SelectableStringEntry { private static final int DEFAULT_COLOR = Color.WHITE; - private String text; - private int color; - private boolean localize; - /** * */ @@ -42,22 +38,7 @@ public StringEntry(String text) { public StringEntry(String text, boolean localize) { this(text, DEFAULT_COLOR, localize); } // This might be false as the text might have been localized already. public StringEntry(String text, int color) { this(text, color, true); } // This should be always true with the new localization IDs. public StringEntry(String text, int color, boolean localize) { + super(text, color, localize); setSelectable(false); - this.text = text; - this.localize = localize; - this.color = color; - } - - public void setText(String text) { - this.text = text; } - - @Override - public void tick(InputHandler input) {} - - @Override - public int getColor(boolean isSelected) { return color; } - - @Override - public String toString() { return localize? Localization.getLocalized(text): text; } } From 1bec04eecf03494d370143c5bd7c600039d485d0 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Wed, 26 Jul 2023 22:51:04 +0800 Subject: [PATCH 2/9] Fix some entry problems --- src/client/java/minicraft/screen/Menu.java | 2 +- src/client/java/minicraft/screen/entry/ListEntry.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/client/java/minicraft/screen/Menu.java b/src/client/java/minicraft/screen/Menu.java index 687fc69a4..a27d8ef9f 100644 --- a/src/client/java/minicraft/screen/Menu.java +++ b/src/client/java/minicraft/screen/Menu.java @@ -355,7 +355,7 @@ public void render(Screen screen) { Point pos = entryPos.positionRect(new Dimension(entryWidth, ListEntry.getHeight()), new Rectangle(entryBounds.getLeft(), y, entryBounds.getWidth(), ListEntry.getHeight(), Rectangle.CORNER_DIMS)); boolean selected = idx == selection; int xDisplacement = selected ? entry.getXDisplacement() : 0; - boolean hideOverflow = entry.hideWhenOverflow(); + boolean hideOverflow = entry.hideWhenOverflow() && entry.isSelectable(); if (searcherBarActive && useSearcherBar) { entry.render(screen, pos.x + xDisplacement, pos.y, selected, hideOverflow ? renderingXBounds : null, typingSearcher, Color.YELLOW); } else { diff --git a/src/client/java/minicraft/screen/entry/ListEntry.java b/src/client/java/minicraft/screen/entry/ListEntry.java index 6d01a0b2a..0a21fdeab 100644 --- a/src/client/java/minicraft/screen/entry/ListEntry.java +++ b/src/client/java/minicraft/screen/entry/ListEntry.java @@ -108,7 +108,7 @@ public void displaceLeft(int boundsWidth) { public void displaceRight(int boundsWidth) { int width = getWidth(); if (boundsWidth < width) - xDisplacement = Math.min(xDisplacement + MinicraftImage.boxWidth, width - boundsWidth); + xDisplacement = Math.min(xDisplacement + MinicraftImage.boxWidth, 0); } /** From a1aac5ba1334526bf157edcc5289d0f227b8365d Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Thu, 8 Feb 2024 11:32:11 +0800 Subject: [PATCH 3/9] Reorganize entry scrolling function A new common class is added: RenderingLimitingModel Controls to entry shifting are limited to only menus internally. Rendering bounds configs are removed. Limiting bounds configs are removed because of its uselessness. Internally, entries are wrapped by MenuListEntry for the controls of entries over menu. --- src/client/java/minicraft/core/Renderer.java | 58 ++-- src/client/java/minicraft/entity/Arrow.java | 2 +- .../java/minicraft/entity/FireSpark.java | 4 +- .../java/minicraft/entity/ItemEntity.java | 4 +- src/client/java/minicraft/entity/Spark.java | 4 +- .../minicraft/entity/furniture/Furniture.java | 2 +- .../java/minicraft/entity/mob/MobAi.java | 4 +- .../java/minicraft/entity/mob/Player.java | 64 ++-- .../java/minicraft/entity/mob/Sheep.java | 4 +- .../minicraft/entity/particle/Particle.java | 2 +- src/client/java/minicraft/gfx/Font.java | 26 +- src/client/java/minicraft/gfx/Rectangle.java | 37 +- src/client/java/minicraft/gfx/Screen.java | 78 ++++- .../java/minicraft/gfx/SpriteAnimation.java | 34 +- src/client/java/minicraft/item/Item.java | 4 +- .../java/minicraft/level/tile/TreeTile.java | 16 +- .../level/tile/farming/PotatoTile.java | 2 +- .../level/tile/farming/WheatTile.java | 2 +- .../minicraft/screen/AchievementsDisplay.java | 2 +- .../minicraft/screen/ControlsDisplay.java | 8 +- .../screen/LevelTransitionDisplay.java | 4 +- src/client/java/minicraft/screen/Menu.java | 321 +++++++++++++----- .../java/minicraft/screen/PopupDisplay.java | 9 - .../java/minicraft/screen/QuestsDisplay.java | 2 +- .../minicraft/screen/ResourcePackDisplay.java | 14 +- .../java/minicraft/screen/SkinDisplay.java | 2 +- .../java/minicraft/screen/TitleDisplay.java | 2 +- .../minicraft/screen/WorldGenDisplay.java | 16 +- .../minicraft/screen/entry/BlankEntry.java | 2 +- .../minicraft/screen/entry/InputEntry.java | 60 +--- .../minicraft/screen/entry/ItemEntry.java | 6 +- .../minicraft/screen/entry/ListEntry.java | 79 +---- .../minicraft/screen/entry/RecipeEntry.java | 6 +- .../screen/entry/SelectableStringEntry.java | 138 +++++--- 34 files changed, 575 insertions(+), 443 deletions(-) diff --git a/src/client/java/minicraft/core/Renderer.java b/src/client/java/minicraft/core/Renderer.java index 01e024f4e..dbec233f7 100644 --- a/src/client/java/minicraft/core/Renderer.java +++ b/src/client/java/minicraft/core/Renderer.java @@ -200,7 +200,7 @@ private static void renderLevel() { for (int y = 0; y < 28; y++) for (int x = 0; x < 48; x++) { // Creates the background for the sky (and dungeon) level: - screen.render(x * 8 - ((xScroll / 4) & 7), y * 8 - ((yScroll / 4) & 7), 0, 0, 0, cloud); + screen.render(null, x * 8 - ((xScroll / 4) & 7), y * 8 - ((yScroll / 4) & 7), 0, 0, 0, cloud); } } @@ -224,7 +224,7 @@ private static void renderGui() { // This draws the black square where the selected item would be if you were holding it if (!isMode("minicraft.settings.mode.creative") || player.activeItem != null) { for (int x = 10; x < 26; x++) { - screen.render(x * 8, Screen.h - 8, 5, 2, 0, hudSheet.getSheet()); + screen.render(null, x * 8, Screen.h - 8, 5, 2, 0, hudSheet.getSheet()); } } @@ -240,11 +240,11 @@ private static void renderGui() { int ac = player.getInventory().count(Items.arrowItem); // "^" is an infinite symbol. if (isMode("minicraft.settings.mode.creative") || ac >= 10000) - Font.drawBackground(" x" + "^", screen, 84, Screen.h - 16); + Font.drawBackground(null, " x" + "^", screen, 84, Screen.h - 16); else - Font.drawBackground(" x" + ac, screen, 84, Screen.h - 16); + Font.drawBackground(null, " x" + ac, screen, 84, Screen.h - 16); // Displays the arrow icon - screen.render(10 * 8 + 4, Screen.h - 16, 4, 1, 0, hudSheet.getSheet()); + screen.render(null, 10 * 8 + 4, Screen.h - 16, 4, 1, 0, hudSheet.getSheet()); } } @@ -353,16 +353,16 @@ private static void renderGui() { for (int i = 1; i <= 30; i++) { // Renders your current red default hearts, golden hearts for 20 HP, obsidian hearts for 30 HP, or black hearts for damaged health. if (i < 11) { - screen.render((i - 1) * 8, Screen.h - 16, 0, 1, 0, hudSheet.getSheet()); // Empty Hearts + screen.render(null, (i - 1) * 8, Screen.h - 16, 0, 1, 0, hudSheet.getSheet()); // Empty Hearts } if (i < player.health + 1 && i < 11) { - screen.render((i - 1) * 8, Screen.h - 16, 0, 0, 0, hudSheet.getSheet()); // Red Hearts + screen.render(null, (i - 1) * 8, Screen.h - 16, 0, 0, 0, hudSheet.getSheet()); // Red Hearts } if (i < player.health + 1 && i < 21 && i >= 11) { - screen.render((i - 11) * 8, Screen.h - 16, 0, 2, 0, hudSheet.getSheet()); // Yellow Hearts + screen.render(null, (i - 11) * 8, Screen.h - 16, 0, 2, 0, hudSheet.getSheet()); // Yellow Hearts } if (i < player.health + 1 && i >= 21) { - screen.render((i - 21) * 8, Screen.h - 16, 0, 3, 0, hudSheet.getSheet()); // Obsidian Hearts + screen.render(null, (i - 21) * 8, Screen.h - 16, 0, 3, 0, hudSheet.getSheet()); // Obsidian Hearts } } for (int i = 0; i < Player.maxStat; i++) { @@ -370,30 +370,30 @@ private static void renderGui() { // Renders armor int armor = player.armor * Player.maxStat / Player.maxArmor; if (i <= armor && player.curArmor != null) { - screen.render(i * 8, Screen.h - 24, player.curArmor.sprite); + screen.render(null, i * 8, Screen.h - 24, player.curArmor.sprite); } if (player.staminaRechargeDelay > 0) { // Creates the white/gray blinking effect when you run out of stamina. if (player.staminaRechargeDelay / 4 % 2 == 0) { - screen.render(i * 8, Screen.h - 8, 1, 2, 0, hudSheet.getSheet()); + screen.render(null, i * 8, Screen.h - 8, 1, 2, 0, hudSheet.getSheet()); } else { - screen.render(i * 8, Screen.h - 8, 1, 1, 0, hudSheet.getSheet()); + screen.render(null, i * 8, Screen.h - 8, 1, 1, 0, hudSheet.getSheet()); } } else { // Renders your current stamina, and uncharged gray stamina. if (i < player.stamina) { - screen.render(i * 8, Screen.h - 8, 1, 0, 0, hudSheet.getSheet()); + screen.render(null, i * 8, Screen.h - 8, 1, 0, 0, hudSheet.getSheet()); } else { - screen.render(i * 8, Screen.h - 8, 1, 1, 0, hudSheet.getSheet()); + screen.render(null, i * 8, Screen.h - 8, 1, 1, 0, hudSheet.getSheet()); } } // Renders hunger if (i < player.hunger) { - screen.render(i * 8 + (Screen.w - 80), Screen.h - 16, 2, 0, 0, hudSheet.getSheet()); + screen.render(null, i * 8 + (Screen.w - 80), Screen.h - 16, 2, 0, 0, hudSheet.getSheet()); } else { - screen.render(i * 8 + (Screen.w - 80), Screen.h - 16, 2, 1, 0, hudSheet.getSheet()); + screen.render(null, i * 8 + (Screen.w - 80), Screen.h - 16, 2, 1, 0, hudSheet.getSheet()); } } } @@ -426,20 +426,20 @@ public static void renderBossbar(int length, String title) { int ACTIVE_BOSSBAR = 5; // sprite x position - screen.render(x + (max_bar_length * 2), y, 0, INACTIVE_BOSSBAR, 1, hudSheet.getSheet()); // left corner + screen.render(null, x + (max_bar_length * 2), y, 0, INACTIVE_BOSSBAR, 1, hudSheet.getSheet()); // left corner // The middle for (int bx = 0; bx < max_bar_length; bx++) { for (int by = 0; by < 1; by++) { - screen.render(x + bx * 2, y + by * 8, 3, INACTIVE_BOSSBAR, 0, hudSheet.getSheet()); + screen.render(null, x + bx * 2, y + by * 8, 3, INACTIVE_BOSSBAR, 0, hudSheet.getSheet()); } } - screen.render(x - 5 , y , 0, ACTIVE_BOSSBAR, 0, hudSheet.getSheet()); // right corner + screen.render(null, x - 5 , y , 0, ACTIVE_BOSSBAR, 0, hudSheet.getSheet()); // right corner for (int bx = 0; bx < bar_length; bx++) { for (int by = 0; by < 1; by++) { - screen.render(x + bx * 2, y + by * 8, 3, ACTIVE_BOSSBAR, 0, hudSheet.getSheet()); + screen.render(null, x + bx * 2, y + by * 8, 3, ACTIVE_BOSSBAR, 0, hudSheet.getSheet()); } } @@ -541,24 +541,24 @@ private static void renderFocusNagger() { int h = 1; // Renders the four corners of the box - screen.render(xx - 8, yy - 8, 0, 6, 0, hudSheet.getSheet()); - screen.render(xx + w * 8, yy - 8, 0, 6, 1, hudSheet.getSheet()); - screen.render(xx - 8, yy + 8, 0, 6, 2, hudSheet.getSheet()); - screen.render(xx + w * 8, yy + 8, 0, 6, 3, hudSheet.getSheet()); + screen.render(null, xx - 8, yy - 8, 0, 6, 0, hudSheet.getSheet()); + screen.render(null, xx + w * 8, yy - 8, 0, 6, 1, hudSheet.getSheet()); + screen.render(null, xx - 8, yy + 8, 0, 6, 2, hudSheet.getSheet()); + screen.render(null, xx + w * 8, yy + 8, 0, 6, 3, hudSheet.getSheet()); // Renders each part of the box... for (int x = 0; x < w; x++) { - screen.render(xx + x * 8, yy - 8, 1, 6, 0, hudSheet.getSheet()); // ...Top part - screen.render(xx + x * 8, yy + 8, 1, 6, 2, hudSheet.getSheet()); // ...Bottom part + screen.render(null, xx + x * 8, yy - 8, 1, 6, 0, hudSheet.getSheet()); // ...Top part + screen.render(null, xx + x * 8, yy + 8, 1, 6, 2, hudSheet.getSheet()); // ...Bottom part } for (int y = 0; y < h; y++) { - screen.render(xx - 8, yy + y * 8, 2, 6, 0, hudSheet.getSheet()); // ...Left part - screen.render(xx + w * 8, yy + y * 8, 2, 6, 1, hudSheet.getSheet()); // ...Right part + screen.render(null, xx - 8, yy + y * 8, 2, 6, 0, hudSheet.getSheet()); // ...Left part + screen.render(null, xx + w * 8, yy + y * 8, 2, 6, 1, hudSheet.getSheet()); // ...Right part } // The middle for (int x = 0; x < w; x++) { - screen.render(xx + x * 8, yy, 3, 6, 0, hudSheet.getSheet()); + screen.render(null, xx + x * 8, yy, 3, 6, 0, hudSheet.getSheet()); } // Renders the focus nagger text with a flash effect... diff --git a/src/client/java/minicraft/entity/Arrow.java b/src/client/java/minicraft/entity/Arrow.java index c5220d4a9..d7d5e103b 100644 --- a/src/client/java/minicraft/entity/Arrow.java +++ b/src/client/java/minicraft/entity/Arrow.java @@ -93,6 +93,6 @@ public boolean isSolid() { @Override public void render(Screen screen) { - screen.render(x - 4, y - 4, sprite); + screen.render(null, x - 4, y - 4, sprite); } } diff --git a/src/client/java/minicraft/entity/FireSpark.java b/src/client/java/minicraft/entity/FireSpark.java index 06dd9daf3..c458a11a7 100644 --- a/src/client/java/minicraft/entity/FireSpark.java +++ b/src/client/java/minicraft/entity/FireSpark.java @@ -96,8 +96,8 @@ public void render(Screen screen) { int xt = 8; int yt = 13; - screen.render(x - 4, y - 4 + 2, sprite.getSprite(), randmirror, false, Color.BLACK); // renders the shadow on the ground - screen.render(x - 4, y - 4 - 2, sprite.getSprite(), randmirror, false, Color.RED); // Renders the spark + screen.render(null, x - 4, y - 4 + 2, sprite.getSprite(), randmirror, false, Color.BLACK); // renders the shadow on the ground + screen.render(null, x - 4, y - 4 - 2, sprite.getSprite(), randmirror, false, Color.RED); // Renders the spark } /** diff --git a/src/client/java/minicraft/entity/ItemEntity.java b/src/client/java/minicraft/entity/ItemEntity.java index 6c7e09083..88b16d8bf 100644 --- a/src/client/java/minicraft/entity/ItemEntity.java +++ b/src/client/java/minicraft/entity/ItemEntity.java @@ -128,8 +128,8 @@ public void render(Screen screen) { if (time / 6 % 2 == 0) return; } - screen.render(x-4, y - 4, item.sprite.getSprite(), 0, false, Color.get(0, 31)); // Item shadow - screen.render(x - 4, y - 4 - (int) zz, item.sprite); // Item + screen.render(null, x-4, y - 4, item.sprite.getSprite(), 0, false, Color.get(0, 31)); // Item shadow + screen.render(null, x - 4, y - 4 - (int) zz, item.sprite); // Item } @Override diff --git a/src/client/java/minicraft/entity/Spark.java b/src/client/java/minicraft/entity/Spark.java index 4ce29a876..4010effa1 100644 --- a/src/client/java/minicraft/entity/Spark.java +++ b/src/client/java/minicraft/entity/Spark.java @@ -77,8 +77,8 @@ public void render(Screen screen) { } sprite.setMirror(randmirror); - screen.render(x - 4, y - 4 + 2, sprite.getSprite(), 0, false, Color.BLACK); // renders the shadow on the ground - screen.render(x - 4, y - 4 - 2, sprite); // Renders the spark + screen.render(null, x - 4, y - 4 + 2, sprite.getSprite(), 0, false, Color.BLACK); // renders the shadow on the ground + screen.render(null, x - 4, y - 4 - 2, sprite); // Renders the spark } /** diff --git a/src/client/java/minicraft/entity/furniture/Furniture.java b/src/client/java/minicraft/entity/furniture/Furniture.java index 148a8098a..34581be14 100644 --- a/src/client/java/minicraft/entity/furniture/Furniture.java +++ b/src/client/java/minicraft/entity/furniture/Furniture.java @@ -68,7 +68,7 @@ public void tick() { } /** Draws the furniture on the screen. */ - public void render(Screen screen) { screen.render(x-8, y-8, sprite); } + public void render(Screen screen) { screen.render(null, x-8, y-8, sprite); } /** Called when the player presses the MENU key in front of this. */ public boolean use(Player player) { return false; } diff --git a/src/client/java/minicraft/entity/mob/MobAi.java b/src/client/java/minicraft/entity/mob/MobAi.java index 8195e9171..2290f5993 100644 --- a/src/client/java/minicraft/entity/mob/MobAi.java +++ b/src/client/java/minicraft/entity/mob/MobAi.java @@ -126,9 +126,9 @@ public void render(Screen screen) { LinkedSprite curSprite = sprites[dir.getDir()][(walkDist >> 3) % sprites[dir.getDir()].length]; if (hurtTime > 0) { - screen.render(xo, yo, curSprite.getSprite(), true); + screen.render(null, xo, yo, curSprite.getSprite(), true); } else { - screen.render(xo, yo, curSprite.getSprite()); + screen.render(null, xo, yo, curSprite.getSprite()); } } diff --git a/src/client/java/minicraft/entity/mob/Player.java b/src/client/java/minicraft/entity/mob/Player.java index a669d05ce..b85939006 100644 --- a/src/client/java/minicraft/entity/mob/Player.java +++ b/src/client/java/minicraft/entity/mob/Player.java @@ -840,21 +840,21 @@ public void render(Screen screen) { // animation effect if (tickTime / 8 % 2 == 0) { - screen.render(xo + 0, yo + 3, 5, 0, 0, hudSheet.getSheet()); // Render the water graphic - screen.render(xo + 8, yo + 3, 5, 0, 1, hudSheet.getSheet()); // Render the mirrored water graphic to the right. + screen.render(null, xo + 0, yo + 3, 5, 0, 0, hudSheet.getSheet()); // Render the water graphic + screen.render(null, xo + 8, yo + 3, 5, 0, 1, hudSheet.getSheet()); // Render the mirrored water graphic to the right. } else { - screen.render(xo + 0, yo + 3, 5, 1, 0, hudSheet.getSheet()); - screen.render(xo + 8, yo + 3, 5, 1, 1, hudSheet.getSheet()); + screen.render(null, xo + 0, yo + 3, 5, 1, 0, hudSheet.getSheet()); + screen.render(null, xo + 8, yo + 3, 5, 1, 1, hudSheet.getSheet()); } } else if (level.getTile(x / 16, y / 16) == Tiles.get("lava")) { if (tickTime / 8 % 2 == 0) { - screen.render(xo + 0, yo + 3, 6, 0, 1, hudSheet.getSheet()); // Render the lava graphic - screen.render(xo + 8, yo + 3, 6, 0, 0, hudSheet.getSheet()); // Render the mirrored lava graphic to the right. + screen.render(null, xo + 0, yo + 3, 6, 0, 1, hudSheet.getSheet()); // Render the lava graphic + screen.render(null, xo + 8, yo + 3, 6, 0, 0, hudSheet.getSheet()); // Render the mirrored lava graphic to the right. } else { - screen.render(xo + 0, yo + 3, 6, 1, 1, hudSheet.getSheet()); - screen.render(xo + 8, yo + 3, 6, 1, 0, hudSheet.getSheet()); + screen.render(null, xo + 0, yo + 3, 6, 1, 1, hudSheet.getSheet()); + screen.render(null, xo + 8, yo + 3, 6, 1, 0, hudSheet.getSheet()); } } } @@ -862,10 +862,10 @@ public void render(Screen screen) { // Renders indicator for what tile the item will be placed on if (activeItem instanceof TileItem && !isSwimming()) { Point t = getInteractionTile(); - screen.render(t.x * 16, t.y * 16, 3, 2, 0, hudSheet.getSheet()); - screen.render(t.x * 16 + 8, t.y * 16, 3, 2, 1, hudSheet.getSheet()); - screen.render(t.x * 16, t.y * 16 + 8, 3, 2, 2, hudSheet.getSheet()); - screen.render(t.x * 16 + 8, t.y * 16 + 8, 3, 2, 3, hudSheet.getSheet()); + screen.render(null, t.x * 16, t.y * 16, 3, 2, 0, hudSheet.getSheet()); + screen.render(null, t.x * 16 + 8, t.y * 16, 3, 2, 1, hudSheet.getSheet()); + screen.render(null, t.x * 16, t.y * 16 + 8, 3, 2, 2, hudSheet.getSheet()); + screen.render(null, t.x * 16 + 8, t.y * 16 + 8, 3, 2, 3, hudSheet.getSheet()); } // Makes the player white if they have just gotten hurt @@ -881,16 +881,16 @@ public void render(Screen screen) { // This makes falling look really cool. int spriteToUse = Math.round(onFallDelay / 2f) % carrySprites.length; curSprite = carrySprites[spriteToUse][(walkDist >> 3) & 1]; - screen.render(xo, yo - 4 * onFallDelay, curSprite.setColor(shirtColor)); + screen.render(null, xo, yo - 4 * onFallDelay, curSprite.setColor(shirtColor)); } else { curSprite = spriteSet[dir.getDir()][(walkDist >> 3) & 1]; // Gets the correct sprite to render. // Render each corner of the sprite if (isSwimming()) { Sprite sprite = curSprite.getSprite(); - screen.render(xo, yo, sprite.spritePixels[0][0], shirtColor); - screen.render(xo + 8, yo, sprite.spritePixels[0][1], shirtColor); + screen.render(null, xo, yo, sprite.spritePixels[0][0], shirtColor); + screen.render(null, xo + 8, yo, sprite.spritePixels[0][1], shirtColor); } else { // Don't render the bottom half if swimming. - screen.render(xo, yo - 4 * onFallDelay, curSprite.setColor(shirtColor)); + screen.render(null, xo, yo - 4 * onFallDelay, curSprite.setColor(shirtColor)); } } @@ -898,31 +898,31 @@ public void render(Screen screen) { if (attackTime > 0) { switch (attackDir) { case UP: // If currently attacking upwards... - screen.render(xo + 0, yo - 4, 3, 0, 0, hudSheet.getSheet()); // Render left half-slash - screen.render(xo + 8, yo - 4, 3, 0, 1, hudSheet.getSheet()); // Render right half-slash (mirror of left). + screen.render(null, xo + 0, yo - 4, 3, 0, 0, hudSheet.getSheet()); // Render left half-slash + screen.render(null, xo + 8, yo - 4, 3, 0, 1, hudSheet.getSheet()); // Render right half-slash (mirror of left). if (attackItem != null && !(attackItem instanceof PowerGloveItem)) { // If the player had an item when they last attacked... - screen.render(xo + 4, yo - 4, attackItem.sprite.getSprite(), 1, false); // Then render the icon of the item, mirrored + screen.render(null, xo + 4, yo - 4, attackItem.sprite.getSprite(), 1, false); // Then render the icon of the item, mirrored } break; case LEFT: // Attacking to the left... (Same as above) - screen.render(xo - 4, yo, 4, 0, 1, hudSheet.getSheet()); - screen.render(xo - 4, yo + 8, 4, 0, 3, hudSheet.getSheet()); + screen.render(null, xo - 4, yo, 4, 0, 1, hudSheet.getSheet()); + screen.render(null, xo - 4, yo + 8, 4, 0, 3, hudSheet.getSheet()); if (attackItem != null && !(attackItem instanceof PowerGloveItem)) { - screen.render(xo - 4, yo + 4, attackItem.sprite.getSprite(), 1, false); + screen.render(null, xo - 4, yo + 4, attackItem.sprite.getSprite(), 1, false); } break; case RIGHT: // Attacking to the right (Same as above) - screen.render(xo + 8 + 4, yo, 4, 0, 0, hudSheet.getSheet()); - screen.render(xo + 8 + 4, yo + 8, 4, 0, 2, hudSheet.getSheet()); + screen.render(null, xo + 8 + 4, yo, 4, 0, 0, hudSheet.getSheet()); + screen.render(null, xo + 8 + 4, yo + 8, 4, 0, 2, hudSheet.getSheet()); if (attackItem != null && !(attackItem instanceof PowerGloveItem)) { - screen.render(xo + 8 + 4, yo + 4, attackItem.sprite.getSprite()); + screen.render(null, xo + 8 + 4, yo + 4, attackItem.sprite.getSprite()); } break; case DOWN: // Attacking downwards (Same as above) - screen.render(xo + 0, yo + 8 + 4, 3, 0, 2, hudSheet.getSheet()); - screen.render(xo + 8, yo + 8 + 4, 3, 0, 3, hudSheet.getSheet()); + screen.render(null, xo + 0, yo + 8 + 4, 3, 0, 2, hudSheet.getSheet()); + screen.render(null, xo + 8, yo + 8 + 4, 3, 0, 3, hudSheet.getSheet()); if (attackItem != null && !(attackItem instanceof PowerGloveItem)) { - screen.render(xo + 4, yo + 8 + 4, attackItem.sprite.getSprite()); + screen.render(null, xo + 4, yo + 8 + 4, attackItem.sprite.getSprite()); } break; case NONE: @@ -934,16 +934,16 @@ public void render(Screen screen) { if (isFishing) { switch (dir) { case UP: - screen.render(xo + 4, yo - 4, activeItem.sprite.getSprite(), 1, false); + screen.render(null, xo + 4, yo - 4, activeItem.sprite.getSprite(), 1, false); break; case LEFT: - screen.render(xo - 4, yo + 4, activeItem.sprite.getSprite(), 1, false); + screen.render(null, xo - 4, yo + 4, activeItem.sprite.getSprite(), 1, false); break; case RIGHT: - screen.render(xo + 8 + 4, yo + 4, activeItem.sprite.getSprite()); + screen.render(null, xo + 8 + 4, yo + 4, activeItem.sprite.getSprite()); break; case DOWN: - screen.render(xo + 4, yo + 8 + 4, activeItem.sprite.getSprite()); + screen.render(null, xo + 4, yo + 8 + 4, activeItem.sprite.getSprite()); break; case NONE: break; diff --git a/src/client/java/minicraft/entity/mob/Sheep.java b/src/client/java/minicraft/entity/mob/Sheep.java index 90a594d49..f44230a75 100644 --- a/src/client/java/minicraft/entity/mob/Sheep.java +++ b/src/client/java/minicraft/entity/mob/Sheep.java @@ -35,9 +35,9 @@ public void render(Screen screen) { LinkedSprite curSprite = curAnim[dir.getDir()][(walkDist >> 3) % curAnim[dir.getDir()].length]; if (hurtTime > 0) { - screen.render(xo, yo, curSprite.getSprite(), true); + screen.render(null, xo, yo, curSprite.getSprite(), true); } else { - screen.render(xo, yo, curSprite); + screen.render(null, xo, yo, curSprite); } } diff --git a/src/client/java/minicraft/entity/particle/Particle.java b/src/client/java/minicraft/entity/particle/Particle.java index 5480f4876..8ca776e09 100644 --- a/src/client/java/minicraft/entity/particle/Particle.java +++ b/src/client/java/minicraft/entity/particle/Particle.java @@ -49,7 +49,7 @@ public void tick() { } @Override - public void render(Screen screen) { screen.render(x, y, sprite); } + public void render(Screen screen) { screen.render(null, x, y, sprite); } @Override public boolean isSolid() { return false; } diff --git a/src/client/java/minicraft/gfx/Font.java b/src/client/java/minicraft/gfx/Font.java index a08c4bd6f..f3d31b109 100644 --- a/src/client/java/minicraft/gfx/Font.java +++ b/src/client/java/minicraft/gfx/Font.java @@ -21,25 +21,23 @@ public class Font { /* The order of the letters in the chars string is represented in the order that they appear in the sprite-sheet. */ - public static void draw(String msg, Screen screen, int x, int y) { draw(msg, screen, x, y, -1); } + public static void draw(String msg, Screen screen, int x, int y) { draw(null, msg, screen, x, y, -1); } + public static void draw(@Nullable Screen.RenderingLimitingModel bounds, String msg, Screen screen, int x, int y) { draw(bounds, msg, screen, x, y, -1); } /** Draws the message to the x & y coordinates on the screen. */ - public static void draw(String msg, Screen screen, int x, int y, int whiteTint) { draw(msg, screen, x, y, whiteTint, null); } - public static void draw(String msg, Screen screen, int x, int y, int whiteTint, @Nullable ListEntry.IntRange bounds) { + public static void draw(String msg, Screen screen, int x, int y, int whiteTint) { draw(null, msg, screen, x, y, whiteTint); } + public static void draw(@Nullable Screen.RenderingLimitingModel bounds, String msg, Screen screen, int x, int y, int whiteTint) { msg = msg.toUpperCase(Localization.getSelectedLocale()); //makes all letters uppercase. for (int i = 0; i < msg.length(); i++) { // Loops through all the characters that you typed int ix = chars.indexOf(msg.charAt(i)); // The current letter in the message loop if (ix >= 0) { // If that character's position is larger than or equal to 0, then render the character on the screen. - int xx = x + i * textWidth(msg.substring(i, i+1)); - if (bounds == null || xx >= bounds.lower && xx + MinicraftImage.boxWidth <= bounds.upper) - screen.render(xx, y, ix % 32, ix / 32, 0, Renderer.spriteLinker.getSheet(SpriteType.Gui, "font"), whiteTint); + screen.render(bounds, x + i * textWidth(msg.substring(i, i+1)), y, ix % 32, ix / 32, 0, Renderer.spriteLinker.getSheet(SpriteType.Gui, "font"), whiteTint); } } } - public static void drawColor(String message, Screen screen, int x, int y) { drawColor(message, screen, x, y, null); } - public static void drawColor(String message, Screen screen, int x, int y, @Nullable ListEntry.IntRange bounds) { + public static void drawColor(@Nullable Screen.RenderingLimitingModel bounds, String message, Screen screen, int x, int y) { // Set default color message if it doesn't have initially if (message.charAt(0) != Color.COLOR_CHAR) { message = Color.WHITE_CODE + message; @@ -63,21 +61,23 @@ public static void drawColor(String message, Screen screen, int x, int y, @Nulla color = Color.WHITE_CODE; } - Font.draw(text, screen, x + leading, y, Color.get(color), bounds); + Font.draw(bounds, text, screen, x + leading, y, Color.get(color)); leading += Font.textWidth(text); } } - public static void drawBackground(String msg, Screen screen, int x, int y) { drawBackground(msg, screen, x, y, -1); } + public static void drawBackground(String msg, Screen screen, int x, int y) { drawBackground(null, msg, screen, x, y, -1); } + public static void drawBackground(@Nullable Screen.RenderingLimitingModel bounds, String msg, Screen screen, int x, int y) { drawBackground(bounds, msg, screen, x, y, -1); } - public static void drawBackground(String msg, Screen screen, int x, int y, int whiteTint) { + public static void drawBackground(String msg, Screen screen, int x, int y, int whiteTint) { drawBackground(null, msg, screen, x, y, whiteTint); } + public static void drawBackground(@Nullable Screen.RenderingLimitingModel bounds, String msg, Screen screen, int x, int y, int whiteTint) { String newMsg = msg.toUpperCase(Localization.getSelectedLocale()); for (int i = 0; i < newMsg.length(); i++) { // Renders the black boxes under the text - screen.render(x + i * textWidth(newMsg.substring(i, i+1)), y, 5, 2, 0, Renderer.spriteLinker.getSheet(SpriteType.Gui, "hud")); + screen.render(null, x + i * textWidth(newMsg.substring(i, i+1)), y, 5, 2, 0, Renderer.spriteLinker.getSheet(SpriteType.Gui, "hud")); } // Renders the text - draw(msg, screen, x, y, whiteTint); + draw(bounds, msg, screen, x, y, whiteTint); } public static int textWidth(String text) { // Filtering out coloring codes. diff --git a/src/client/java/minicraft/gfx/Rectangle.java b/src/client/java/minicraft/gfx/Rectangle.java index e1c7b38b5..18daa8ed9 100644 --- a/src/client/java/minicraft/gfx/Rectangle.java +++ b/src/client/java/minicraft/gfx/Rectangle.java @@ -3,17 +3,17 @@ import minicraft.screen.RelPos; public class Rectangle { - + public static final int CORNER_DIMS = 0; public static final int CORNERS = 1; public static final int CENTER_DIMS = 2; - + private int x, y, w, h; - + public Rectangle() {} // 0 all. public Rectangle(int x, int y, int x1, int y1, int type) { if(type < 0 || type > 2) type = 0; - + if (type != CENTER_DIMS) { // x and y are the coords of the top left corner. this.x = x; this.y = y; @@ -21,7 +21,7 @@ public Rectangle(int x, int y, int x1, int y1, int type) { this.x = x - x1/2; this.y = y - y1/2; } - + if (type != CORNERS) { // x1 and y1 are the width and height. this.w = x1; this.h = y1; @@ -30,37 +30,37 @@ public Rectangle(int x, int y, int x1, int y1, int type) { this.h = y1 - y; } } - + public Rectangle(Point p, Dimension d) { this(false, p, d); } public Rectangle(boolean isCenter, Point p, Dimension d) { this(p.x, p.y, d.width, d.height, isCenter ? CENTER_DIMS : CORNER_DIMS); } - + public Rectangle(Rectangle model) { x = model.x; y = model.y; w = model.w; h = model.h; } - + public int getLeft() { return x; } public int getRight() { return x + w; } public int getTop() { return y; } public int getBottom() { return y + h; } - + public int getWidth() { return w; } public int getHeight() { return h; } - + public Point getCenter() { return new Point(x + w/2, y + h/2); } public Dimension getSize() { return new Dimension(w, h); } - + public Point getPosition(RelPos relPos) { Point p = new Point(x, y); p.x += relPos.xIndex * w/2; p.y += relPos.yIndex * h/2; return p; } - + public boolean intersects(Rectangle other) { return !( getLeft() > other.getRight() // Left side is past the other right side || other.getLeft() > getRight() // Other left side is past the right side @@ -68,18 +68,23 @@ public boolean intersects(Rectangle other) { || other.getBottom() < getTop() // Top is below the other bottom ); } - + + public boolean contains(Point pt) { return contains(pt.x, pt.y); } + public boolean contains(int x, int y) { // inclusive + return getLeft() <= x && x <= getRight() && getTop() <= y && y <= getBottom(); + } + public void setPosition(Point p, RelPos relPos) { setPosition(p.x, p.y, relPos); } public void setPosition(int x, int y, RelPos relPos) { this.x = x - relPos.xIndex*w/2; this.y = y - relPos.yIndex*h/2; } - + public void translate(int xoff, int yoff) { x += xoff; y += yoff; } - + public void setSize(Dimension d, RelPos anchor) { setSize(d.width, d.height, anchor); } public void setSize(int width, int height, RelPos anchor) { Point p = getPosition(anchor); @@ -87,6 +92,6 @@ public void setSize(int width, int height, RelPos anchor) { this.h = height; setPosition(p, anchor); } - + public String toString() { return super.toString()+"[center=" + getCenter() + "; size=" + getSize() + "]"; } } diff --git a/src/client/java/minicraft/gfx/Screen.java b/src/client/java/minicraft/gfx/Screen.java index 8491cd62b..4f4bc2155 100644 --- a/src/client/java/minicraft/gfx/Screen.java +++ b/src/client/java/minicraft/gfx/Screen.java @@ -4,6 +4,10 @@ import minicraft.core.Updater; import minicraft.gfx.SpriteLinker.LinkedSprite; import minicraft.gfx.SpriteLinker.SpriteType; +import minicraft.screen.RelPos; +import minicraft.screen.entry.ListEntry; +import minicraft.screen.entry.SelectableStringEntry; +import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -41,36 +45,73 @@ public void clear(int color) { Arrays.fill(pixels, color); } - public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet) { render(xp, yp, xt, yt, bits, sheet, -1); } - public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint) { render(xp, yp, xt, yt, bits, sheet, whiteTint, false); } + /** Inclusive bounds for rendering */ + public static abstract class RenderingLimitingModel { + public abstract int getLeftBound(); + public abstract int getRightBound(); + public abstract int getTopBound(); + public abstract int getBottomBound(); + + public boolean contains(int x, int y) { // inclusive + return getLeftBound() <= x && x <= getRightBound() && getTopBound() <= y && y <= getBottomBound(); + } + } + + // Just use a menu for it. + public static final class ScreenLimitingModel extends RenderingLimitingModel { + public static final ScreenLimitingModel INSTANCE = new ScreenLimitingModel(); + + @Override + public int getLeftBound() { + return 0; + } + + @Override + public int getRightBound() { + return Screen.w - 1; + } + + @Override + public int getTopBound() { + return 0; + } + + @Override + public int getBottomBound() { + return Screen.h - 1; + } + } + + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet) { render(limitingModel, xp, yp, xt, yt, bits, sheet, -1); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint) { render(limitingModel, xp, yp, xt, yt, bits, sheet, whiteTint, false); } /** This method takes care of assigning the correct spritesheet to assign to the sheet variable **/ - public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint, boolean fullbright) { - render(xp, yp, xt, yt, bits, sheet, whiteTint, fullbright, 0); + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint, boolean fullbright) { + render(limitingModel, xp, yp, xt, yt, bits, sheet, whiteTint, fullbright, 0); } - public void render(int xp, int yp, LinkedSprite sprite) { render(xp, yp, sprite.getSprite()); } - public void render(int xp, int yp, Sprite sprite) { render(xp, yp, sprite, false); } - public void render(int xp, int yp, Sprite sprite, boolean fullbright) { render(xp, yp, sprite, 0, fullbright, 0); } - public void render(int xp, int yp, Sprite sprite, int mirror, boolean fullbright) { render(xp, yp, sprite, mirror, fullbright, 0); } - public void render(int xp, int yp, Sprite sprite, int mirror, boolean fullbright, int color) { + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, LinkedSprite sprite) { render(limitingModel, xp, yp, sprite.getSprite()); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite sprite) { render(limitingModel, xp, yp, sprite, false); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite sprite, boolean fullbright) { render(limitingModel, xp, yp, sprite, 0, fullbright, 0); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite sprite, int mirror, boolean fullbright) { render(limitingModel, xp, yp, sprite, mirror, fullbright, 0); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite sprite, int mirror, boolean fullbright, int color) { for (int r = 0; r < sprite.spritePixels.length; r++) { for (int c = 0; c < sprite.spritePixels[r].length; c++) { Sprite.Px px = sprite.spritePixels[r][c]; - render(xp + c * 8, yp + r * 8, px, mirror, sprite.color, fullbright, color); + render(limitingModel, xp + c * 8, yp + r * 8, px, mirror, sprite.color, fullbright, color); } } } - public void render(int xp, int yp, Sprite.Px pixel) { render(xp, yp, pixel, -1); } - public void render(int xp, int yp, Sprite.Px pixel, int whiteTint) { render(xp, yp, pixel, 0, whiteTint); } - public void render(int xp, int yp, Sprite.Px pixel, int mirror, int whiteTint) { render(xp, yp, pixel, mirror, whiteTint, false); } - public void render(int xp, int yp, Sprite.Px pixel, int mirror, int whiteTint, boolean fullbright) { render(xp, yp, pixel, mirror, whiteTint, fullbright, 0); } - public void render(int xp, int yp, Sprite.Px pixel, int mirror, int whiteTint, boolean fullbright, int color) { - render(xp, yp, pixel.x, pixel.y, pixel.mirror ^ mirror, pixel.sheet, whiteTint, fullbright, color); + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite.Px pixel) { render(limitingModel, xp, yp, pixel, -1); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite.Px pixel, int whiteTint) { render(limitingModel, xp, yp, pixel, 0, whiteTint); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite.Px pixel, int mirror, int whiteTint) { render(limitingModel, xp, yp, pixel, mirror, whiteTint, false); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite.Px pixel, int mirror, int whiteTint, boolean fullbright) { render(limitingModel, xp, yp, pixel, mirror, whiteTint, fullbright, 0); } + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, Sprite.Px pixel, int mirror, int whiteTint, boolean fullbright, int color) { + render(limitingModel, xp, yp, pixel.x, pixel.y, pixel.mirror ^ mirror, pixel.sheet, whiteTint, fullbright, color); } /** Renders an object from the sprite sheet based on screen coordinates, tile (SpriteSheet location), colors, and bits (for mirroring). I believe that xp and yp refer to the desired position of the upper-left-most pixel. */ - public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint, boolean fullbright, int color) { + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint, boolean fullbright, int color) { if (sheet == null) return; // Verifying that sheet is not null. // xp and yp are originally in level coordinates, but offset turns them to screen coordinates. @@ -82,7 +123,7 @@ public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage shee boolean mirrorY = (bits & BIT_MIRROR_Y) > 0; // Vertically. // Validation check - if (sheet == null || xt * 8 + yt * 8 * sheet.width + 7 + 7 * sheet.width >= sheet.pixels.length) { + if (xt * 8 + yt * 8 * sheet.width + 7 + 7 * sheet.width >= sheet.pixels.length) { sheet = Renderer.spriteLinker.missingSheet(SpriteType.Item); xt = 0; yt = 0; @@ -99,6 +140,7 @@ public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage shee if (y + yp < 0 || y + yp >= h) continue; // If the pixel is out of bounds, then skip the rest of the loop. for (int x = 0; x < 8; x++) { // Loops 8 times (because of the width of the tile) if (x + xp < 0 || x + xp >= w) continue; // Skip rest if out of bounds. + if (limitingModel != null && !limitingModel.contains(x + xp, y + yp)) continue; int xs = x; // Current x pixel if (mirrorX) xs = 7 - x; // Reverses the pixel for a mirroring effect diff --git a/src/client/java/minicraft/gfx/SpriteAnimation.java b/src/client/java/minicraft/gfx/SpriteAnimation.java index 3b5782676..ad5d1ce08 100644 --- a/src/client/java/minicraft/gfx/SpriteAnimation.java +++ b/src/client/java/minicraft/gfx/SpriteAnimation.java @@ -208,41 +208,41 @@ public void render(Screen screen, Level level, int x, int y) { if (u && l) { int connectiveColor = singletonWithConnective ? full.color : sparse.color; Sprite.Px connective = singletonWithConnective ? full.spritePixels[1][1] : sparse.spritePixels[1][1]; - if (ul) screen.render(x, y, connective, connectiveColor); - else if (sides == null) screen.render(x, y, full.spritePixels[1][1], full.color); - else screen.render(x, y, sides.spritePixels[0][0], 3, sides.color); + if (ul) screen.render(null, x, y, connective, connectiveColor); + else if (sides == null) screen.render(null, x, y, full.spritePixels[1][1], full.color); + else screen.render(null, x, y, sides.spritePixels[0][0], 3, sides.color); } else - screen.render(x, y, sparse.spritePixels[u ? 1 : 0][l ? 1 : 0], sparse.color); + screen.render(null, x, y, sparse.spritePixels[u ? 1 : 0][l ? 1 : 0], sparse.color); if (u && r) { int connectiveColor = singletonWithConnective ? full.color : sparse.color; Sprite.Px connective = singletonWithConnective ? full.spritePixels[1][0] : sparse.spritePixels[1][1]; - if (ur) screen.render(x + 8, y, connective, connectiveColor); - else if (sides == null) screen.render(x + 8, y, full.spritePixels[1][0], full.color); - else screen.render(x + 8, y, sides.spritePixels[0][1], 3, sides.color); + if (ur) screen.render(null, x + 8, y, connective, connectiveColor); + else if (sides == null) screen.render(null, x + 8, y, full.spritePixels[1][0], full.color); + else screen.render(null, x + 8, y, sides.spritePixels[0][1], 3, sides.color); } else - screen.render(x + 8, y, sparse.spritePixels[u ? 1 : 0][r ? 1 : 2], sparse.color); + screen.render(null, x + 8, y, sparse.spritePixels[u ? 1 : 0][r ? 1 : 2], sparse.color); if (d && l) { int connectiveColor = singletonWithConnective ? full.color : sparse.color; Sprite.Px connective = singletonWithConnective ? full.spritePixels[0][1] : sparse.spritePixels[1][1]; - if (dl) screen.render(x, y + 8, connective, connectiveColor); - else if (sides == null) screen.render(x, y + 8, full.spritePixels[0][1], full.color); - else screen.render(x, y + 8, sides.spritePixels[1][0], 3, sides.color); + if (dl) screen.render(null, x, y + 8, connective, connectiveColor); + else if (sides == null) screen.render(null, x, y + 8, full.spritePixels[0][1], full.color); + else screen.render(null, x, y + 8, sides.spritePixels[1][0], 3, sides.color); } else - screen.render(x, y + 8, sparse.spritePixels[d ? 1 : 2][l ? 1 : 0], sparse.color); + screen.render(null, x, y + 8, sparse.spritePixels[d ? 1 : 2][l ? 1 : 0], sparse.color); if (d && r) { int connectiveColor = singletonWithConnective ? full.color : sparse.color; Sprite.Px connective = singletonWithConnective ? full.spritePixels[0][0] : sparse.spritePixels[1][1]; - if (dr) screen.render(x + 8, y + 8, connective, connectiveColor); - else if (sides == null) screen.render(x + 8, y + 8, full.spritePixels[0][0], full.color); - else screen.render(x + 8, y + 8, sides.spritePixels[1][1], 3, sides.color); + if (dr) screen.render(null, x + 8, y + 8, connective, connectiveColor); + else if (sides == null) screen.render(null, x + 8, y + 8, full.spritePixels[0][0], full.color); + else screen.render(null, x + 8, y + 8, sides.spritePixels[1][1], 3, sides.color); } else - screen.render(x + 8, y + 8, sparse.spritePixels[d ? 1 : 2][r ? 1 : 2], sparse.color); + screen.render(null, x + 8, y + 8, sparse.spritePixels[d ? 1 : 2][r ? 1 : 2], sparse.color); } else - screen.render(x << 4, y << 4, animations[frame]); + screen.render(null, x << 4, y << 4, animations[frame]); // If there is animation. if (animations.length > 1) { diff --git a/src/client/java/minicraft/item/Item.java b/src/client/java/minicraft/item/Item.java index a7fed6f38..fb5b58188 100644 --- a/src/client/java/minicraft/item/Item.java +++ b/src/client/java/minicraft/item/Item.java @@ -33,8 +33,8 @@ protected Item(String name, LinkedSprite sprite) { /** Renders an item on the HUD */ public void renderHUD(Screen screen, int x, int y, int fontColor) { String dispName = getDisplayName(); - screen.render(x, y, sprite); - Font.drawBackground(dispName, screen, x + 8, y, fontColor); + screen.render(null, x, y, sprite); + Font.drawBackground(null, dispName, screen, x + 8, y, fontColor); } /** Determines what happens when the player interacts with a tile */ diff --git a/src/client/java/minicraft/level/tile/TreeTile.java b/src/client/java/minicraft/level/tile/TreeTile.java index 895ad1fd4..097b9ddb5 100644 --- a/src/client/java/minicraft/level/tile/TreeTile.java +++ b/src/client/java/minicraft/level/tile/TreeTile.java @@ -46,27 +46,27 @@ public void render(Screen screen, Level level, int x, int y) { Sprite spriteFull = treeSpriteFull.getSprite(); if (u && ul && l) { - screen.render(x * 16 + 0, y * 16 + 0, spriteFull.spritePixels[0][1]); + screen.render(null, x * 16 + 0, y * 16 + 0, spriteFull.spritePixels[0][1]); } else { - screen.render(x * 16 + 0, y * 16 + 0, sprite.spritePixels[0][0]); + screen.render(null, x * 16 + 0, y * 16 + 0, sprite.spritePixels[0][0]); } if (u && ur && r) { - screen.render(x * 16 + 8, y * 16 + 0, spriteFull.spritePixels[0][0]); + screen.render(null, x * 16 + 8, y * 16 + 0, spriteFull.spritePixels[0][0]); } else { - screen.render(x * 16 + 8, y * 16 + 0, sprite.spritePixels[0][1]); + screen.render(null, x * 16 + 8, y * 16 + 0, sprite.spritePixels[0][1]); } if (d && dl && l) { - screen.render(x * 16 + 0, y * 16 + 8, spriteFull.spritePixels[1][1]); + screen.render(null, x * 16 + 0, y * 16 + 8, spriteFull.spritePixels[1][1]); } else { - screen.render(x * 16 + 0, y * 16 + 8, sprite.spritePixels[1][0]); + screen.render(null, x * 16 + 0, y * 16 + 8, sprite.spritePixels[1][0]); } if (d && dr && r) { - screen.render(x * 16 + 8, y * 16 + 8, spriteFull.spritePixels[1][0]); + screen.render(null, x * 16 + 8, y * 16 + 8, spriteFull.spritePixels[1][0]); } else { - screen.render(x * 16 + 8, y * 16 + 8, sprite.spritePixels[1][1]); + screen.render(null, x * 16 + 8, y * 16 + 8, sprite.spritePixels[1][1]); } } diff --git a/src/client/java/minicraft/level/tile/farming/PotatoTile.java b/src/client/java/minicraft/level/tile/farming/PotatoTile.java index a4c96fd49..485314b64 100644 --- a/src/client/java/minicraft/level/tile/farming/PotatoTile.java +++ b/src/client/java/minicraft/level/tile/farming/PotatoTile.java @@ -34,7 +34,7 @@ public void render(Screen screen, Level level, int x, int y) { int icon = age / (maxAge / 5); Tiles.get("Farmland").render(screen, level, x, y); - screen.render(x * 16, y * 16, spritStages[icon]); + screen.render(null, x * 16, y * 16, spritStages[icon]); } @Override diff --git a/src/client/java/minicraft/level/tile/farming/WheatTile.java b/src/client/java/minicraft/level/tile/farming/WheatTile.java index f5f3c6cfb..1a4c749d0 100644 --- a/src/client/java/minicraft/level/tile/farming/WheatTile.java +++ b/src/client/java/minicraft/level/tile/farming/WheatTile.java @@ -26,6 +26,6 @@ public void render(Screen screen, Level level, int x, int y) { int icon = age / (maxAge / 5); Tiles.get("Farmland").render(screen, level, x, y); - screen.render(x * 16, y * 16, spritStages[icon]); + screen.render(null, x * 16, y * 16, spritStages[icon]); } } diff --git a/src/client/java/minicraft/screen/AchievementsDisplay.java b/src/client/java/minicraft/screen/AchievementsDisplay.java index aceab7e75..58e09d62c 100644 --- a/src/client/java/minicraft/screen/AchievementsDisplay.java +++ b/src/client/java/minicraft/screen/AchievementsDisplay.java @@ -71,7 +71,7 @@ public class AchievementsDisplay extends Display { public AchievementsDisplay() { super(true, true, - new Menu.Builder(false, 2, RelPos.CENTER, getAchievemensAsEntries()).setSize(48, 48).createMenu(), + new Menu.Builder(false, 2, RelPos.CENTER, getAchievemensAsEntries()).setSize(Screen.w, 48).createMenu(), new Menu.Builder(false, 2, RelPos.BOTTOM, new StringEntry("")).setSize(200, 32).setPositioning(new Point(Screen.w / 2, Screen.h / 2 + 32), RelPos.BOTTOM).createMenu()); } diff --git a/src/client/java/minicraft/screen/ControlsDisplay.java b/src/client/java/minicraft/screen/ControlsDisplay.java index f50ecc8bf..c91f66468 100644 --- a/src/client/java/minicraft/screen/ControlsDisplay.java +++ b/src/client/java/minicraft/screen/ControlsDisplay.java @@ -10,10 +10,8 @@ import minicraft.gfx.Screen; import minicraft.screen.entry.ListEntry; import minicraft.screen.entry.SelectableStringEntry; -import minicraft.screen.entry.StringEntry; import java.util.ArrayList; -import java.util.Arrays; public class ControlsDisplay extends Display { private ListEntry[] keyControls; @@ -41,8 +39,7 @@ private void initKeyControls() { ArrayList entries = new ArrayList<>(23); for (int i = 0; i < 23; i++) { SelectableStringEntry entry = new SelectableStringEntry(String.format("minicraft.displays.controls.display.keyboard.%02d", i)); - entry.setScrollingTextRenderTicker( - new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2), MinicraftImage.boxWidth * 2); + entry.setScrollerScrollingTicker(); entries.add(entry); } keyControls = entries.toArray(new ListEntry[0]); @@ -52,8 +49,7 @@ private void initControllerControls() { ArrayList entries = new ArrayList<>(16); for (int i = 0; i < 16; i++) { SelectableStringEntry entry = new SelectableStringEntry(String.format("minicraft.displays.controls.display.controller.%02d", i)); - entry.setScrollingTextRenderTicker( - new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2), MinicraftImage.boxWidth * 2); + entry.setScrollerScrollingTicker(); entries.add(entry); } controllerControls = entries.toArray(new ListEntry[0]); diff --git a/src/client/java/minicraft/screen/LevelTransitionDisplay.java b/src/client/java/minicraft/screen/LevelTransitionDisplay.java index 7681c5141..887f9079d 100644 --- a/src/client/java/minicraft/screen/LevelTransitionDisplay.java +++ b/src/client/java/minicraft/screen/LevelTransitionDisplay.java @@ -33,8 +33,8 @@ public void render(Screen screen) { for (int y = 0; y < 150; y++) { // Loop however many times depending on the height (It's divided by 3 because the pixels are scaled up by 3) int dd = (y + x % 2 * 2 + x / 3) - time*2; // Used as part of the positioning. if (dd < 0 && dd > -30) { - if (dir > 0) screen.render(x * 8, y * 8, 5, 2, 0, hudSheet.getSheet()); // If the direction is upwards then render the squares going up - else screen.render(x * 8, Screen.h - y * 8 - 8, 5, 2, 0, hudSheet.getSheet()); // If the direction is negative, then the squares will go down. + if (dir > 0) screen.render(null, x * 8, y * 8, 5, 2, 0, hudSheet.getSheet()); // If the direction is upwards then render the squares going up + else screen.render(null, x * 8, Screen.h - y * 8 - 8, 5, 2, 0, hudSheet.getSheet()); // If the direction is negative, then the squares will go down. } } } diff --git a/src/client/java/minicraft/screen/Menu.java b/src/client/java/minicraft/screen/Menu.java index a27d8ef9f..dae9c6cf4 100644 --- a/src/client/java/minicraft/screen/Menu.java +++ b/src/client/java/minicraft/screen/Menu.java @@ -15,6 +15,7 @@ import minicraft.gfx.SpriteLinker.SpriteType; import minicraft.screen.entry.BlankEntry; import minicraft.screen.entry.ListEntry; +import minicraft.screen.entry.SelectableStringEntry; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,12 +30,12 @@ public class Menu { private static final int LIMIT_TYPING_SEARCHER = 22; @NotNull - private final ArrayList entries = new ArrayList<>(); + private final ArrayList entries = new ArrayList<>(); private int spacing = 0; private Rectangle bounds = null; private Rectangle entryBounds = null; - private Rectangle renderingBounds = null; + private Rectangle entryRenderingBounds = null; private RelPos entryPos = RelPos.CENTER; // the x part of this is re-applied per entry, while the y part is calculated once using the cumulative height of all entries and spacing. private String title = ""; @@ -43,6 +44,7 @@ public class Menu { private boolean drawVertically = false; private boolean hasFrame; + private boolean renderOutOfFrame = false; private boolean selectable = false; boolean shouldRender = true; @@ -73,12 +75,13 @@ public class Menu { private Menu() {} protected Menu(Menu m) { - entries.addAll(m.entries); spacing = m.spacing; bounds = m.bounds == null ? null : new Rectangle(m.bounds); entryBounds = m.entryBounds == null ? null : new Rectangle(m.entryBounds); - renderingBounds = m.renderingBounds; + entryRenderingBounds = m.entryRenderingBounds == null ? null : new Rectangle(m.entryRenderingBounds); entryPos = m.entryPos; + // Requires parameters before this. + setEntries(m.entries.stream().map(e -> e.delegate).toArray(ListEntry[]::new)); title = m.title; titleColor = m.titleColor; titleLoc = m.titleLoc; @@ -92,6 +95,7 @@ protected Menu(Menu m) { selection = m.selection; dispSelection = m.dispSelection; offset = m.offset; + renderOutOfFrame = m.renderOutOfFrame; useSearcherBar = m.useSearcherBar; selectionSearcher = 0; @@ -112,13 +116,13 @@ public void init() { selection = Math.min(selection, entries.size()-1); selection = Math.max(0, selection); - if(!entries.get(selection).isSelectable()) { + if(!entries.get(selection).delegate.isSelectable()) { int prevSel = selection; do { selection++; if (selection < 0) selection = entries.size() - 1; selection = selection % entries.size(); - } while (!entries.get(selection).isSelectable() && selection != prevSel); + } while (!entries.get(selection).delegate.isSelectable() && selection != prevSel); } dispSelection = selection; @@ -141,16 +145,17 @@ void setSelection(int idx) { int getSelection() { return selection; } int getDispSelection() { return dispSelection; } - ListEntry[] getEntries() { return entries.toArray(new ListEntry[0]); } + ListEntry[] getEntries() { return entries.stream().map(a -> a.delegate).toArray(ListEntry[]::new); } protected void setEntries(ListEntry[] entries) { this.entries.clear(); - this.entries.addAll(0, Arrays.asList(entries)); + Arrays.stream(entries) + .forEach(entry -> this.entries.add(new MenuListEntry(entry, entryPos))); } protected void setEntries(List entries) { this.entries.clear(); - this.entries.addAll(entries); + entries.forEach(entry -> this.entries.add(new MenuListEntry(entry, entryPos))); } - @Nullable ListEntry getCurEntry() { return entries.size() == 0 ? null : entries.get(selection); } + @Nullable ListEntry getCurEntry() { return entries.size() == 0 ? null : entries.get(selection).delegate; } int getNumOptions() { return entries.size(); } Rectangle getBounds() { return new Rectangle(bounds); } @@ -165,16 +170,24 @@ protected void setEntries(List entries) { void translate(int xoff, int yoff) { bounds.translate(xoff, yoff); entryBounds.translate(xoff, yoff); + entryRenderingBounds.translate(xoff, yoff); titleLoc.translate(xoff, yoff); } public void tick(InputHandler input) { - if(!selectable || entries.size() == 0) return; + if (!selectable) { + if (!entries.isEmpty()) entries.get(selection).tick(input); + return; + } else if (entries.size() == 0) { + return; + } int prevSel = selection; if (input.getKey("ALT").down) { - if (input.inputPressed("cursor-left")) entries.get(selection).displaceRight(entryBounds.getWidth()); - if (input.inputPressed("cursor-right")) entries.get(selection).displaceLeft(entryBounds.getWidth()); + if (input.inputPressed("cursor-left")) + entries.get(selection).translateX(MinicraftImage.boxWidth, true); + if (input.inputPressed("cursor-right")) + entries.get(selection).translateX(-MinicraftImage.boxWidth, true); } else { if (input.inputPressed("cursor-up")) selection--; if (input.inputPressed("cursor-down")) selection++; @@ -220,10 +233,10 @@ public void tick(InputHandler input) { listSearcher.clear(); listPositionSearcher = 0; - Iterator entryIt = entries.iterator(); + Iterator entryIt = entries.iterator(); boolean shouldSelect = true; for (int i = 0; entryIt.hasNext(); i++) { - ListEntry entry = entryIt.next(); + ListEntry entry = entryIt.next().delegate; String stringEntry = entry.toString().toLowerCase(Locale.ENGLISH); String typingString = typingSearcher.toLowerCase(Locale.ENGLISH); @@ -261,7 +274,7 @@ public void tick(InputHandler input) { selection += delta; if (selection < 0) selection = entries.size() - 1; selection = selection % entries.size(); - } while(!entries.get(selection).isSelectable() && selection != prevSel); + } while(!entries.get(selection).delegate.isSelectable() && selection != prevSel); // update offset and selection displayed dispSelection += selection - prevSel; @@ -304,12 +317,12 @@ public void render(Screen screen) { if(title.length() > 0) { if (drawVertically) { for (int i = 0; i < title.length(); i++) { - if (hasFrame) screen.render(titleLoc.x, titleLoc.y + i * Font.textHeight(), 3, 6, 0, hudSheet.getSheet()); + if (hasFrame) screen.render(null, titleLoc.x, titleLoc.y + i * Font.textHeight(), 3, 6, 0, hudSheet.getSheet()); Font.draw(title.substring(i, i + 1), screen, titleLoc.x, titleLoc.y + i * Font.textHeight(), titleColor); } } else { for (int i = 0; i < title.length(); i++) { - if (hasFrame) screen.render(titleLoc.x + i * Font.textWidth(" "), titleLoc.y, 3, 6, 0, hudSheet.getSheet()); + if (hasFrame) screen.render(null, titleLoc.x + i * Font.textWidth(" "), titleLoc.y, 3, 6, 0, hudSheet.getSheet()); Font.draw(title.substring(i, i + 1), screen, titleLoc.x + i * Font.textWidth(" "), titleLoc.y, titleColor); } } @@ -328,7 +341,7 @@ public void render(Screen screen) { for (int i = 0; i < typingSearcher.length() + 4; i++) { if (hasFrame) { - screen.render(xSearcherBar + spaceWidth * i - leading, titleLoc.y - 8, 3, 6, 0, hudSheet.getSheet()); + screen.render(null, xSearcherBar + spaceWidth * i - leading, titleLoc.y - 8, 3, 6, 0, hudSheet.getSheet()); } Font.draw("> " + typingSearcher + " <", screen, xSearcherBar - leading, titleLoc.y - 8, typingSearcher.length() < Menu.LIMIT_TYPING_SEARCHER ? Color.YELLOW : Color.RED); @@ -343,29 +356,16 @@ public void render(Screen screen) { int extra = diff*(ListEntry.getHeight() + spacing) / 2; y += extra; } - ListEntry.IntRange renderingXBounds = new ListEntry.IntRange(renderingBounds.getLeft(), renderingBounds.getRight()); for(int i = offset; i < (wrap ? offset + displayLength : Math.min(offset + displayLength, entries.size())); i++) { if(special && i-offset >= entries.size()) break; int idx = i % entries.size(); - ListEntry entry = entries.get(idx); - - if(!(entry instanceof BlankEntry)) { - int entryWidth = entry.getWidth(); - Point pos = entryPos.positionRect(new Dimension(entryWidth, ListEntry.getHeight()), new Rectangle(entryBounds.getLeft(), y, entryBounds.getWidth(), ListEntry.getHeight(), Rectangle.CORNER_DIMS)); - boolean selected = idx == selection; - int xDisplacement = selected ? entry.getXDisplacement() : 0; - boolean hideOverflow = entry.hideWhenOverflow() && entry.isSelectable(); + MenuListEntry entry = entries.get(idx); + if(!(entry.delegate instanceof BlankEntry) && entry.delegate.isVisible()) { if (searcherBarActive && useSearcherBar) { - entry.render(screen, pos.x + xDisplacement, pos.y, selected, hideOverflow ? renderingXBounds : null, typingSearcher, Color.YELLOW); + entry.render(screen, y, idx == selection, typingSearcher, Color.YELLOW); } else { - entry.render(screen, pos.x + xDisplacement, pos.y, selected, hideOverflow ? renderingXBounds : null); - } - if (selected && entry.isSelectable()) { - // draw the arrows - boolean hiddenOverflow = hideOverflow && entryWidth > entryBounds.getWidth(); - Font.draw("> ", screen, hiddenOverflow ? entryBounds.getLeft() - Font.textWidth("> ") : pos.x + xDisplacement - Font.textWidth("> "), y, ListEntry.COL_SLCT); - Font.draw(" <", screen, hiddenOverflow ? entryBounds.getRight() : pos.x + xDisplacement + entryWidth, y, ListEntry.COL_SLCT); + entry.render(screen, y, idx == selection); } } @@ -376,7 +376,7 @@ public void render(Screen screen) { void updateSelectedEntry(ListEntry newEntry) { updateEntry(selection, newEntry); } void updateEntry(int idx, ListEntry newEntry) { if(idx >= 0 && idx < entries.size()) - entries.set(idx, newEntry); + entries.set(idx, new MenuListEntry(newEntry, entryPos)); } public void removeSelectedEntry() { @@ -408,7 +408,7 @@ private void renderFrame(Screen screen) { int spriteoffset = (xend && yend ? 0 : (yend ? 1 : (xend ? 2 : 3))); // determines which sprite to use int mirrors = ( x == right ? 1 : 0 ) + ( y == bottom ? 2 : 0 ); // gets mirroring - screen.render(x, y, spriteoffset, 6, mirrors, hudSheet.getSheet()); + screen.render(null, x, y, spriteoffset, 6, mirrors, hudSheet.getSheet()); if(x < right && x + MinicraftImage.boxWidth > right) x = right - MinicraftImage.boxWidth; @@ -419,6 +419,182 @@ private void renderFrame(Screen screen) { } } + /** Acts as an internal wrapping decorator of a {@link ListEntry} for the {@link Menu} environment. */ + private class MenuListEntry { + private class MenuEntryLimitingModel extends Screen.RenderingLimitingModel { + @Override + public int getLeftBound() { + return getEntryBounds().getLeft(); + } + + @Override + public int getRightBound() { + return getEntryBounds().getRight() - 1; // As this model is inclusive. + } + + @Override + public int getTopBound() { + return getEntryBounds().getTop(); + } + + @Override + public int getBottomBound() { + return getEntryBounds().getBottom() - 1; // As this model is inclusive. + } + } + + private class MenuEntryXAccessor implements SelectableStringEntry.EntryXAccessor { + /* Most of the methods here can be obtained by algebra. */ + + @Override + public int getWidth() { + return delegate.getWidth(); + } + + @Override + public int getX(RelPos anchor) { + return xPos + (menuAnchor.xIndex - anchor.xIndex) * getEntryBounds().getWidth() / 2 + + (anchor.xIndex - entryAnchor.xIndex) * delegate.getWidth() / 2; + } + + @Override + public void setX(RelPos anchor, int x) { + xPos = x + (anchor.xIndex - menuAnchor.xIndex) * getEntryBounds().getWidth() / 2 + + (entryAnchor.xIndex - anchor.xIndex) * delegate.getWidth() / 2; + } + + @Override + public void translateX(int displacement) { + xPos += displacement; + } + + @Override + public void setAnchors(RelPos anchor) { + changeRelativeEntryAnchor(anchor); + changeRelativeMenuAnchor(anchor); + } + + @Override + public int getLeftBound(RelPos anchor) { + return anchor.xIndex * (delegate.getWidth() - getEntryBounds().getWidth()) / 2; + } + + @Override + public int getRightBound(RelPos anchor) { + return (2 - anchor.xIndex) * (getEntryBounds().getWidth() - delegate.getWidth()) / 2; + } + } + + public final MenuEntryLimitingModel limitingModel = new MenuEntryLimitingModel(); + public final MenuEntryXAccessor accessor = new MenuEntryXAccessor(); + public final ListEntry delegate; + + /** + * A reference anchor, which is the relative position of the container menu
+ * Also, it is the relative position of the entry.
+ * Acceptable values: {@code LEFT}, {@code CENTER}, {@code RIGHT} + */ + private @NotNull RelPos menuAnchor; + /** + * A reference anchor of the entry
+ * This is usually the same as {@link #menuAnchor}. + */ + private @NotNull RelPos entryAnchor; + /** The x-position of the entry at its anchor {@link #entryAnchor} relative to the menu anchor {@link #menuAnchor} */ + private int xPos = 0; + + public MenuListEntry(ListEntry delegate, @NotNull RelPos anchor) { + this.delegate = delegate; + anchor = RelPos.getPos(anchor.xIndex, 1); // Confirms that this meets the requirement. + this.menuAnchor = anchor; + this.entryAnchor = anchor; + } + + private Rectangle getEntryBounds() { + return delegate.isSelectable() ? entryBounds : entryRenderingBounds; + } + + public void translateX(int displacement, boolean limit) { + xPos += displacement; + if (limit) { + int w = delegate.getWidth(); + int minX; + int maxX; + if (w <= getEntryBounds().getWidth()) { + minX = (entryAnchor.xIndex * w - menuAnchor.xIndex * getEntryBounds().getWidth()) / 2; + maxX = ((2 - menuAnchor.xIndex) * getEntryBounds().getWidth() - (2 - entryAnchor.xIndex) * w) / 2; + } else { + minX = ((2 - menuAnchor.xIndex) * getEntryBounds().getWidth() - (2 - entryAnchor.xIndex) * w) / 2; + maxX = (entryAnchor.xIndex * w - menuAnchor.xIndex * getEntryBounds().getWidth()) / 2; + } + if (xPos < minX) xPos = minX; + if (xPos > maxX) xPos = maxX; + } + } + + public void resetRelativeAnchorsSynced(RelPos newAnchor) { + entryAnchor = menuAnchor = newAnchor; + xPos = 0; + } + public void moveRelativeMenuAnchor(RelPos newAnchor) { + menuAnchor = newAnchor; + xPos = 0; + } + public void moveRelativeEntryAnchor(RelPos newAnchor) { + entryAnchor = newAnchor; + xPos = 0; + } + public void changeRelativeMenuAnchor(RelPos newAnchor) { + xPos += (menuAnchor.xIndex - newAnchor.xIndex) * getEntryBounds().getWidth() / 2; + menuAnchor = newAnchor; + } + public void changeRelativeEntryAnchor(RelPos newAnchor) { + xPos += (newAnchor.xIndex - entryAnchor.xIndex) * delegate.getWidth() / 2; + entryAnchor = newAnchor; + } + + public void tick(InputHandler input) { + delegate.hook(() -> { + xPos = 0; // Resets position + int diff = xPos + (menuAnchor.xIndex - 2) * getEntryBounds().getWidth() / 2 + + (2 - entryAnchor.xIndex) * delegate.getWidth() / 2; + if (diff > 0) translateX(-diff, false); // Shifts if and only if the entry exceeds the area. + }); + delegate.tickScrollingTicker(accessor); + delegate.tick(input); + } + + private void renderCursor(Screen screen, int x, int y, int entryWidth, boolean tickerSet) { + if (tickerSet) { + entryWidth = Math.min(entryWidth, getEntryBounds().getWidth()); + x = getEntryBounds().getLeft(); + } + + Font.draw(null, "> ", screen, x - 2 * MinicraftImage.boxWidth, y, ListEntry.COL_SLCT); + Font.draw(null, " <", screen, x + entryWidth, y, ListEntry.COL_SLCT); + } + + public void render(Screen screen, int y, boolean selected) { + int w = delegate.getWidth(); // Reduce calculation + int x = getRenderX(w); // Reduce calculation + delegate.render(screen, renderOutOfFrame ? null : limitingModel, x, y, selected); + if (selected && delegate.isSelectable()) + renderCursor(screen, x, y, w, delegate.isScrollingTickerSet()); + } + + public void render(Screen screen, int y, boolean selected, String contain, int containColor) { + int w = delegate.getWidth(); // Reduce calculation + int x = getRenderX(w); // Reduce calculation + delegate.render(screen, renderOutOfFrame ? null : limitingModel, x, y, selected, contain, containColor); + if (selected && delegate.isSelectable()) + renderCursor(screen, x, y, w, delegate.isScrollingTickerSet()); + } + + private int getRenderX(int entryWidth) { + return getEntryBounds().getLeft() + xPos - entryAnchor.xIndex * entryWidth / 2 + + menuAnchor.xIndex * getEntryBounds().getWidth() / 2; + } + } // This needs to be in the Menu class, to have access to the private constructor and fields. @@ -438,8 +614,6 @@ public static class Builder { @NotNull private Point anchor = center; @NotNull private RelPos menuPos = RelPos.CENTER; private Dimension menuSize = null; - private Rectangle limitingBounds = null; - private Rectangle renderingBounds = null; private boolean removeEntryPeaks = false; private boolean searcherBar; @@ -447,16 +621,15 @@ public static class Builder { public Builder(boolean hasFrame, int entrySpacing, RelPos entryPos, ListEntry... entries) { this(hasFrame, entrySpacing, entryPos, Arrays.asList(entries)); } public Builder(boolean hasFrame, int entrySpacing, RelPos entryPos, List entries) { menu = new Menu(); - setEntries(entries); menu.hasFrame = hasFrame; menu.spacing = entrySpacing; menu.entryPos = entryPos; + setEntries(entries); } public Builder setEntries(ListEntry... entries) { return setEntries(Arrays.asList(entries)); } public Builder setEntries(List entries) { - menu.entries.clear(); - menu.entries.addAll(entries); + menu.setEntries(entries); return this; } @@ -477,32 +650,11 @@ public Builder setBounds(Rectangle rect) { public Builder setDisplayLength(int numEntries) { menu.displayLength = numEntries; return this; } - /** This restricts the maximum bounds this menu could be. */ - public Builder setMaxBounds(Rectangle rect) { - limitingBounds = rect; - return this; - } - - public Builder setMaxBoundsAsMaxMenuBounds() { - limitingBounds = new Rectangle(0, 0, Screen.w, Screen.h, Rectangle.CORNERS); - return this; - } - public Builder setRemoveEntryPeaks(boolean removeEntryPeaks) { this.removeEntryPeaks = removeEntryPeaks; return this; } - public Builder setRenderingBounds(Rectangle rect) { - renderingBounds = rect; - return this; - } - - public Builder setMaxBoundsAsRenderingBounds() { - renderingBounds = new Rectangle(0, 0, Screen.w, Screen.h, Rectangle.CORNERS); - return this; - } - public Builder setTitlePos(RelPos rp) { titlePos = (rp == null ? RelPos.TOP : rp); return this; } public Builder setTitle(String title) { menu.title = title; return this; } @@ -563,8 +715,8 @@ private Menu createMenu(Builder b) { // set default selectability if(!setSelectable) { - for(ListEntry entry: menu.entries) { - menu.selectable = menu.selectable || entry.isSelectable(); + for(MenuListEntry entry: menu.entries) { + menu.selectable = menu.selectable || entry.delegate.isSelectable(); if(menu.selectable) break; } @@ -632,7 +784,7 @@ else if (c.xIndex == 2) // must be center right // First write into an array to more easily find peaks. int[] entryWidths = new int[menu.entries.size()]; for (int i = 0; i < menu.entries.size(); ++i) { - entryWidths[i] = menu.entries.get(i).getWidth(); + entryWidths[i] = menu.entries.get(i).delegate.getWidth(); } // Reference: https://www.geeksforgeeks.org/print-all-the-peaks-and-troughs-in-an-array-of-integers/ @@ -644,7 +796,7 @@ else if (c.xIndex == 2) // must be center right peaks.add(entryWidths[i]); } else { int entryWidth = entryWidths[i]; - if (menu.isSelectable() && !menu.entries.get(i).isSelectable()) + if (menu.isSelectable() && !menu.entries.get(i).delegate.isSelectable()) entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); width = Math.max(width, entryWidth); handled = true; @@ -661,14 +813,15 @@ else if (c.xIndex == 2) // must be center right } } } else { - for(ListEntry entry: menu.entries) { - int entryWidth = entry.getWidth(); - if(menu.isSelectable() && !entry.isSelectable()) + for(MenuListEntry entry: menu.entries) { + int entryWidth = entry.delegate.getWidth(); + if(menu.isSelectable() && !entry.delegate.isSelectable()) entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); width = Math.max(width, entryWidth); } } + if (!menu.hasFrame && menu.entryPos.xIndex == 1) width = Screen.w; // Reduces troubles simply :) if(menu.displayLength > 0) { // has been set; use to determine entry bounds int height = (ListEntry.getHeight() + menu.spacing) * menu.displayLength - menu.spacing; @@ -707,23 +860,18 @@ else if(menuPos.yIndex == 2) // based on the menu centering, and the anchor, determine the upper-left point from which to draw the menu. menu.bounds = menuPos.positionRect(menuSize, anchor, new Rectangle()); // reset to a value that is actually useful to the menu - if (limitingBounds != null) { - // Exceeded areas are cut off from the bounds. - if (!limitingBounds.intersects(menu.bounds)) - throw new IndexOutOfBoundsException("limitingBounds does not intersect bounds"); - int x = Math.max(limitingBounds.getLeft(), menu.bounds.getLeft()); // Get the rightmost left bound - int y = Math.max(limitingBounds.getTop(), menu.bounds.getTop()); // Get the bottommost top bound - int x1 = Math.min(limitingBounds.getBottom(), menu.bounds.getBottom()); // Get the topmost bottom bound - int y1 = Math.min(limitingBounds.getRight(), menu.bounds.getRight()); // Get the leftmost right bount - menu.bounds = new Rectangle(x, y, x1, y1, Rectangle.CORNERS); // Reset to the bounds after cut off the exceeded bounds + menu.entryBounds = border.subtractFrom(menu.bounds); + + if (menu.isSelectable()) { + // remove spacing for selection cursors + border.left -= MinicraftImage.boxWidth * 2; + border.right -= MinicraftImage.boxWidth * 2; } - menu.entryBounds = border.subtractFrom(menu.bounds); + menu.entryRenderingBounds = border.subtractFrom(menu.bounds); menu.titleLoc = titlePos.positionRect(titleDim, menu.bounds); - menu.renderingBounds = renderingBounds == null ? menu.entryBounds : renderingBounds; - if(titlePos.xIndex == 0 && titlePos.yIndex != 1) menu.titleLoc.x += MinicraftImage.boxWidth; if(titlePos.xIndex == 2 && titlePos.yIndex != 1) @@ -745,6 +893,9 @@ else if(menuPos.yIndex == 2) menu.useSearcherBar = searcherBar; + if (!menu.hasFrame && removeEntryPeaks) + menu.renderOutOfFrame = true; // Peaks are usually hidden, so this is assumed to be true. + // done setting defaults/values; return the new menu menu.init(); // any setup the menu does by itself right before being finished. @@ -753,7 +904,7 @@ else if(menuPos.yIndex == 2) // returns a new Builder instance, that can be further modified to create another menu. public Builder copy() { - Builder b = new Builder(menu.hasFrame, menu.spacing, menu.entryPos, menu.entries); + Builder b = new Builder(menu.hasFrame, menu.spacing, menu.entryPos); b.menu = new Menu(menu); @@ -767,8 +918,6 @@ public Builder copy() { b.setTitleColor = setTitleColor; b.titleCol = titleCol; b.searcherBar = searcherBar; - b.limitingBounds = limitingBounds; - b.renderingBounds = renderingBounds; b.removeEntryPeaks = removeEntryPeaks; return b; diff --git a/src/client/java/minicraft/screen/PopupDisplay.java b/src/client/java/minicraft/screen/PopupDisplay.java index 83bd9e20b..13b519c8f 100644 --- a/src/client/java/minicraft/screen/PopupDisplay.java +++ b/src/client/java/minicraft/screen/PopupDisplay.java @@ -51,21 +51,12 @@ public PopupDisplay(@Nullable PopupConfig config, boolean clearScreen, boolean m for (ListEntry entry : entries) { if (entry instanceof InputEntry) { ((InputEntry) entry).setChangeListener(v -> update()); - ((InputEntry) entry).setRenderingBounds(new ListEntry.IntRange(MinicraftImage.boxWidth * 3, Screen.w - MinicraftImage.boxWidth * 3)) - .setEntryPos(RelPos.CENTER) - .setBounds(new ListEntry.IntRange(menuBounds.getLeft() + MinicraftImage.boxWidth * 3, menuBounds.getRight() - MinicraftImage.boxWidth * 3)); } } } private void update() { menus[onScreenKeyboardMenu == null ? 0 : 1] = builder.createMenu(); - Rectangle menuBounds = menus[onScreenKeyboardMenu == null ? 0 : 1].getBounds(); - for (ListEntry entry : menus[onScreenKeyboardMenu == null ? 0 : 1].getEntries()) { - if (entry instanceof InputEntry) { - ((InputEntry) entry).setBounds(new ListEntry.IntRange(menuBounds.getLeft() + MinicraftImage.boxWidth * 3, menuBounds.getRight() - MinicraftImage.boxWidth * 3)); - } - } } OnScreenKeyboardMenu onScreenKeyboardMenu; diff --git a/src/client/java/minicraft/screen/QuestsDisplay.java b/src/client/java/minicraft/screen/QuestsDisplay.java index c871b79da..03b5a047d 100644 --- a/src/client/java/minicraft/screen/QuestsDisplay.java +++ b/src/client/java/minicraft/screen/QuestsDisplay.java @@ -301,7 +301,7 @@ private static class SeriesQuestViewerDisplay extends Display { private final int[] rasterPixels; private final Screen simulatedRasterScreen = new Screen() { @Override - public void render(int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint, boolean fullbright, int color) { + public void render(RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int bits, MinicraftImage sheet, int whiteTint, boolean fullbright, int color) { if (sheet == null) return; // Verifying that sheet is not null. // Ignoring mirror. // Validation check diff --git a/src/client/java/minicraft/screen/ResourcePackDisplay.java b/src/client/java/minicraft/screen/ResourcePackDisplay.java index 48a818b12..7046b2bae 100644 --- a/src/client/java/minicraft/screen/ResourcePackDisplay.java +++ b/src/client/java/minicraft/screen/ResourcePackDisplay.java @@ -190,7 +190,8 @@ public ResourcePackDisplay() { menus[1].translate(menus[0].getBounds().getRight() - menus[1].getBounds().getLeft() + padding, 0); fileWatcher = new WatcherThread(); - helpPositionTextEntry.setAlternatingScrollingTextRenderTicker(new ListEntry.IntRange(0, Screen.w), (Screen.w - helpPositionTextEntry.getWidth()) / 2); + ((StringEntry) Objects.requireNonNull(helpPositionTextEntryMenu.getCurEntry())) + .setExceedingAlternatingScrollingTicker(); } @Override @@ -323,7 +324,7 @@ public void onExit() { @Override public void tick(InputHandler input) { - helpPositionTextEntry.tick(input); // For rendering purpose + helpPositionTextEntryMenu.tick(input); // For rendering purpose // Overrides the default tick handler. if (input.getKey("right").clicked) { // Move cursor to the second list. @@ -383,7 +384,10 @@ public void tick(InputHandler input) { super.tick(input); } - private final StringEntry helpPositionTextEntry = new StringEntry("minicraft.displays.resource_packs.display.help.position", Color.DARK_GRAY); + private final Menu helpPositionTextEntryMenu = new Menu.Builder(false, 0, RelPos.CENTER, + new StringEntry("minicraft.displays.resource_packs.display.help.position", Color.DARK_GRAY)) + .setPositioning(new Point(Screen.w / 2, Screen.h), RelPos.TOP) + .createMenu(); @Override public void render(Screen screen) { @@ -396,7 +400,7 @@ public void render(Screen screen) { if (Game.input.anyControllerConnected()) Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.keyboard_needed"), screen, Screen.h - 33, Color.DARK_GRAY); Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.move", Game.input.getMapping("cursor-down"), Game.input.getMapping("cursor-up")), screen, Screen.h - 25, Color.DARK_GRAY); Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.select", Game.input.getMapping("SELECT")), screen, Screen.h - 17, Color.DARK_GRAY); - helpPositionTextEntry.render(screen, (Screen.w - helpPositionTextEntry.getWidth()) / 2 + helpPositionTextEntry.getXDisplacement(), Screen.h - 9, false, null); + helpPositionTextEntryMenu.render(screen); ArrayList packs = selection == 0 ? resourcePacks : loadedPacks; if (packs.size() > 0) { // If there is any pack that can be selected. @@ -410,7 +414,7 @@ public void render(Screen screen) { for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { // Resource pack logo - screen.render(xo + x * 8, yo + y * 8, x, y, 0, logo); + screen.render(null, xo + x * 8, yo + y * 8, x, y, 0, logo); } } } diff --git a/src/client/java/minicraft/screen/SkinDisplay.java b/src/client/java/minicraft/screen/SkinDisplay.java index 40896cd5d..e2c07dca9 100644 --- a/src/client/java/minicraft/screen/SkinDisplay.java +++ b/src/client/java/minicraft/screen/SkinDisplay.java @@ -262,7 +262,7 @@ public void render(Screen screen) { // Render preview of skin. LinkedSprite sprite = new ArrayList<>(skins.values()).get(menus[0].getSelection())[spriteIndex / 2][spriteIndex % 2]; - screen.render(xOffset, yOffset, sprite); + screen.render(null, xOffset, yOffset, sprite); // Help text. Font.drawCentered(Localization.getLocalized("minicraft.displays.resource_packs.display.help.move", Game.input.getMapping("cursor-down"), Game.input.getMapping("cursor-up")), screen, Screen.h - 17, Color.DARK_GRAY); diff --git a/src/client/java/minicraft/screen/TitleDisplay.java b/src/client/java/minicraft/screen/TitleDisplay.java index 4fc688322..b5d526442 100644 --- a/src/client/java/minicraft/screen/TitleDisplay.java +++ b/src/client/java/minicraft/screen/TitleDisplay.java @@ -122,7 +122,7 @@ public void render(Screen screen) { for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { - screen.render(xo + x * 8, yo + y * 8, x, y, 0, sheet); + screen.render(null, xo + x * 8, yo + y * 8, x, y, 0, sheet); } } diff --git a/src/client/java/minicraft/screen/WorldGenDisplay.java b/src/client/java/minicraft/screen/WorldGenDisplay.java index d09c1764a..38d30a1a4 100644 --- a/src/client/java/minicraft/screen/WorldGenDisplay.java +++ b/src/client/java/minicraft/screen/WorldGenDisplay.java @@ -7,6 +7,7 @@ import minicraft.core.io.Localization; import minicraft.core.io.Settings; import minicraft.gfx.Color; +import minicraft.gfx.Dimension; import minicraft.gfx.Font; import minicraft.gfx.MinicraftImage; import minicraft.gfx.Rectangle; @@ -143,8 +144,7 @@ public String getUserInput() { public WorldGenDisplay() { super(true); - InputEntry nameField = makeWorldNameInput("minicraft.displays.world_gen.enter_world", WorldSelectDisplay.getWorldNames(), "", true) - .setRenderingBounds(new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2)).setEntryPos(RelPos.LEFT); + InputEntry nameField = makeWorldNameInput("minicraft.displays.world_gen.enter_world", WorldSelectDisplay.getWorldNames(), "", true); SelectEntry nameHelp = new SelectEntry("minicraft.displays.world_gen.troublesome_input", () -> Game.setDisplay(new PopupDisplay(null, "minicraft.displays.world_gen.troublesome_input.msg"))) { @Override @@ -168,7 +168,7 @@ public int getColor(boolean isSelected) { worldSeed = new InputEntry("minicraft.displays.world_gen.world_seed", "[-!\"#%/()=+,a-zA-Z0-9]+", 20) { @Override public boolean isValid() { return true; } - }.setRenderingBounds(new ListEntry.IntRange(MinicraftImage.boxWidth * 2, Screen.w - MinicraftImage.boxWidth * 2)).setEntryPos(RelPos.LEFT); + }; Menu mainMenu = new Menu.Builder(false, 10, RelPos.LEFT, @@ -183,8 +183,8 @@ public int getColor(boolean isSelected) { Game.setDisplay(new LoadingDisplay()); }) { @Override - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { - Font.draw(toString(), screen, x, y, Color.CYAN, bounds); + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel limitingModel, int x, int y, boolean isSelected) { + Font.draw(limitingModel, toString(), screen, x, y, Color.CYAN); } }, @@ -198,14 +198,8 @@ public void render(Screen screen, int x, int y, boolean isSelected, @Nullable In .setDisplayLength(5) .setScrollPolicies(0.8f, false) .setTitle("minicraft.displays.world_gen.title") - .setMaxBoundsAsRenderingBounds() .createMenu(); - Rectangle menuBounds = mainMenu.getBounds(); - ListEntry.IntRange xBounds = new ListEntry.IntRange(menuBounds.getLeft() + MinicraftImage.boxWidth * 2, menuBounds.getRight() - MinicraftImage.boxWidth * 2); - nameField.setBounds(xBounds); - worldSeed.setBounds(xBounds); - onScreenKeyboardMenu = OnScreenKeyboardMenu.checkAndCreateMenu(); if (onScreenKeyboardMenu == null) menus = new Menu[] { mainMenu }; diff --git a/src/client/java/minicraft/screen/entry/BlankEntry.java b/src/client/java/minicraft/screen/entry/BlankEntry.java index e8eb20adc..1a0d796b0 100644 --- a/src/client/java/minicraft/screen/entry/BlankEntry.java +++ b/src/client/java/minicraft/screen/entry/BlankEntry.java @@ -15,7 +15,7 @@ public BlankEntry() { public void tick(InputHandler input) {} @Override - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) {} + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel limitingModel, int x, int y, boolean isSelected) {} @Override public int getWidth() { diff --git a/src/client/java/minicraft/screen/entry/InputEntry.java b/src/client/java/minicraft/screen/entry/InputEntry.java index 12b18aafd..247a53d0c 100644 --- a/src/client/java/minicraft/screen/entry/InputEntry.java +++ b/src/client/java/minicraft/screen/entry/InputEntry.java @@ -1,22 +1,23 @@ package minicraft.screen.entry; +import minicraft.core.Action; import minicraft.core.io.ClipboardHandler; import minicraft.core.io.InputHandler; import minicraft.core.io.Localization; import minicraft.gfx.Color; import minicraft.gfx.Font; -import minicraft.gfx.MinicraftImage; import minicraft.gfx.Screen; import minicraft.screen.RelPos; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.function.Consumer; + public class InputEntry extends ListEntry { private String prompt; private String regex; private int maxLength; - private IntRange bounds; - private IntRange renderingBounds; private RelPos entryPos; private String userInput; @@ -44,12 +45,12 @@ public void tick(InputHandler input) { String prev = userInput; userInput = input.addKeyTyped(userInput, regex); if (!prev.equals(userInput)) { - if (listener != null) { + if (hook != null) + hook.act(); + if (listener != null) listener.onChange(userInput); - } - - updateCursorDisplacement(); } + hook = null; // Once per tick if (maxLength > 0 && userInput.length() > maxLength) userInput = userInput.substring(0, maxLength); // truncates extra @@ -67,47 +68,22 @@ public void tick(InputHandler input) { } } - public String getUserInput() { return userInput; } - - public String toString() { - return Localization.getLocalized(prompt) + (prompt.length() == 0 ? "" : ": ") + userInput; - } - - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { - Font.draw(toString(), screen, x, y, isValid() ? isSelected ? Color.GREEN : COL_UNSLCT : Color.RED, bounds); - } + private @Nullable Action hook = null; - private void updateCursorDisplacement() { - IntRange renderingBounds = this.renderingBounds == null ? bounds : this.renderingBounds; - if (bounds != null && entryPos != null) { - int spaceWidth = renderingBounds.upper - bounds.lower; - int width = getWidth(); - if (width <= spaceWidth) { - xDisplacement = 0; - } else { - if (entryPos.xIndex == 0) { // Left - xDisplacement = spaceWidth - width; - } else if (entryPos.xIndex == 1) { // Center - // Assume that the menu is rebuilt when updated. - xDisplacement = (spaceWidth - width) / 2; - } // xIndex == 2 is not handled. - } - } + @Override + public void hook(@NotNull Action callback) { + this.hook = callback; } - public InputEntry setBounds(IntRange bounds) { - this.bounds = bounds; - return this; - } + public String getUserInput() { return userInput; } - public InputEntry setEntryPos(RelPos entryPos) { - this.entryPos = entryPos; - return this; + public String toString() { + return Localization.getLocalized(prompt) + (prompt.length() == 0 ? "" : ": ") + userInput; } - public InputEntry setRenderingBounds(IntRange bounds) { - this.renderingBounds = bounds; - return this; + @Override + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel limitingModel, int x, int y, boolean isSelected) { + Font.draw(limitingModel, toString(), screen, x, y, isValid() ? isSelected ? Color.GREEN : COL_UNSLCT : Color.RED); } public boolean isValid() { diff --git a/src/client/java/minicraft/screen/entry/ItemEntry.java b/src/client/java/minicraft/screen/entry/ItemEntry.java index 8c10c5193..cddb51963 100644 --- a/src/client/java/minicraft/screen/entry/ItemEntry.java +++ b/src/client/java/minicraft/screen/entry/ItemEntry.java @@ -26,9 +26,9 @@ public static ItemEntry[] useItems(List items) { public void tick(InputHandler input) {} @Override - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { - super.render(screen, x, y, true, bounds); - screen.render(x, y, item.sprite); + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel limitingModel, int x, int y, boolean isSelected) { + super.render(screen, limitingModel, x, y, true); + screen.render(null, x, y, item.sprite); } // If you add to the length of the string, and therefore the width of the entry, then it will actually move the entry RIGHT in the inventory, instead of the intended left, because it is auto-positioned to the left side. diff --git a/src/client/java/minicraft/screen/entry/ListEntry.java b/src/client/java/minicraft/screen/entry/ListEntry.java index 0a21fdeab..d1dec3097 100644 --- a/src/client/java/minicraft/screen/entry/ListEntry.java +++ b/src/client/java/minicraft/screen/entry/ListEntry.java @@ -1,10 +1,11 @@ package minicraft.screen.entry; +import minicraft.core.Action; import minicraft.core.io.InputHandler; import minicraft.gfx.Color; import minicraft.gfx.Font; -import minicraft.gfx.MinicraftImage; import minicraft.gfx.Screen; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Locale; @@ -16,20 +17,24 @@ public abstract class ListEntry { private boolean selectable = true, visible = true; - protected int xDisplacement = 0; // Displacing to the left for negative values, right for positive values. - /** * Ticks the entry. Used to handle input from the InputHandler * @param input InputHandler used to get player input. */ public abstract void tick(InputHandler input); - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds, String contain, int containColor) { + public void tickScrollingTicker(@NotNull SelectableStringEntry.EntryXAccessor accessor) {} + + public boolean isScrollingTickerSet() { return false; } + + public void hook(@NotNull Action callback) {} + + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel bounds, int x, int y, boolean isSelected, String contain, int containColor) { if (!visible) { return; } - render(screen, x, y, isSelected, bounds); + render(screen, bounds, x, y, isSelected); if (contain == null || contain.isEmpty()) { return; } @@ -37,16 +42,7 @@ public void render(Screen screen, int x, int y, boolean isSelected, @Nullable In String string = toString().toLowerCase(Locale.ENGLISH); contain = contain.toLowerCase(Locale.ENGLISH); - Font.drawColor(string.replace(contain, Color.toStringCode(containColor) + contain + Color.WHITE_CODE), screen, x, y); - } - - public static class IntRange { - public final int lower; - public final int upper; - public IntRange(int lower, int upper) { - this.lower = lower; - this.upper = upper; - } + Font.drawColor(bounds, string.replace(contain, Color.toStringCode(containColor) + contain + Color.WHITE_CODE), screen, x, y); } /** @@ -58,13 +54,13 @@ public IntRange(int lower, int upper) { * @param isSelected true if the entry is selected, false otherwise * @param bounds X rendering bounds */ - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel bounds, int x, int y, boolean isSelected) { if (visible) { String text = toString().replace(Color.WHITE_CODE + Color.GRAY_CODE, Color.toStringCode(getColor(isSelected))); if (text.contains(String.valueOf(Color.COLOR_CHAR))) - Font.drawColor(Color.toStringCode(getColor(isSelected)) + text, screen, x, y, bounds); + Font.drawColor(bounds, Color.toStringCode(getColor(isSelected)) + text, screen, x, y); else - Font.draw(text, screen, x, y, getColor(isSelected), bounds); + Font.draw(bounds, text, screen, x, y, getColor(isSelected)); } } @@ -91,53 +87,6 @@ public static int getHeight() { return Font.textHeight(); } - /** - * Displaces the entry to the left in rendering, with input right. - * @param boundsWidth the length of space in x-axis bounds - */ - public void displaceLeft(int boundsWidth) { - int width = getWidth(); - if (boundsWidth < width) - xDisplacement = Math.max(xDisplacement - MinicraftImage.boxWidth, boundsWidth - width); - } - - /** - * Displaces the entry to the right in rendering, with input left. - * @param boundsWidth the length of space in x-axis bounds - */ - public void displaceRight(int boundsWidth) { - int width = getWidth(); - if (boundsWidth < width) - xDisplacement = Math.min(xDisplacement + MinicraftImage.boxWidth, 0); - } - - /** - * Calculates the rendering displacement of the entry in the menu. - * @return the x displacement. - */ - public int getXDisplacement() { - return xDisplacement; - } - - /** - * Resets the rendering x displacement to the initial value. - * @return this instance itself - * @see #getXDisplacement() - */ - public ListEntry resetXDisplacement() { - xDisplacement = 0; - return this; - } - - /** - * Returns whether to hide the entry content when the entry exceeds the rendering bounds of the menu. - * If the overflowed content is hidden, the cursor is not displaced the part is not rendered if available. - * @return {@code true} to hide the overflowed parts - */ - public boolean hideWhenOverflow() { - return false; - } - /** * Determines if this entry can be selected. * @return true if it is visible and can be selected, false otherwise. diff --git a/src/client/java/minicraft/screen/entry/RecipeEntry.java b/src/client/java/minicraft/screen/entry/RecipeEntry.java index 1a1f8d3ba..42a1e269e 100644 --- a/src/client/java/minicraft/screen/entry/RecipeEntry.java +++ b/src/client/java/minicraft/screen/entry/RecipeEntry.java @@ -28,10 +28,10 @@ public RecipeEntry(Recipe r) { public void tick(InputHandler input) {} @Override - public void render(Screen screen, int x, int y, boolean isSelected, @Nullable IntRange bounds) { + public void render(Screen screen, @Nullable Screen.RenderingLimitingModel limitingModel, int x, int y, boolean isSelected) { if (isVisible()) { - Font.draw(toString(), screen, x, y, recipe.getCanCraft() ? COL_SLCT : COL_UNSLCT, bounds); - screen.render(x, y, getItem().sprite); + Font.draw(limitingModel, toString(), screen, x, y, recipe.getCanCraft() ? COL_SLCT : COL_UNSLCT); + screen.render(null, x, y, getItem().sprite); } } diff --git a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java index 56db81bb0..0b3f42070 100644 --- a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java +++ b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java @@ -4,6 +4,8 @@ import minicraft.core.io.Localization; import minicraft.gfx.Color; import minicraft.gfx.MinicraftImage; +import minicraft.screen.RelPos; +import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Range; @@ -15,7 +17,7 @@ public class SelectableStringEntry extends ListEntry { private final int color; private final boolean localize; - private TextRenderTicker ticker; + private EntryScrollingTicker ticker; public SelectableStringEntry(String text) { this(text, DEFAULT_COLOR); @@ -28,104 +30,120 @@ public SelectableStringEntry(String text, int color, boolean localize) { this.color = color; } - protected abstract static class TextRenderTicker { - protected static final int DEFAULT_CYCLING_PERIOD = 90; + public interface EntryXAccessor { + int getWidth(); + int getX(RelPos anchor); + void setX(RelPos anchor, int x); + void translateX(int displacement); + void setAnchors(RelPos anchor); // This is recommended to be invoked first. + int getLeftBound(RelPos anchor); + int getRightBound(RelPos anchor); + } + + protected abstract static class EntryScrollingTicker { + protected static final int DEFAULT_CYCLING_PERIOD = 90; // in ticks protected int tick = 0; - public abstract void tick(); + public abstract void tick(@NotNull EntryXAccessor accessor); } - private class HorizontalAlternatingScrollingTextRenderTicker extends TextRenderTicker { - private final IntRange bounds; - private final int originX; - + private static class ExceedingHorizontallyAlternatingScrollingTicker extends EntryScrollingTicker { @Range(from = -1, to = 1) private int direction = 0; // Number line direction; text movement - public HorizontalAlternatingScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { - this.bounds = bounds; - this.originX = originX; - } - @Override - public void tick() { - int width = getWidth(); - if (width > bounds.upper - bounds.lower) { + public void tick(@NotNull EntryXAccessor accessor) { + RelPos refAnchor = RelPos.getPos(1 - direction, 0); + accessor.setAnchors(refAnchor); + int x = accessor.getX(refAnchor); + // Proceeds when the entry is out of bounds. + if (x < accessor.getLeftBound(refAnchor) || x > accessor.getRightBound(refAnchor)) { if (direction != 0) { if (tick++ == 5) { - xDisplacement += direction * MinicraftImage.boxWidth; - if (originX + xDisplacement == bounds.lower || // Left side of text tips at left bound - originX + xDisplacement + width == bounds.upper) { // Stop if destination is reached - direction = 0; + x += direction * MinicraftImage.boxWidth; + if ((direction == 1 ? x - accessor.getLeftBound(refAnchor) : + accessor.getRightBound(refAnchor) - x) >= 0) { + // Alignment correction + x = direction == 1 ? accessor.getLeftBound(refAnchor) : accessor.getRightBound(refAnchor); + direction = 0; // Stops when destination is reached. } + accessor.setX(refAnchor, x); tick = 0; } } else if (tick++ == DEFAULT_CYCLING_PERIOD) { - if (originX + xDisplacement + width == bounds.upper) { // Right side of text tips at right bound + if (x <= accessor.getRightBound(refAnchor)) direction = 1; // Right - } else { + else direction = -1; // Left - } - tick = 0; } - } + } else tick = 0; } } - private class HorizontalScrollingTextRenderTicker extends TextRenderTicker { - private final IntRange bounds; - private final int originX; - - private boolean moving = false; + private static class HorizontalScrollerScrollingTicker extends EntryScrollingTicker { + @MagicConstant(intValues = {-1, 1}) + private final int direction; - public HorizontalScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { - this.bounds = bounds; - this.originX = originX; + public HorizontalScrollerScrollingTicker(@MagicConstant int direction) { + switch (direction) { + case -1: case 1: + this.direction = direction; break; + default: + throw new IllegalArgumentException("direction; input: " + direction); + } } + private boolean moving = false; + @Override - public void tick() { - int width = getWidth(); - if (width > bounds.upper - bounds.lower) { + public void tick(@NotNull EntryXAccessor accessor) { + RelPos refAnchor = direction == 1 ? RelPos.LEFT : RelPos.RIGHT; + accessor.setAnchors(refAnchor); + int x = accessor.getX(refAnchor); + int width = accessor.getWidth(); + int lw = direction == -1 ? -width : 0; + int rw = direction == 1 ? width : 0; + // Proceeds when the entry is out of bounds. + if (x < accessor.getLeftBound(refAnchor) || x > accessor.getRightBound(refAnchor)) { if (moving) { if (tick++ == 5) { - if (originX + xDisplacement + width == bounds.lower) { // Right side of text tips at left bound - xDisplacement = bounds.upper - originX; // Moves to the rightmost + if (direction == 1 && x >= accessor.getRightBound(refAnchor) + rw) { // Left side reaches right bound + x += accessor.getLeftBound(refAnchor) - x - width; + } else if (direction == -1 && x <= accessor.getLeftBound(refAnchor) + lw) { + x += accessor.getRightBound(refAnchor) - x + width; + } else { + x += direction * MinicraftImage.boxWidth; } - xDisplacement -= MinicraftImage.boxWidth; - if (xDisplacement == 0) { // Moves back to the original point + int diff = direction == 1 ? accessor.getRightBound(refAnchor) - x : + x - accessor.getLeftBound(refAnchor); + if (diff >= 0 && diff < MinicraftImage.boxWidth) { // Moves back to the original point moving = false; // Pauses the scrolling + // Alignment correction + x = direction == 1 ? accessor.getRightBound(refAnchor) : accessor.getLeftBound(refAnchor); } + accessor.setX(refAnchor, x); tick = 0; } } else if (tick++ == DEFAULT_CYCLING_PERIOD) { moving = true; tick = 0; } - } + } else tick = 0; } } - @Override - public boolean hideWhenOverflow() { - return true; - } - - public void setAlternatingScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { - ticker = new HorizontalAlternatingScrollingTextRenderTicker(bounds, originX); - } - - public void setScrollingTextRenderTicker(@NotNull IntRange bounds, int originX) { - ticker = new HorizontalScrollingTextRenderTicker(bounds, originX); + public void setExceedingAlternatingScrollingTicker() { + ticker = new ExceedingHorizontallyAlternatingScrollingTicker(); } - public void setRenderTicker(TextRenderTicker renderTicker) { - ticker = renderTicker; + public void setScrollerScrollingTicker() { setScrollerScrollingTicker(-1); } + public void setScrollerScrollingTicker(@MagicConstant int direction) { + ticker = new HorizontalScrollerScrollingTicker(direction); } public void setText(String text) { @@ -133,9 +151,17 @@ public void setText(String text) { } @Override - public void tick(InputHandler input) { + public void tick(InputHandler input) {} + + @Override + public void tickScrollingTicker(@NotNull EntryXAccessor accessor) { if (ticker != null) - ticker.tick(); + ticker.tick(accessor); + } + + @Override + public boolean isScrollingTickerSet() { + return ticker != null; } @Override From ea5f95377ef6c27760fa043ca621e00e592e904c Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Thu, 8 Feb 2024 11:40:38 +0800 Subject: [PATCH 4/9] Resolve post-merge errors --- src/client/java/minicraft/level/tile/farming/CarrotTile.java | 2 +- .../java/minicraft/level/tile/farming/HeavenlyBerriesTile.java | 2 +- .../java/minicraft/level/tile/farming/HellishBerriesTile.java | 2 +- src/client/java/minicraft/level/tile/farming/TomatoTile.java | 2 +- src/client/java/minicraft/screen/Menu.java | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/java/minicraft/level/tile/farming/CarrotTile.java b/src/client/java/minicraft/level/tile/farming/CarrotTile.java index de8cfc7b4..bdee09020 100644 --- a/src/client/java/minicraft/level/tile/farming/CarrotTile.java +++ b/src/client/java/minicraft/level/tile/farming/CarrotTile.java @@ -23,6 +23,6 @@ public void render(Screen screen, Level level, int x, int y) { int age = (level.getData(x, y) >> 3) & maxAge; Tiles.get("Farmland").render(screen, level, x, y); int stage = (int) ((float) age / maxAge * 3); - screen.render(x * 16, y * 16, spritStages[stage]); + screen.render(null, x * 16, y * 16, spritStages[stage]); } } diff --git a/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java b/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java index 70b7e3fc8..bdeb932c8 100644 --- a/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java +++ b/src/client/java/minicraft/level/tile/farming/HeavenlyBerriesTile.java @@ -22,6 +22,6 @@ public void render(Screen screen, Level level, int x, int y) { int age = (level.getData(x, y) >> 3) & maxAge; Tiles.get("Farmland").render(screen, level, x, y); int stage = (int) ((float) age / maxAge * 3); - screen.render(x * 16, y * 16, spritStages[stage]); + screen.render(null, x * 16, y * 16, spritStages[stage]); } } diff --git a/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java b/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java index bbad34091..c7506a421 100644 --- a/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java +++ b/src/client/java/minicraft/level/tile/farming/HellishBerriesTile.java @@ -22,6 +22,6 @@ public void render(Screen screen, Level level, int x, int y) { int age = (level.getData(x, y) >> 3) & maxAge; Tiles.get("Farmland").render(screen, level, x, y); int stage = (int) ((float) age / maxAge * 3); - screen.render(x * 16, y * 16, spritStages[stage]); + screen.render(null, x * 16, y * 16, spritStages[stage]); } } diff --git a/src/client/java/minicraft/level/tile/farming/TomatoTile.java b/src/client/java/minicraft/level/tile/farming/TomatoTile.java index 1abe88c04..04bc64eac 100644 --- a/src/client/java/minicraft/level/tile/farming/TomatoTile.java +++ b/src/client/java/minicraft/level/tile/farming/TomatoTile.java @@ -23,6 +23,6 @@ public void render(Screen screen, Level level, int x, int y) { int age = (level.getData(x, y) >> 3) & maxAge; Tiles.get("Farmland").render(screen, level, x, y); int stage = (int) ((float) age / maxAge * 3); - screen.render(x * 16, y * 16, spritStages[stage]); + screen.render(null, x * 16, y * 16, spritStages[stage]); } } diff --git a/src/client/java/minicraft/screen/Menu.java b/src/client/java/minicraft/screen/Menu.java index 89547b823..6a25cb5bf 100644 --- a/src/client/java/minicraft/screen/Menu.java +++ b/src/client/java/minicraft/screen/Menu.java @@ -214,7 +214,7 @@ public void tick(InputHandler input) { } int prevSel = selection; - if (input.getKey("ALT").down) { + if (input.getMappedKey("ALT").isDown()) { if (input.inputPressed("cursor-left")) entries.get(selection).translateX(MinicraftImage.boxWidth, true); if (input.inputPressed("cursor-right")) From 1b99fa6c93a4104f40fc0e71af32ff14cf13f81c Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:20:37 +0800 Subject: [PATCH 5/9] Create abstract EntryRenderingUnit --- src/client/java/minicraft/gfx/Screen.java | 157 ++++++++++++++++ src/client/java/minicraft/screen/Menu.java | 176 ++++-------------- .../screen/entry/SelectableStringEntry.java | 1 + 3 files changed, 198 insertions(+), 136 deletions(-) diff --git a/src/client/java/minicraft/gfx/Screen.java b/src/client/java/minicraft/gfx/Screen.java index 814524b4c..79c197eae 100644 --- a/src/client/java/minicraft/gfx/Screen.java +++ b/src/client/java/minicraft/gfx/Screen.java @@ -2,11 +2,13 @@ import minicraft.core.Renderer; import minicraft.core.Updater; +import minicraft.core.io.InputHandler; import minicraft.gfx.SpriteLinker.LinkedSprite; import minicraft.gfx.SpriteLinker.SpriteType; import minicraft.screen.RelPos; import minicraft.screen.entry.ListEntry; import minicraft.screen.entry.SelectableStringEntry; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -59,6 +61,161 @@ public boolean contains(int x, int y) { // inclusive } } + public static abstract class EntryRenderingUnit { + protected abstract class EntryLimitingModel extends RenderingLimitingModel { + @Override + public int getLeftBound() { + return getEntryBounds().getLeft(); + } + + @Override + public int getRightBound() { + return getEntryBounds().getRight() - 1; // As this model is inclusive. + } + + @Override + public int getTopBound() { + return getEntryBounds().getTop(); + } + + @Override + public int getBottomBound() { + return getEntryBounds().getBottom() - 1; // As this model is inclusive. + } + } + + protected abstract class EntryXAccessor implements SelectableStringEntry.EntryXAccessor { + /* Most of the methods here can be obtained by algebra. */ + + @Override + public int getWidth() { + return getDelegate().getWidth(); + } + + @Override + public int getX(RelPos anchor) { + return xPos + (containerAnchor.xIndex - anchor.xIndex) * getEntryBounds().getWidth() / 2 + + (anchor.xIndex - entryAnchor.xIndex) * getDelegate().getWidth() / 2; + } + + @Override + public void setX(RelPos anchor, int x) { + xPos = x + (anchor.xIndex - containerAnchor.xIndex) * getEntryBounds().getWidth() / 2 + + (entryAnchor.xIndex - anchor.xIndex) * getDelegate().getWidth() / 2; + } + + @Override + public void translateX(int displacement) { + xPos += displacement; + } + + @Override + public void setAnchors(RelPos anchor) { + changeRelativeEntryAnchor(anchor); + changeRelativeContainerAnchor(anchor); + } + + @Override + public int getLeftBound(RelPos anchor) { + return anchor.xIndex * (getDelegate().getWidth() - getEntryBounds().getWidth()) / 2; + } + + @Override + public int getRightBound(RelPos anchor) { + return (2 - anchor.xIndex) * (getEntryBounds().getWidth() - getDelegate().getWidth()) / 2; + } + } + + protected abstract EntryLimitingModel getLimitingModel(); + protected abstract EntryXAccessor getXAccessor(); + + /** + * A reference anchor, which is the relative position of its container
+ * Also, it is the relative position of the entry.
+ * Acceptable values: {@code LEFT}, {@code CENTER}, {@code RIGHT} + */ + protected @NotNull RelPos containerAnchor; + /** + * A reference anchor of the entry
+ * This is usually the same as {@link #containerAnchor}. + * Acceptable values: {@code LEFT}, {@code CENTER}, {@code RIGHT} + */ + protected @NotNull RelPos entryAnchor; + /** The x-position of the entry at its anchor {@link #entryAnchor} relative to the container anchor {@link #containerAnchor} */ + protected int xPos = 0; + + /** This is used to prevent further exceptions to {@link RelPos}. + * Here, we only use the x indices for positioning, so we can just simply shrink the acceptable value range + * to an extent that we use the best. */ + private static RelPos getRelPos(@NotNull RelPos anchor) { + return RelPos.getPos(anchor.xIndex, 1); // Confirms that this meets the requirement. + } + + protected EntryRenderingUnit(@NotNull RelPos anchor) { + anchor = getRelPos(anchor); + this.containerAnchor = anchor; + this.entryAnchor = anchor; + } + protected EntryRenderingUnit(@NotNull RelPos containerAnchor, @NotNull RelPos entryAnchor) { + this.containerAnchor = getRelPos(containerAnchor); + this.entryAnchor = getRelPos(entryAnchor); + } + + protected abstract Rectangle getEntryBounds(); // Global coordinate system + protected abstract ListEntry getDelegate(); + + public void resetRelativeAnchorsSynced(RelPos newAnchor) { + entryAnchor = containerAnchor = newAnchor; + xPos = 0; + } + public void moveRelativeContainerAnchor(RelPos newAnchor) { + containerAnchor = newAnchor; + xPos = 0; + } + public void moveRelativeEntryAnchor(RelPos newAnchor) { + entryAnchor = newAnchor; + xPos = 0; + } + public void changeRelativeContainerAnchor(RelPos newAnchor) { + xPos += (containerAnchor.xIndex - newAnchor.xIndex) * getEntryBounds().getWidth() / 2; + containerAnchor = newAnchor; + } + public void changeRelativeEntryAnchor(RelPos newAnchor) { + xPos += (newAnchor.xIndex - entryAnchor.xIndex) * getDelegate().getWidth() / 2; + entryAnchor = newAnchor; + } + + public void tick(InputHandler input) { + getDelegate().tickScrollingTicker(getXAccessor()); + getDelegate().tick(input); + } + + protected void renderExtra(Screen screen, int x, int y, int entryWidth, boolean selected) {} + + protected boolean renderOutOfFrame() { + return false; + } + + public void render(Screen screen, int y, boolean selected) { + int w = getDelegate().getWidth(); // Reduce calculation + int x = getRenderX(w); // Reduce calculation + getDelegate().render(screen, renderOutOfFrame() ? null : getLimitingModel(), x, y, selected); + renderExtra(screen, x, y, w, selected); + } + + public void render(Screen screen, int y, boolean selected, String contain, int containColor) { + int w = getDelegate().getWidth(); // Reduce calculation + int x = getRenderX(w); // Reduce calculation + getDelegate().render(screen, renderOutOfFrame() ? null : getLimitingModel(), x, y, selected, contain, containColor); + renderExtra(screen, x, y, w, selected); + } + + protected int getRenderX(int entryWidth) { + return getEntryBounds().getLeft() + xPos - entryAnchor.xIndex * entryWidth / 2 + + containerAnchor.xIndex * getEntryBounds().getWidth() / 2; + } + } + // Just use a menu for it. public static final class ScreenLimitingModel extends RenderingLimitingModel { public static final ScreenLimitingModel INSTANCE = new ScreenLimitingModel(); diff --git a/src/client/java/minicraft/screen/Menu.java b/src/client/java/minicraft/screen/Menu.java index 6a25cb5bf..5f6de5306 100644 --- a/src/client/java/minicraft/screen/Menu.java +++ b/src/client/java/minicraft/screen/Menu.java @@ -450,100 +450,39 @@ private void renderFrame(Screen screen) { } /** Acts as an internal wrapping decorator of a {@link ListEntry} for the {@link Menu} environment. */ - private class MenuListEntry { - private class MenuEntryLimitingModel extends Screen.RenderingLimitingModel { - @Override - public int getLeftBound() { - return getEntryBounds().getLeft(); - } - - @Override - public int getRightBound() { - return getEntryBounds().getRight() - 1; // As this model is inclusive. - } - - @Override - public int getTopBound() { - return getEntryBounds().getTop(); - } - - @Override - public int getBottomBound() { - return getEntryBounds().getBottom() - 1; // As this model is inclusive. - } - } - - private class MenuEntryXAccessor implements SelectableStringEntry.EntryXAccessor { - /* Most of the methods here can be obtained by algebra. */ - - @Override - public int getWidth() { - return delegate.getWidth(); - } + private class MenuListEntry extends Screen.EntryRenderingUnit { + private class MenuEntryLimitingModel extends EntryLimitingModel {} - @Override - public int getX(RelPos anchor) { - return xPos + (menuAnchor.xIndex - anchor.xIndex) * getEntryBounds().getWidth() / 2 + - (anchor.xIndex - entryAnchor.xIndex) * delegate.getWidth() / 2; - } - - @Override - public void setX(RelPos anchor, int x) { - xPos = x + (anchor.xIndex - menuAnchor.xIndex) * getEntryBounds().getWidth() / 2 + - (entryAnchor.xIndex - anchor.xIndex) * delegate.getWidth() / 2; - } - - @Override - public void translateX(int displacement) { - xPos += displacement; - } - - @Override - public void setAnchors(RelPos anchor) { - changeRelativeEntryAnchor(anchor); - changeRelativeMenuAnchor(anchor); - } - - @Override - public int getLeftBound(RelPos anchor) { - return anchor.xIndex * (delegate.getWidth() - getEntryBounds().getWidth()) / 2; - } - - @Override - public int getRightBound(RelPos anchor) { - return (2 - anchor.xIndex) * (getEntryBounds().getWidth() - delegate.getWidth()) / 2; - } - } + private class MenuEntryXAccessor extends EntryXAccessor {} public final MenuEntryLimitingModel limitingModel = new MenuEntryLimitingModel(); public final MenuEntryXAccessor accessor = new MenuEntryXAccessor(); public final ListEntry delegate; - /** - * A reference anchor, which is the relative position of the container menu
- * Also, it is the relative position of the entry.
- * Acceptable values: {@code LEFT}, {@code CENTER}, {@code RIGHT} - */ - private @NotNull RelPos menuAnchor; - /** - * A reference anchor of the entry
- * This is usually the same as {@link #menuAnchor}. - */ - private @NotNull RelPos entryAnchor; - /** The x-position of the entry at its anchor {@link #entryAnchor} relative to the menu anchor {@link #menuAnchor} */ - private int xPos = 0; - public MenuListEntry(ListEntry delegate, @NotNull RelPos anchor) { + super(anchor); this.delegate = delegate; - anchor = RelPos.getPos(anchor.xIndex, 1); // Confirms that this meets the requirement. - this.menuAnchor = anchor; - this.entryAnchor = anchor; } - private Rectangle getEntryBounds() { + @Override + protected EntryLimitingModel getLimitingModel() { + return limitingModel; + } + + @Override + protected EntryXAccessor getXAccessor() { + return accessor; + } + + protected Rectangle getEntryBounds() { return delegate.isSelectable() ? entryBounds : entryRenderingBounds; } + @Override + protected ListEntry getDelegate() { + return delegate; + } + public void translateX(int displacement, boolean limit) { xPos += displacement; if (limit) { @@ -551,78 +490,43 @@ public void translateX(int displacement, boolean limit) { int minX; int maxX; if (w <= getEntryBounds().getWidth()) { - minX = (entryAnchor.xIndex * w - menuAnchor.xIndex * getEntryBounds().getWidth()) / 2; - maxX = ((2 - menuAnchor.xIndex) * getEntryBounds().getWidth() - (2 - entryAnchor.xIndex) * w) / 2; + minX = (entryAnchor.xIndex * w - containerAnchor.xIndex * getEntryBounds().getWidth()) / 2; + maxX = ((2 - containerAnchor.xIndex) * getEntryBounds().getWidth() - (2 - entryAnchor.xIndex) * w) / 2; } else { - minX = ((2 - menuAnchor.xIndex) * getEntryBounds().getWidth() - (2 - entryAnchor.xIndex) * w) / 2; - maxX = (entryAnchor.xIndex * w - menuAnchor.xIndex * getEntryBounds().getWidth()) / 2; + minX = ((2 - containerAnchor.xIndex) * getEntryBounds().getWidth() - (2 - entryAnchor.xIndex) * w) / 2; + maxX = (entryAnchor.xIndex * w - containerAnchor.xIndex * getEntryBounds().getWidth()) / 2; } if (xPos < minX) xPos = minX; if (xPos > maxX) xPos = maxX; } } - public void resetRelativeAnchorsSynced(RelPos newAnchor) { - entryAnchor = menuAnchor = newAnchor; - xPos = 0; - } - public void moveRelativeMenuAnchor(RelPos newAnchor) { - menuAnchor = newAnchor; - xPos = 0; - } - public void moveRelativeEntryAnchor(RelPos newAnchor) { - entryAnchor = newAnchor; - xPos = 0; - } - public void changeRelativeMenuAnchor(RelPos newAnchor) { - xPos += (menuAnchor.xIndex - newAnchor.xIndex) * getEntryBounds().getWidth() / 2; - menuAnchor = newAnchor; - } - public void changeRelativeEntryAnchor(RelPos newAnchor) { - xPos += (newAnchor.xIndex - entryAnchor.xIndex) * delegate.getWidth() / 2; - entryAnchor = newAnchor; - } - + @Override public void tick(InputHandler input) { - delegate.hook(() -> { + delegate.hook(() -> { // For input entry xPos = 0; // Resets position - int diff = xPos + (menuAnchor.xIndex - 2) * getEntryBounds().getWidth() / 2 + + int diff = xPos + (containerAnchor.xIndex - 2) * getEntryBounds().getWidth() / 2 + (2 - entryAnchor.xIndex) * delegate.getWidth() / 2; if (diff > 0) translateX(-diff, false); // Shifts if and only if the entry exceeds the area. }); - delegate.tickScrollingTicker(accessor); - delegate.tick(input); - } - - private void renderCursor(Screen screen, int x, int y, int entryWidth, boolean tickerSet) { - if (tickerSet) { - entryWidth = Math.min(entryWidth, getEntryBounds().getWidth()); - x = getEntryBounds().getLeft(); - } - - Font.draw(null, "> ", screen, x - 2 * MinicraftImage.boxWidth, y, ListEntry.COL_SLCT); - Font.draw(null, " <", screen, x + entryWidth, y, ListEntry.COL_SLCT); + super.tick(input); } - public void render(Screen screen, int y, boolean selected) { - int w = delegate.getWidth(); // Reduce calculation - int x = getRenderX(w); // Reduce calculation - delegate.render(screen, renderOutOfFrame ? null : limitingModel, x, y, selected); - if (selected && delegate.isSelectable()) - renderCursor(screen, x, y, w, delegate.isScrollingTickerSet()); + @Override + protected boolean renderOutOfFrame() { + return renderOutOfFrame; } - public void render(Screen screen, int y, boolean selected, String contain, int containColor) { - int w = delegate.getWidth(); // Reduce calculation - int x = getRenderX(w); // Reduce calculation - delegate.render(screen, renderOutOfFrame ? null : limitingModel, x, y, selected, contain, containColor); - if (selected && delegate.isSelectable()) - renderCursor(screen, x, y, w, delegate.isScrollingTickerSet()); - } + protected void renderExtra(Screen screen, int x, int y, int entryWidth, boolean selected) { + if (selected && delegate.isSelectable()) { + if (delegate.isScrollingTickerSet()) { + entryWidth = Math.min(entryWidth, getEntryBounds().getWidth()); + x = getEntryBounds().getLeft(); + } - private int getRenderX(int entryWidth) { - return getEntryBounds().getLeft() + xPos - entryAnchor.xIndex * entryWidth / 2 + - menuAnchor.xIndex * getEntryBounds().getWidth() / 2; + Font.draw(null, "> ", screen, x - 2 * MinicraftImage.boxWidth, y, ListEntry.COL_SLCT); + Font.draw(null, " <", screen, x + entryWidth, y, ListEntry.COL_SLCT); + } } } diff --git a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java index 0b3f42070..5d58f6068 100644 --- a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java +++ b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java @@ -30,6 +30,7 @@ public SelectableStringEntry(String text, int color, boolean localize) { this.color = color; } + /** All values here should be in the same relative coordinate system. */ public interface EntryXAccessor { int getWidth(); int getX(RelPos anchor); From fda908ce8e2fb8fd0c3abc85e1df86190c0823a1 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:44:36 +0800 Subject: [PATCH 6/9] Add general ScreenEntry interface --- src/client/java/minicraft/gfx/Screen.java | 13 ++++++++++++- .../java/minicraft/screen/entry/ListEntry.java | 8 +++++++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/client/java/minicraft/gfx/Screen.java b/src/client/java/minicraft/gfx/Screen.java index 79c197eae..49079011c 100644 --- a/src/client/java/minicraft/gfx/Screen.java +++ b/src/client/java/minicraft/gfx/Screen.java @@ -61,6 +61,17 @@ public boolean contains(int x, int y) { // inclusive } } + /** The basic class of a GUI entry. */ + public interface ScreenEntry { + int getWidth(); + void tick(InputHandler input); + void tickScrollingTicker(SelectableStringEntry.EntryXAccessor accessor); + boolean isScrollingTickerSet(); + void render(Screen screen, @Nullable RenderingLimitingModel limitingModel, int x, int y, boolean selected); + void render(Screen screen, @Nullable RenderingLimitingModel limitingModel, int x, int y, boolean selected, + String contain, int containColor); + } + public static abstract class EntryRenderingUnit { protected abstract class EntryLimitingModel extends RenderingLimitingModel { @Override @@ -162,7 +173,7 @@ protected EntryRenderingUnit(@NotNull RelPos containerAnchor, @NotNull RelPos en } protected abstract Rectangle getEntryBounds(); // Global coordinate system - protected abstract ListEntry getDelegate(); + protected abstract ScreenEntry getDelegate(); public void resetRelativeAnchorsSynced(RelPos newAnchor) { entryAnchor = containerAnchor = newAnchor; diff --git a/src/client/java/minicraft/screen/entry/ListEntry.java b/src/client/java/minicraft/screen/entry/ListEntry.java index 86765d370..9041fbbc5 100644 --- a/src/client/java/minicraft/screen/entry/ListEntry.java +++ b/src/client/java/minicraft/screen/entry/ListEntry.java @@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -public abstract class ListEntry { +public abstract class ListEntry implements Screen.ScreenEntry { public static final int COL_UNSLCT = Color.GRAY; public static final int COL_SLCT = Color.WHITE; @@ -20,14 +20,18 @@ public abstract class ListEntry { * * @param input InputHandler used to get player input. */ + @Override public abstract void tick(InputHandler input); + @Override public void tickScrollingTicker(@NotNull SelectableStringEntry.EntryXAccessor accessor) {} + @Override public boolean isScrollingTickerSet() { return false; } public void hook(@NotNull Action callback) {} + @Override public void render(Screen screen, @Nullable Screen.RenderingLimitingModel bounds, int x, int y, boolean isSelected, String contain, int containColor) { if (!visible) { return; @@ -53,6 +57,7 @@ public void render(Screen screen, @Nullable Screen.RenderingLimitingModel bounds * @param isSelected true if the entry is selected, false otherwise * @param bounds X rendering bounds */ + @Override public void render(Screen screen, @Nullable Screen.RenderingLimitingModel bounds, int x, int y, boolean isSelected) { if (visible) { String text = toString().replace(Color.WHITE_CODE + Color.GRAY_CODE, Color.toStringCode(getColor(isSelected))); @@ -78,6 +83,7 @@ public int getColor(boolean isSelected) { * * @return the entry's width */ + @Override public int getWidth() { return Font.textWidth(toString()); } From debcca021c22962f0f2f80aad81666b01dbc181e Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Thu, 8 Feb 2024 19:56:39 +0800 Subject: [PATCH 7/9] Move scrolling ticker classes to screen entry --- src/client/java/minicraft/gfx/Screen.java | 111 +++++++++++++++++- .../screen/entry/SelectableStringEntry.java | 111 ------------------ 2 files changed, 110 insertions(+), 112 deletions(-) diff --git a/src/client/java/minicraft/gfx/Screen.java b/src/client/java/minicraft/gfx/Screen.java index 49079011c..897d792ec 100644 --- a/src/client/java/minicraft/gfx/Screen.java +++ b/src/client/java/minicraft/gfx/Screen.java @@ -6,10 +6,11 @@ import minicraft.gfx.SpriteLinker.LinkedSprite; import minicraft.gfx.SpriteLinker.SpriteType; import minicraft.screen.RelPos; -import minicraft.screen.entry.ListEntry; import minicraft.screen.entry.SelectableStringEntry; +import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.Range; import java.util.Arrays; @@ -70,6 +71,114 @@ public interface ScreenEntry { void render(Screen screen, @Nullable RenderingLimitingModel limitingModel, int x, int y, boolean selected); void render(Screen screen, @Nullable RenderingLimitingModel limitingModel, int x, int y, boolean selected, String contain, int containColor); + + /** All values here should be in the same relative coordinate system. */ + interface EntryXAccessor { + int getWidth(); + int getX(RelPos anchor); + void setX(RelPos anchor, int x); + void translateX(int displacement); + void setAnchors(RelPos anchor); // This is recommended to be invoked first. + int getLeftBound(RelPos anchor); + int getRightBound(RelPos anchor); + } + + abstract class EntryScrollingTicker { + protected static final int DEFAULT_CYCLING_PERIOD = 90; // in ticks + + protected int tick = 0; + + public abstract void tick(@NotNull EntryXAccessor accessor); + } + + class ExceedingHorizontallyAlternatingScrollingTicker extends EntryScrollingTicker { + @Range(from = -1, to = 1) + private int direction = 0; // Number line direction; text movement + + @Override + public void tick(@NotNull EntryXAccessor accessor) { + RelPos refAnchor = RelPos.getPos(1 - direction, 0); + accessor.setAnchors(refAnchor); + int x = accessor.getX(refAnchor); + // Proceeds when the entry is out of bounds. + if (x < accessor.getLeftBound(refAnchor) || x > accessor.getRightBound(refAnchor)) { + if (direction != 0) { + if (tick++ == 5) { + x += direction * MinicraftImage.boxWidth; + if ((direction == 1 ? x - accessor.getLeftBound(refAnchor) : + accessor.getRightBound(refAnchor) - x) >= 0) { + // Alignment correction + x = direction == 1 ? accessor.getLeftBound(refAnchor) : accessor.getRightBound(refAnchor); + direction = 0; // Stops when destination is reached. + } + + accessor.setX(refAnchor, x); + tick = 0; + } + } else if (tick++ == DEFAULT_CYCLING_PERIOD) { + if (x <= accessor.getRightBound(refAnchor)) + direction = 1; // Right + else + direction = -1; // Left + tick = 0; + } + } else tick = 0; + } + } + + class HorizontalScrollerScrollingTicker extends EntryScrollingTicker { + @MagicConstant(intValues = {-1, 1}) + private final int direction; + + public HorizontalScrollerScrollingTicker(@MagicConstant int direction) { + switch (direction) { + case -1: case 1: + this.direction = direction; break; + default: + throw new IllegalArgumentException("direction; input: " + direction); + } + } + + private boolean moving = false; + + @Override + public void tick(@NotNull EntryXAccessor accessor) { + RelPos refAnchor = direction == 1 ? RelPos.LEFT : RelPos.RIGHT; + accessor.setAnchors(refAnchor); + int x = accessor.getX(refAnchor); + int width = accessor.getWidth(); + int lw = direction == -1 ? -width : 0; + int rw = direction == 1 ? width : 0; + // Proceeds when the entry is out of bounds. + if (x < accessor.getLeftBound(refAnchor) || x > accessor.getRightBound(refAnchor)) { + if (moving) { + if (tick++ == 5) { + if (direction == 1 && x >= accessor.getRightBound(refAnchor) + rw) { // Left side reaches right bound + x += accessor.getLeftBound(refAnchor) - x - width; + } else if (direction == -1 && x <= accessor.getLeftBound(refAnchor) + lw) { + x += accessor.getRightBound(refAnchor) - x + width; + } else { + x += direction * MinicraftImage.boxWidth; + } + + int diff = direction == 1 ? accessor.getRightBound(refAnchor) - x : + x - accessor.getLeftBound(refAnchor); + if (diff >= 0 && diff < MinicraftImage.boxWidth) { // Moves back to the original point + moving = false; // Pauses the scrolling + // Alignment correction + x = direction == 1 ? accessor.getRightBound(refAnchor) : accessor.getLeftBound(refAnchor); + } + + accessor.setX(refAnchor, x); + tick = 0; + } + } else if (tick++ == DEFAULT_CYCLING_PERIOD) { + moving = true; + tick = 0; + } + } else tick = 0; + } + } } public static abstract class EntryRenderingUnit { diff --git a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java index 5d58f6068..1ef4f35b4 100644 --- a/src/client/java/minicraft/screen/entry/SelectableStringEntry.java +++ b/src/client/java/minicraft/screen/entry/SelectableStringEntry.java @@ -3,11 +3,8 @@ import minicraft.core.io.InputHandler; import minicraft.core.io.Localization; import minicraft.gfx.Color; -import minicraft.gfx.MinicraftImage; -import minicraft.screen.RelPos; import org.intellij.lang.annotations.MagicConstant; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Range; public class SelectableStringEntry extends ListEntry { @@ -30,114 +27,6 @@ public SelectableStringEntry(String text, int color, boolean localize) { this.color = color; } - /** All values here should be in the same relative coordinate system. */ - public interface EntryXAccessor { - int getWidth(); - int getX(RelPos anchor); - void setX(RelPos anchor, int x); - void translateX(int displacement); - void setAnchors(RelPos anchor); // This is recommended to be invoked first. - int getLeftBound(RelPos anchor); - int getRightBound(RelPos anchor); - } - - protected abstract static class EntryScrollingTicker { - protected static final int DEFAULT_CYCLING_PERIOD = 90; // in ticks - - protected int tick = 0; - - public abstract void tick(@NotNull EntryXAccessor accessor); - } - - private static class ExceedingHorizontallyAlternatingScrollingTicker extends EntryScrollingTicker { - @Range(from = -1, to = 1) - private int direction = 0; // Number line direction; text movement - - @Override - public void tick(@NotNull EntryXAccessor accessor) { - RelPos refAnchor = RelPos.getPos(1 - direction, 0); - accessor.setAnchors(refAnchor); - int x = accessor.getX(refAnchor); - // Proceeds when the entry is out of bounds. - if (x < accessor.getLeftBound(refAnchor) || x > accessor.getRightBound(refAnchor)) { - if (direction != 0) { - if (tick++ == 5) { - x += direction * MinicraftImage.boxWidth; - if ((direction == 1 ? x - accessor.getLeftBound(refAnchor) : - accessor.getRightBound(refAnchor) - x) >= 0) { - // Alignment correction - x = direction == 1 ? accessor.getLeftBound(refAnchor) : accessor.getRightBound(refAnchor); - direction = 0; // Stops when destination is reached. - } - - accessor.setX(refAnchor, x); - tick = 0; - } - } else if (tick++ == DEFAULT_CYCLING_PERIOD) { - if (x <= accessor.getRightBound(refAnchor)) - direction = 1; // Right - else - direction = -1; // Left - tick = 0; - } - } else tick = 0; - } - } - - private static class HorizontalScrollerScrollingTicker extends EntryScrollingTicker { - @MagicConstant(intValues = {-1, 1}) - private final int direction; - - public HorizontalScrollerScrollingTicker(@MagicConstant int direction) { - switch (direction) { - case -1: case 1: - this.direction = direction; break; - default: - throw new IllegalArgumentException("direction; input: " + direction); - } - } - - private boolean moving = false; - - @Override - public void tick(@NotNull EntryXAccessor accessor) { - RelPos refAnchor = direction == 1 ? RelPos.LEFT : RelPos.RIGHT; - accessor.setAnchors(refAnchor); - int x = accessor.getX(refAnchor); - int width = accessor.getWidth(); - int lw = direction == -1 ? -width : 0; - int rw = direction == 1 ? width : 0; - // Proceeds when the entry is out of bounds. - if (x < accessor.getLeftBound(refAnchor) || x > accessor.getRightBound(refAnchor)) { - if (moving) { - if (tick++ == 5) { - if (direction == 1 && x >= accessor.getRightBound(refAnchor) + rw) { // Left side reaches right bound - x += accessor.getLeftBound(refAnchor) - x - width; - } else if (direction == -1 && x <= accessor.getLeftBound(refAnchor) + lw) { - x += accessor.getRightBound(refAnchor) - x + width; - } else { - x += direction * MinicraftImage.boxWidth; - } - - int diff = direction == 1 ? accessor.getRightBound(refAnchor) - x : - x - accessor.getLeftBound(refAnchor); - if (diff >= 0 && diff < MinicraftImage.boxWidth) { // Moves back to the original point - moving = false; // Pauses the scrolling - // Alignment correction - x = direction == 1 ? accessor.getRightBound(refAnchor) : accessor.getLeftBound(refAnchor); - } - - accessor.setX(refAnchor, x); - tick = 0; - } - } else if (tick++ == DEFAULT_CYCLING_PERIOD) { - moving = true; - tick = 0; - } - } else tick = 0; - } - } - public void setExceedingAlternatingScrollingTicker() { ticker = new ExceedingHorizontallyAlternatingScrollingTicker(); } From 327b6bfd6f13c5846f56ffa5d3474c98a928cc70 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Fri, 16 Feb 2024 01:16:37 +0800 Subject: [PATCH 8/9] Remove uses of menu entry peak removal calculation --- src/client/java/minicraft/screen/Menu.java | 55 ++----------------- .../minicraft/screen/OptionsWorldDisplay.java | 1 - 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/src/client/java/minicraft/screen/Menu.java b/src/client/java/minicraft/screen/Menu.java index 5f6de5306..6eaf307d7 100644 --- a/src/client/java/minicraft/screen/Menu.java +++ b/src/client/java/minicraft/screen/Menu.java @@ -550,7 +550,6 @@ public static class Builder { @NotNull private RelPos menuPos = RelPos.CENTER; private Dimension menuSize = null; - private boolean removeEntryPeaks = false; private boolean searcherBar; @@ -608,11 +607,6 @@ public Builder setTitlePos(RelPos rp) { return this; } - public Builder setRemoveEntryPeaks(boolean removeEntryPeaks) { - this.removeEntryPeaks = removeEntryPeaks; - return this; - } - public Builder setTitle(String title) { menu.title = title; return this; @@ -752,46 +746,11 @@ else if (c.xIndex == 2) // must be center right if (menuSize == null) { int width = titleDim.width; - // There should be valid peaks to be handled when the number of entries is greater than 1. - if (removeEntryPeaks && menu.entries.size() > 1) { - // First write into an array to more easily find peaks. - int[] entryWidths = new int[menu.entries.size()]; - for (int i = 0; i < menu.entries.size(); ++i) { - entryWidths[i] = menu.entries.get(i).delegate.getWidth(); - } - - // Reference: https://www.geeksforgeeks.org/print-all-the-peaks-and-troughs-in-an-array-of-integers/ - boolean handled = false; // For security check. - ArrayList peaks = new ArrayList<>(); - for (int i = 0; i < entryWidths.length; ++i) { - // Checks if the element is greater than the neighbours - if ((i == 0 || entryWidths[i] > entryWidths[i - 1]) && (i == entryWidths.length - 1 || entryWidths[i] > entryWidths[i + 1])) { - peaks.add(entryWidths[i]); - } else { - int entryWidth = entryWidths[i]; - if (menu.isSelectable() && !menu.entries.get(i).delegate.isSelectable()) - entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); - width = Math.max(width, entryWidth); - handled = true; - } - } - - if (!handled) width = Math.max(width, Arrays.stream(entryWidths).max().getAsInt()); - else { - if (peaks.size() > 0) { // Count in the peaks into the resultant width with small ratio. - double peaksAvg = (double) peaks.stream().mapToInt(a -> a).sum() / peaks.size(); - double ratio = .07 / Math.max(peaksAvg / width - 1, .07); // max is used to prevent extreme case. - width = (int) (width * ratio + (1 - ratio) * peaksAvg); - width -= width % MinicraftImage.boxWidth; // strip extra pixels - } - } - } else { - for (MenuListEntry entry : menu.entries) { - int entryWidth = entry.delegate.getWidth(); - if (menu.isSelectable() && !entry.delegate.isSelectable()) - entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); - width = Math.max(width, entryWidth); - } + for (MenuListEntry entry : menu.entries) { + int entryWidth = entry.delegate.getWidth(); + if (menu.isSelectable() && !entry.delegate.isSelectable()) + entryWidth = Math.max(0, entryWidth - MinicraftImage.boxWidth * 4); + width = Math.max(width, entryWidth); } if (!menu.hasFrame && menu.entryPos.xIndex == 1) width = Screen.w; // Reduces troubles simply :) @@ -864,9 +823,6 @@ else if (menuPos.yIndex == 2) menu.useSearcherBar = searcherBar; - if (!menu.hasFrame && removeEntryPeaks) - menu.renderOutOfFrame = true; // Peaks are usually hidden, so this is assumed to be true. - // done setting defaults/values; return the new menu menu.init(); // any setup the menu does by itself right before being finished. @@ -889,7 +845,6 @@ public Builder copy() { b.setTitleColor = setTitleColor; b.titleCol = titleCol; b.searcherBar = searcherBar; - b.removeEntryPeaks = removeEntryPeaks; return b; } diff --git a/src/client/java/minicraft/screen/OptionsWorldDisplay.java b/src/client/java/minicraft/screen/OptionsWorldDisplay.java index eadef46da..a72fbcee7 100644 --- a/src/client/java/minicraft/screen/OptionsWorldDisplay.java +++ b/src/client/java/minicraft/screen/OptionsWorldDisplay.java @@ -53,7 +53,6 @@ public OptionsWorldDisplay() { menus = new Menu[]{ new Menu.Builder(false, 6, RelPos.LEFT, entries) .setTitle("minicraft.displays.options_world") - .setRemoveEntryPeaks(true) .createMenu() }; } From 8b1962796fc3cf8eb320bd309cc6fd9b64071e97 Mon Sep 17 00:00:00 2001 From: BenCheung0422 <74168521+BenCheung0422@users.noreply.github.com> Date: Sun, 4 Aug 2024 05:21:15 +0800 Subject: [PATCH 9/9] Resolve post-merge errors --- src/client/java/minicraft/gfx/Screen.java | 22 ++++++++-------- .../java/minicraft/level/tile/DecorTile.java | 2 +- .../minicraft/screen/ContainerDisplay.java | 26 +++++++++---------- .../screen/OnScreenKeyboardMenu.java | 6 ++--- .../minicraft/screen/PlayerInvDisplay.java | 14 +++++----- .../java/minicraft/screen/QuestsDisplay.java | 2 +- 6 files changed, 36 insertions(+), 36 deletions(-) diff --git a/src/client/java/minicraft/gfx/Screen.java b/src/client/java/minicraft/gfx/Screen.java index c7b4b5504..f384e8209 100644 --- a/src/client/java/minicraft/gfx/Screen.java +++ b/src/client/java/minicraft/gfx/Screen.java @@ -675,28 +675,28 @@ public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int y // xp and yp are originally in level coordinates, but offset turns them to screen coordinates. // xOffset and yOffset account for screen offset - render(xp - xOffset, yp - yOffset, xt * 8, yt * 8, 8, 8, sheet, bits, whiteTint, fullbright, color); + render(limitingModel, xp - xOffset, yp - yOffset, xt * 8, yt * 8, 8, 8, sheet, bits, whiteTint, fullbright, color); } - public void render(int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet) { - render(xp, yp, xt, yt ,tw, th, sheet, 0); + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet) { + render(limitingModel, xp, yp, xt, yt ,tw, th, sheet, 0); } - public void render(int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors) { - render(xp, yp, xt, yt ,tw, th, sheet, mirrors, -1); + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors) { + render(limitingModel, xp, yp, xt, yt ,tw, th, sheet, mirrors, -1); } - public void render(int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors, int whiteTint) { - render(xp, yp, xt, yt, tw, th, sheet, mirrors, whiteTint, false); + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors, int whiteTint) { + render(limitingModel, xp, yp, xt, yt, tw, th, sheet, mirrors, whiteTint, false); } - public void render(int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors, int whiteTint, boolean fullbright) { - render(xp, yp, xt, yt, tw, th, sheet, mirrors, whiteTint, fullbright, 0); + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors, int whiteTint, boolean fullbright) { + render(limitingModel, xp, yp, xt, yt, tw, th, sheet, mirrors, whiteTint, fullbright, 0); } // Any single pixel from the image can be rendered using this method. - public void render(int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors, int whiteTint, boolean fullBright, int color) { + public void render(@Nullable RenderingLimitingModel limitingModel, int xp, int yp, int xt, int yt, int tw, int th, MinicraftImage sheet, int mirrors, int whiteTint, boolean fullBright, int color) { if (sheet == null) return; // Verifying that sheet is not null. // Validation check if (xt + tw > sheet.width && yt + th > sheet.height) { - render(xp, yp, 0, 0, mirrors, Renderer.spriteLinker.missingSheet(SpriteType.Item)); + render(null, xp, yp, 0, 0, mirrors, Renderer.spriteLinker.missingSheet(SpriteType.Item)); return; } diff --git a/src/client/java/minicraft/level/tile/DecorTile.java b/src/client/java/minicraft/level/tile/DecorTile.java index a3ee6968c..45e5a53a4 100644 --- a/src/client/java/minicraft/level/tile/DecorTile.java +++ b/src/client/java/minicraft/level/tile/DecorTile.java @@ -52,7 +52,7 @@ protected DecorTile(decorType type) { @Override public void render(Screen screen, Level level, int x, int y) { super.render(screen, level, x, y); - screen.render(x * 16 + 0, y * 16, sprite.getCurrentFrame().getSprite().spritePixels[0][0]); + screen.render(null, x >> 4, y >> 4, sprite.getCurrentFrame().getSprite().spritePixels[0][0]); } public boolean interact(Level level, int xt, int yt, Player player, Item item, Direction attackDir) { diff --git a/src/client/java/minicraft/screen/ContainerDisplay.java b/src/client/java/minicraft/screen/ContainerDisplay.java index 05da5855e..77d7ea61e 100644 --- a/src/client/java/minicraft/screen/ContainerDisplay.java +++ b/src/client/java/minicraft/screen/ContainerDisplay.java @@ -85,10 +85,10 @@ public void render(Screen screen) { // Expanded counter if (sizeLeft < 10) { // At the moment at most just 2 digits and always 2 digits for capacity (no worry yet) // Background - screen.render(boundsLeft.getRight() + 2 - (23 - 5), boundsLeft.getTop() - 3, + screen.render(null, boundsLeft.getRight() + 2 - (23 - 5), boundsLeft.getTop() - 3, 12, 12, 3, 13, counterSheet); // Skips the middle part as that is for more digits - screen.render(boundsLeft.getRight() + 2 - 15, boundsLeft.getTop() - 3, + screen.render(null, boundsLeft.getRight() + 2 - 15, boundsLeft.getTop() - 3, 20, 12, 15, 13, counterSheet); // Digits @@ -98,7 +98,7 @@ public void render(Screen screen) { 0, 4, 5, capLeft, Color.GRAY); } else { // Background - screen.render(boundsLeft.getRight() + 2 - 23, boundsLeft.getTop() - 3, + screen.render(null, boundsLeft.getRight() + 2 - 23, boundsLeft.getTop() - 3, 12, 12, 23, 13, counterSheet); // Digits @@ -115,10 +115,10 @@ public void render(Screen screen) { // Minimized counter if (sizeRight < 10) { // no worry yet, really // Background - screen.render(boundsRight.getLeft() + 4, boundsRight.getTop() - 1, + screen.render(null, boundsRight.getLeft() + 4, boundsRight.getTop() - 1, 0, 12, 4, 9, counterSheet); // Skips the middle part as that is for more digits - screen.render(boundsRight.getLeft() + 8, boundsRight.getTop() - 1, + screen.render(null, boundsRight.getLeft() + 8, boundsRight.getTop() - 1, 8, 12, 4, 9, counterSheet); // Digits @@ -126,7 +126,7 @@ public void render(Screen screen) { 0, 4, 5, sizeRight, fadeColor(colorByHeaviness(calculateHeaviness(sizeRight, capRight), false))); } else { // Background - screen.render(boundsRight.getLeft() + 4, boundsRight.getTop() - 1, + screen.render(null, boundsRight.getLeft() + 4, boundsRight.getTop() - 1, 0, 12, 12, 9, counterSheet); // Digits @@ -141,10 +141,10 @@ public void render(Screen screen) { // Minimized counter if (sizeLeft < 10) { // Background - screen.render(boundsLeft.getRight() - 4 - 8, boundsLeft.getTop() - 1, + screen.render(null, boundsLeft.getRight() - 4 - 8, boundsLeft.getTop() - 1, 0, 12, 4, 9, counterSheet); // Skips the middle part as that is for more digits - screen.render(boundsLeft.getRight() - 4 - 4, boundsLeft.getTop() - 1, + screen.render(null, boundsLeft.getRight() - 4 - 4, boundsLeft.getTop() - 1, 8, 12, 4, 9, counterSheet); // Digits @@ -152,7 +152,7 @@ public void render(Screen screen) { 0, 4, 5, sizeLeft, fadeColor(colorByHeaviness(calculateHeaviness(sizeLeft, capLeft), false))); } else { // Background - screen.render(boundsLeft.getRight() - 4 - 12, boundsLeft.getTop() - 1, + screen.render(null, boundsLeft.getRight() - 4 - 12, boundsLeft.getTop() - 1, 0, 12, 12, 9, counterSheet); // Digits @@ -167,10 +167,10 @@ public void render(Screen screen) { // Expanded counter (background horizontally mirrored) if (sizeRight < 10) { // Background - screen.render(boundsRight.getLeft() - 2 + (20 - 5), boundsRight.getTop() - 3, + screen.render(null, boundsRight.getLeft() - 2 + (20 - 5), boundsRight.getTop() - 3, 12, 12, 3, 13, counterSheet, 1); // Skips the middle part as that is for more digits - screen.render(boundsRight.getLeft() - 2, boundsRight.getTop() - 3, + screen.render(null, boundsRight.getLeft() - 2, boundsRight.getTop() - 3, 20, 12, 15, 13, counterSheet, 1); // Digits @@ -180,7 +180,7 @@ public void render(Screen screen) { 0, 4, 5, capRight, Color.GRAY); } else { // Background - screen.render(boundsRight.getLeft() - 2, boundsRight.getTop() - 3, + screen.render(null, boundsRight.getLeft() - 2, boundsRight.getTop() - 3, 12, 12, 23, 13, counterSheet, 1); // Digits @@ -196,7 +196,7 @@ public void render(Screen screen) { private void renderCounterNumber(Screen screen, int xp, int yp, int ys, int w, int h, int n, int color) { String display = String.valueOf(n); for (int i = 0; i < display.length(); ++i) { - screen.render(xp + i * w, yp, w * (display.charAt(i) - '0'), ys, w, h, counterSheet, 0, color); + screen.render(null, xp + i * w, yp, w * (display.charAt(i) - '0'), ys, w, h, counterSheet, 0, color); } } diff --git a/src/client/java/minicraft/screen/OnScreenKeyboardMenu.java b/src/client/java/minicraft/screen/OnScreenKeyboardMenu.java index 6d6f285dc..23fd080ea 100644 --- a/src/client/java/minicraft/screen/OnScreenKeyboardMenu.java +++ b/src/client/java/minicraft/screen/OnScreenKeyboardMenu.java @@ -287,11 +287,11 @@ public void render(Screen screen) { VirtualKey key = keys[r][c]; int color = keyPressed > 0 && r == this.y && c == this.x? 0x1EFEFF0: 0x1FDFDFD; if (key == backspace) { - screen.render(x, y, 0, 0, keyWidth , keyHeight , sheet, color); + screen.render(null, x, y, 0, 0, keyWidth , keyHeight , sheet, color); } else if (key == shiftKey) { - screen.render(x, y, keyWidth , 0, keyWidth , keyHeight , sheet, color); + screen.render(null, x, y, keyWidth , 0, keyWidth , keyHeight , sheet, color); } else if (key == spaceBar) { - screen.render(x, y, 0, keyHeight, keyWidth , keyHeight , sheet, color); + screen.render(null, x, y, 0, keyHeight, keyWidth , keyHeight , sheet, color); } else Font.draw(String.valueOf(key.output), screen, x + keyWidth / 2 - 3, y + keyHeight / 2 - 3, color); diff --git a/src/client/java/minicraft/screen/PlayerInvDisplay.java b/src/client/java/minicraft/screen/PlayerInvDisplay.java index 65373e58e..27f6df00f 100644 --- a/src/client/java/minicraft/screen/PlayerInvDisplay.java +++ b/src/client/java/minicraft/screen/PlayerInvDisplay.java @@ -206,10 +206,10 @@ public void render(Screen screen) { // Expanded counter if (sizeLeft < 10) { // At the moment at most just 2 digits and always 2 digits for capacity (no worry yet) // Background - screen.render(boundsLeft.getRight() + 2 - (23 - 5), boundsLeft.getTop() - 3, + screen.render(null, boundsLeft.getRight() + 2 - (23 - 5), boundsLeft.getTop() - 3, 12, 12, 3, 13, counterSheet); // Skips the middle part as that is for more digits - screen.render(boundsLeft.getRight() + 2 - 15, boundsLeft.getTop() - 3, + screen.render(null, boundsLeft.getRight() + 2 - 15, boundsLeft.getTop() - 3, 20, 12, 15, 13, counterSheet); // Digits @@ -219,7 +219,7 @@ public void render(Screen screen) { 0, 4, 5, capLeft, Color.GRAY); } else { // Background - screen.render(boundsLeft.getRight() + 2 - 23, boundsLeft.getTop() - 3, + screen.render(null, boundsLeft.getRight() + 2 - 23, boundsLeft.getTop() - 3, 12, 12, 23, 13, counterSheet); // Digits @@ -236,10 +236,10 @@ public void render(Screen screen) { // Minimized counter if (sizeLeft < 10) { // Background - screen.render(boundsLeft.getRight() - 4 - 8, boundsLeft.getTop() - 1, + screen.render(null, boundsLeft.getRight() - 4 - 8, boundsLeft.getTop() - 1, 0, 12, 4, 9, counterSheet); // Skips the middle part as that is for more digits - screen.render(boundsLeft.getRight() - 4 - 4, boundsLeft.getTop() - 1, + screen.render(null, boundsLeft.getRight() - 4 - 4, boundsLeft.getTop() - 1, 8, 12, 4, 9, counterSheet); // Digits @@ -247,7 +247,7 @@ public void render(Screen screen) { 0, 4, 5, sizeLeft, fadeColor(colorByHeaviness(calculateHeaviness(sizeLeft, capLeft), false))); } else { // Background - screen.render(boundsLeft.getRight() - 4 - 12, boundsLeft.getTop() - 1, + screen.render(null, boundsLeft.getRight() - 4 - 12, boundsLeft.getTop() - 1, 0, 12, 12, 9, counterSheet); // Digits @@ -268,7 +268,7 @@ public void render(Screen screen) { private void renderCounterNumber(Screen screen, int xp, int yp, int ys, int w, int h, int n, int color) { String display = String.valueOf(n); for (int i = 0; i < display.length(); ++i) { - screen.render(xp + i * w, yp, w * (display.charAt(i) - '0'), ys, w, h, counterSheet, 0, color); + screen.render(null, xp + i * w, yp, w * (display.charAt(i) - '0'), ys, w, h, counterSheet, 0, color); } } diff --git a/src/client/java/minicraft/screen/QuestsDisplay.java b/src/client/java/minicraft/screen/QuestsDisplay.java index 557301b4f..37bfecc74 100644 --- a/src/client/java/minicraft/screen/QuestsDisplay.java +++ b/src/client/java/minicraft/screen/QuestsDisplay.java @@ -508,7 +508,7 @@ public void render(Screen screen) { renderRaster(); // Border screen.drawRect(rasterX - 1, rasterY - 1, rasterWidth + 2, rasterHeight + 2, Color.WHITE); - screen.render(rasterX, rasterY, 0, 0, rasterWidth, rasterHeight, image); + screen.render(null, rasterX, rasterY, 0, 0, rasterWidth, rasterHeight, image); } private void renderRaster() {