From da65270ab0199ed05d0b8af28b41f074ea7e5e27 Mon Sep 17 00:00:00 2001 From: yugecin Date: Sun, 11 Dec 2016 11:57:17 +0100 Subject: [PATCH] use new option overlay for storyboard menu --- src/itdelatrisu/opsu/states/Game.java | 15 +- src/yugecin/opsudance/ui/ItemList.java | 147 ----------- src/yugecin/opsudance/ui/OptionsOverlay.java | 5 +- .../opsudance/ui/OptionsOverlayOld.java | 229 ------------------ src/yugecin/opsudance/ui/SBOverlay.java | 180 +++++++++----- 5 files changed, 130 insertions(+), 446 deletions(-) delete mode 100644 src/yugecin/opsudance/ui/ItemList.java delete mode 100644 src/yugecin/opsudance/ui/OptionsOverlayOld.java diff --git a/src/itdelatrisu/opsu/states/Game.java b/src/itdelatrisu/opsu/states/Game.java index 74c25464..26138984 100644 --- a/src/itdelatrisu/opsu/states/Game.java +++ b/src/itdelatrisu/opsu/states/Game.java @@ -299,14 +299,13 @@ public class Game extends BasicGameState { private final int state; private final Cursor mirrorCursor; - private final SBOverlay sbOverlay; + private SBOverlay sbOverlay; private FakeCombinedCurve knorkesliders; public Game(int state) { this.state = state; mirrorCursor = new Cursor(true); - sbOverlay = new SBOverlay(this); } public void loadCheckpoint(int checkpoint) { @@ -347,6 +346,7 @@ public class Game extends BasicGameState { @Override public void init(GameContainer container, StateBasedGame game) throws SlickException { + this.sbOverlay = new SBOverlay(this, container); this.container = container; this.game = game; input = container.getInput(); @@ -354,8 +354,6 @@ public class Game extends BasicGameState { int width = container.getWidth(); int height = container.getHeight(); - sbOverlay.init(container, input, width, height); - // create offscreen graphics offscreen = new Image(width, height); gOffscreen = offscreen.getGraphics(); @@ -724,7 +722,7 @@ public class Game extends BasicGameState { else UI.draw(g); - sbOverlay.render(container, game, g); + sbOverlay.render(container, g); if (!Dancer.hidewatermark) { Fonts.SMALL.drawString(0.3f, 0.3f, "opsu!dance " + Updater.get().getCurrentVersion() + " by robin_be | https://github.com/yugecin/opsu-dance"); @@ -741,7 +739,7 @@ public class Game extends BasicGameState { } yugecin.opsudance.spinners.Spinner.update(delta); int mouseX = input.getMouseX(), mouseY = input.getMouseY(); - sbOverlay.update(mouseX, mouseY); + sbOverlay.update(delta, mouseX, mouseY); skipButton.hoverUpdate(delta, mouseX, mouseY); if (isReplay || GameMod.AUTO.isActive()) playbackSpeed.getButton().hoverUpdate(delta, mouseX, mouseY); @@ -1189,7 +1187,10 @@ public class Game extends BasicGameState { @Override public void mouseDragged(int oldx, int oldy, int newx, int newy) { - sbOverlay.mouseDragged(oldx, oldy, newx, newy); + if (sbOverlay.mouseDragged(oldx, oldy, newx, newy)) { + //noinspection UnnecessaryReturnStatement + return; + } } @Override diff --git a/src/yugecin/opsudance/ui/ItemList.java b/src/yugecin/opsudance/ui/ItemList.java deleted file mode 100644 index b08c5185..00000000 --- a/src/yugecin/opsudance/ui/ItemList.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin - * - * opsu!dance is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * opsu!dance is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with opsu!dance. If not, see . - */ - -package yugecin.opsudance.ui; - -import itdelatrisu.opsu.Utils; -import itdelatrisu.opsu.ui.Fonts; -import itdelatrisu.opsu.ui.UI; -import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; -import org.newdawn.slick.Graphics; -import org.newdawn.slick.Input; -import org.newdawn.slick.state.StateBasedGame; - -import java.util.Observer; - -public class ItemList { - - private int x; - private int y; - private int width; - private int height; - - private int start; - - private Object[] items; - private Observer observer; - - private boolean visible; - - public void init(GameContainer container) { - x = container.getWidth() / 6; - y = 0; - width = x * 4; - height = container.getHeight(); - } - - public void setItems(Object[] items) { - this.items = items; - } - - public void setClickListener(Observer o) { - observer = o; - } - - public boolean isVisible() { - return visible; - } - - public void show() { - visible = true; - } - - public void render(GameContainer container, StateBasedGame game, Graphics g) { - g.setColor(Color.black); - g.fillRect(x, y, width, height); - int y = this.y + 5; - for (int i = start, m = items.length; i < m; i++) { - if (container.getInput().getMouseX() >= x && container.getInput().getMouseX() < x + width) { - if (container.getInput().getMouseY() >= y && container.getInput().getMouseY() < y + Fonts.MEDIUM.getLineHeight() + 5) { - g.setColor(new Color(0x11A9FF)); - g.fillRect(x, y, width, Fonts.MEDIUM.getLineHeight() + 5); - } - } - Fonts.MEDIUM.drawString(x + 5, y + 2, items[i].toString(), Color.white); - y += Fonts.MEDIUM.getLineHeight() + 5; - if (y >= height - Fonts.MEDIUM.getLineHeight()) { - break; - } - } - } - - private int startStart; - private int mouseStartX; - private int mouseStartY; - - public void mousePressed(int button, int x, int y) { - mouseStartX = x; - mouseStartY = y; - startStart = start; - } - - public void mouseReleased(int button, int x, int y) { - if (Utils.distance(x, y, mouseStartX, mouseStartY) > 5) { - return; - } - if (UI.getBackButton().contains(x, y)) { - visible = false; - } - if (x < this.x || x > this.x + width) { - visible = false; - return; - } - if (button != 0) { - return; - } - if (y < 5) { - return; - } - y -= 5; - int index = y / (Fonts.MEDIUM.getLineHeight() + 5); - index += start; - if (index >= 0 && index < items.length) { - observer.update(null, index); - visible = false; - } - } - - public void mouseDragged(int oldx, int oldy, int x, int y) { - start = startStart - (y - mouseStartY) / (Fonts.MEDIUM.getLineHeight() + 5); - if (start < 0) { - start = 0; - } else if (start > items.length - 1) { - start = items.length - 1; - } - } - - public void mouseWheelMoved(int delta) { - if (delta > 0) { - start = Math.max(0, start - 1); - } else { - start = Math.min(items.length - 1, start + 1); - } - } - - public void keyPressed(int key, char c) { - if (key == Input.KEY_ESCAPE) { - visible = false; - } - } - -} diff --git a/src/yugecin/opsudance/ui/OptionsOverlay.java b/src/yugecin/opsudance/ui/OptionsOverlay.java index 74e4d3e5..f67732c2 100644 --- a/src/yugecin/opsudance/ui/OptionsOverlay.java +++ b/src/yugecin/opsudance/ui/OptionsOverlay.java @@ -395,12 +395,13 @@ public class OptionsOverlay { } } - public void keyPressed(int key, char c) { + public boolean keyPressed(int key, char c) { switch (key) { case Input.KEY_ESCAPE: parent.onLeave(); - break; + return true; } + return false; } private void updateHoverOption(int mouseX, int mouseY) { diff --git a/src/yugecin/opsudance/ui/OptionsOverlayOld.java b/src/yugecin/opsudance/ui/OptionsOverlayOld.java deleted file mode 100644 index 9ffb4a5a..00000000 --- a/src/yugecin/opsudance/ui/OptionsOverlayOld.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * opsu!dance - fork of opsu! with cursordance auto - * Copyright (C) 2016 yugecin - * - * opsu!dance is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * opsu!dance is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with opsu!dance. If not, see . - */ -package yugecin.opsudance.ui; - -import itdelatrisu.opsu.Options; -import itdelatrisu.opsu.ui.Colors; -import itdelatrisu.opsu.ui.Fonts; -import org.newdawn.slick.Color; -import org.newdawn.slick.GameContainer; -import org.newdawn.slick.Graphics; -import org.newdawn.slick.Input; -import org.newdawn.slick.state.StateBasedGame; - -import java.util.Observable; -import java.util.Observer; - -public class OptionsOverlayOld { - - private int width; - private int height; - - private static Options.GameOption[] options = new Options.GameOption[] { - Options.GameOption.DANCE_MOVER, - Options.GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS, - Options.GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR, - Options.GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS, - Options.GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR, - Options.GameOption.DANCE_MOVER_DIRECTION, - Options.GameOption.DANCE_SLIDER_MOVER_TYPE, - Options.GameOption.DANCE_SPINNER, - Options.GameOption.DANCE_SPINNER_DELAY, - Options.GameOption.DANCE_LAZY_SLIDERS, - Options.GameOption.DANCE_CIRCLE_STREAMS, - Options.GameOption.DANCE_ONLY_CIRCLE_STACKS, - Options.GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS, - Options.GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS, - Options.GameOption.DANCE_MIRROR, - Options.GameOption.DANCE_DRAW_APPROACH, - Options.GameOption.DANCE_OBJECT_COLOR_OVERRIDE, - Options.GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED, - Options.GameOption.DANCE_RGB_OBJECT_INC, - Options.GameOption.DANCE_CURSOR_COLOR_OVERRIDE, - Options.GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE, - Options.GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL, - Options.GameOption.DANCE_RGB_CURSOR_INC, - Options.GameOption.DANCE_CURSOR_TRAIL_OVERRIDE, - Options.GameOption.DANCE_REMOVE_BG, - Options.GameOption.DANCE_HIDE_OBJECTS, - Options.GameOption.DANCE_HIDE_UI, - Options.GameOption.PIPPI_ENABLE, - Options.GameOption.PIPPI_RADIUS_PERCENT, - Options.GameOption.PIPPI_ANGLE_INC_MUL, - Options.GameOption.PIPPI_ANGLE_INC_MUL_SLIDER, - Options.GameOption.PIPPI_SLIDER_FOLLOW_EXPAND, - Options.GameOption.PIPPI_PREVENT_WOBBLY_STREAMS, - Options.GameOption.SHOW_HIT_LIGHTING, - }; - - private int textHeight; - private Input input; - private final ItemList list; - private GameContainer container; - private Options.GameOption selectedOption; - private final SBOverlay overlay; - - public OptionsOverlayOld(SBOverlay overlay) { - this.overlay = overlay; - list = new ItemList(); - } - - public Options.GameOption[] getSavedOptionList() { - return options; - } - - public void init(GameContainer container, Input input, int width, int height) { - list.init(container); - this.input = input; - this.width = width; - this.height = height; - this.container = container; - textHeight = Fonts.SMALL.getLineHeight(); - } - - public void render(GameContainer container, StateBasedGame game, Graphics g) { - int hoverIdx = getOptionIdxAt(input.getMouseY()); - float a = Color.black.a; - Color.black.a = 0.8f; - g.setColor(Color.black); - g.fillRect(0, 0, width, height); - Color.black.a = a; - for (int i = 0, j = 0; i < options.length; i++) { - if (!options[i].showCondition()) { - continue; - } - drawOption(g, options[i], j++, selectedOption == null ? hoverIdx == i : selectedOption == options[i]); - } - if (list.isVisible()) { - list.render(container, game, g); - } - } - - // I know... kill me - private void drawOption(Graphics g, Options.GameOption option, int pos, boolean focus) { - float y = pos * (textHeight + 5); - Color color = (focus) ? Color.cyan : Color.white; - - Fonts.MEDIUM.drawString(width / 6 * 2, y, option.getName(), color); - Fonts.MEDIUM.drawString(width / 3 * 2, y, option.getValueString(), color); - g.setColor(Colors.WHITE_ALPHA); - g.drawLine(0, y + textHeight + 3, width, y + textHeight + 3 + 1); - } - - private int getOptionIdxAt(int y) { - int index = y / (textHeight + 5); - if (index >= options.length) { - return -1; - } - for (int i = 0; i < options.length; i++) { - if (options[i].showCondition()) { - index--; - } - if (index < 0) { - return i; - } - } - return -1; - } - - public void update(int mouseX, int mouseY) { - - } - - public boolean mousePressed(int button, int x, int y) { - if (list.isVisible()) { - list.mousePressed(button, x, y); - return true; - } - int idx = getOptionIdxAt(y); - if (idx >= 0 && idx < options.length) { - final Options.GameOption option = options[idx]; - selectedOption = option; - Object[] listItems = option.getListItems(); - if (listItems == null) { - option.click(container); - } else { - list.setItems(listItems); - list.setClickListener(new Observer() { - @Override - public void update(Observable o, Object arg) { - option.clickListItem((int) arg); - overlay.saveOption(option); - } - }); - list.show(); - } - } - return true; - } - - - public void mouseDragged(int oldx, int oldy, int newx, int newy) { - if (list.isVisible()) { - list.mouseDragged(oldx, oldy, newx, newy); - return; - } - - int multiplier; - if (input.isMouseButtonDown(Input.MOUSE_RIGHT_BUTTON)) - multiplier = 4; - else if (input.isMouseButtonDown(Input.MOUSE_LEFT_BUTTON)) - multiplier = 1; - else - return; - - // get direction - int diff = newx - oldx; - if (diff == 0) - return; - diff = ((diff > 0) ? 1 : -1) * multiplier; - - // options (drag only) - if (selectedOption != null) { - selectedOption.drag(container, diff); - } - } - - public boolean mouseReleased(int button, int x, int y) { - if (selectedOption != null) { - overlay.saveOption(selectedOption); - } - selectedOption = null; - if (list.isVisible()) { - list.mouseReleased(button, x, y); - return true; - } - return true; - } - - public boolean mouseWheenMoved(int newValue) { - if (list.isVisible()) { - list.mouseWheelMoved(newValue); - return true; - } - return true; - } - - public boolean keyPressed(int key, char c) { - if (list.isVisible()) { - list.keyPressed(key, c); - return true; - } - return false; - } -} diff --git a/src/yugecin/opsudance/ui/SBOverlay.java b/src/yugecin/opsudance/ui/SBOverlay.java index 3b4ea368..a2d6028f 100644 --- a/src/yugecin/opsudance/ui/SBOverlay.java +++ b/src/yugecin/opsudance/ui/SBOverlay.java @@ -18,6 +18,7 @@ package yugecin.opsudance.ui; import itdelatrisu.opsu.Options; +import itdelatrisu.opsu.Options.GameOption; import itdelatrisu.opsu.audio.MusicController; import itdelatrisu.opsu.objects.GameObject; import itdelatrisu.opsu.states.Game; @@ -26,15 +27,73 @@ import org.newdawn.slick.Color; import org.newdawn.slick.GameContainer; import org.newdawn.slick.Graphics; import org.newdawn.slick.Input; -import org.newdawn.slick.state.StateBasedGame; import yugecin.opsudance.ObjectColorOverrides; +import yugecin.opsudance.ui.OptionsOverlay.OptionTab; -import java.util.HashMap; -import java.util.Map; +import java.util.*; @SuppressWarnings("unchecked") -public class SBOverlay { +public class SBOverlay implements OptionsOverlay.Parent { + private static final OptionTab[] options = new OptionsOverlay.OptionTab[]{ + new OptionTab("Gameplay", new GameOption[] { + GameOption.BACKGROUND_DIM, + GameOption.SNAKING_SLIDERS, + GameOption.SHRINKING_SLIDERS, + GameOption.SHOW_HIT_LIGHTING, + GameOption.SHOW_COMBO_BURSTS, + GameOption.SHOW_PERFECT_HIT, + GameOption.SHOW_FOLLOW_POINTS, + }), + new OptionTab("Input", new GameOption[] { + GameOption.CURSOR_SIZE, + GameOption.NEW_CURSOR, + GameOption.DISABLE_CURSOR + }), + new OptionTab("Dance", new GameOption[] { + GameOption.DANCE_MOVER, + GameOption.DANCE_QUAD_BEZ_AGGRESSIVENESS, + GameOption.DANCE_QUAD_BEZ_SLIDER_AGGRESSIVENESS_FACTOR, + GameOption.DANCE_QUAD_BEZ_USE_CUBIC_ON_SLIDERS, + GameOption.DANCE_QUAD_BEZ_CUBIC_AGGRESSIVENESS_FACTOR, + GameOption.DANCE_MOVER_DIRECTION, + GameOption.DANCE_SLIDER_MOVER_TYPE, + GameOption.DANCE_SPINNER, + GameOption.DANCE_SPINNER_DELAY, + GameOption.DANCE_LAZY_SLIDERS, + GameOption.DANCE_CIRCLE_STREAMS, + GameOption.DANCE_ONLY_CIRCLE_STACKS, + GameOption.DANCE_CIRLCE_IN_SLOW_SLIDERS, + GameOption.DANCE_CIRLCE_IN_LAZY_SLIDERS, + GameOption.DANCE_MIRROR, + }), + new OptionTab("Dance display", new GameOption[] { + GameOption.DANCE_DRAW_APPROACH, + GameOption.DANCE_OBJECT_COLOR_OVERRIDE, + GameOption.DANCE_OBJECT_COLOR_OVERRIDE_MIRRORED, + GameOption.DANCE_RGB_OBJECT_INC, + GameOption.DANCE_CURSOR_COLOR_OVERRIDE, + GameOption.DANCE_CURSOR_MIRROR_COLOR_OVERRIDE, + GameOption.DANCE_CURSOR_ONLY_COLOR_TRAIL, + GameOption.DANCE_RGB_CURSOR_INC, + GameOption.DANCE_CURSOR_TRAIL_OVERRIDE, + GameOption.DANCE_REMOVE_BG, + GameOption.DANCE_HIDE_OBJECTS, + GameOption.DANCE_HIDE_UI, + GameOption.DANCE_ENABLE_SB, + GameOption.DANCE_HIDE_WATERMARK, + }), + new OptionTab ("Pippi", new GameOption[] { + GameOption.PIPPI_ENABLE, + GameOption.PIPPI_RADIUS_PERCENT, + GameOption.PIPPI_ANGLE_INC_MUL, + GameOption.PIPPI_ANGLE_INC_MUL_SLIDER, + GameOption.PIPPI_SLIDER_FOLLOW_EXPAND, + GameOption.PIPPI_PREVENT_WOBBLY_STREAMS, + }) + }; + + private final static List optionList = new ArrayList<>(); public static boolean isActive = false; private boolean hide; @@ -51,23 +110,25 @@ public class SBOverlay { private int index; private final Game game; - private final OptionsOverlayOld options; + private final OptionsOverlay overlay; - public SBOverlay(Game game) { - this.game = game; - options = new OptionsOverlayOld(this); - initialOptions = new HashMap<>(); + static { + for (OptionTab tab : options) { + optionList.addAll(Arrays.asList(tab.options)); + } } - public void init(GameContainer container, Input input, int width, int height) { - this.width = width; - this.height = height; + public SBOverlay(Game game, GameContainer container) { + this.game = game; + initialOptions = new HashMap<>(); + overlay = new OptionsOverlay(this, options, 2, container); + this.width = container.getWidth(); + this.height = container.getHeight(); speed = 10; gameObjects = new GameObject[0]; - options.init(container, input, width, height); } - public void render(GameContainer container, StateBasedGame game, Graphics g) { + public void render(GameContainer container, Graphics g) { if (!isActive || hide) { return; } @@ -95,16 +156,13 @@ public class SBOverlay { g.fillRect(curtime * width, height - 10f, 10f, 10f); } if (menu) { - options.render(container, game, g); + overlay.render(g, container.getInput().getMouseX(), container.getInput().getMouseY()); } } - public void update(int mouseX, int mouseY) { - if (!isActive) { - return; - } - if (menu) { - options.update(mouseX, mouseY); + public void update(int delta, int mouseX, int mouseY) { + if (isActive && menu) { + overlay.update(delta, mouseX, mouseY); } } @@ -112,7 +170,7 @@ public class SBOverlay { if (!isActive) { return false; } - if (options.keyPressed(key, c)) { + if (menu && overlay.keyPressed(key, c)) { return true; } if (key == Input.KEY_C) { @@ -147,12 +205,6 @@ public class SBOverlay { index++; setMusicPosition(); updateIndex(index); - } else if (key == Input.KEY_ESCAPE && menu) { - menu = false; - if (speed != 0) { - MusicController.resume(); - } - return true; } return false; } @@ -187,27 +239,27 @@ public class SBOverlay { if (optionsMap.length > 0) { // copy all current settings in first obj map optionsMap[0] = new HashMap<>(); - for (Options.GameOption o : options.getSavedOptionList()) { + for (Options.GameOption o : optionList) { optionsMap[0].put(o, o.write()); } } this.gameObjects = gameObjects; } - public void saveOption(Options.GameOption option) { - if (optionsMap[index] == null) { - optionsMap[index] = new HashMap<>(); - } - optionsMap[index].put(option, option.write()); - readOption(option); - } - public boolean mousePressed(int button, int x, int y) { - return menu && options.mousePressed(button, x, y); + if (menu) { + return false; + } + overlay.mousePressed(button, x, y); + return true; } - public void mouseDragged(int oldx, int oldy, int newx, int newy) { - if (menu) options.mouseDragged(oldx, oldy, newx, newy); + public boolean mouseDragged(int oldx, int oldy, int newx, int newy) { + if (menu) { + return false; + } + overlay.mouseDragged(oldx, oldy, newx, newy); + return true; } public void updateIndex(int index) { @@ -227,41 +279,30 @@ public class SBOverlay { public boolean mouseReleased(int button, int x, int y) { if (menu) { - return options.mouseReleased(button, x, y); - } - if (x > 10 || index >= optionsMap.length || optionsMap[index] == null) { return false; } - int lh = Fonts.SMALL.getLineHeight(); - int ypos = 50 + lh / 4; - for (Object o : optionsMap[index].entrySet()) { - if (y >= ypos && y <= ypos + 10) { - optionsMap[index].remove(((Map.Entry) o).getKey()); - if (optionsMap[index].size() == 0) { - optionsMap[index] = null; - } - reloadSBsettingsToIndex(index); - return true; - } - ypos += lh; - } - return false; + overlay.mouseReleased(button, x, y); + return true; } - public boolean mouseWheelMoved(int newValue) { - return menu && options.mouseWheenMoved(newValue); + public boolean mouseWheelMoved(int delta) { + if (!menu) { + return false; + } + overlay.mouseWheelMoved(delta); + return true; } public void enter() { // enter, save current settings - for (Options.GameOption o : options.getSavedOptionList()) { + for (Options.GameOption o : optionList) { initialOptions.put(o, o.write()); } } public void leave() { // leave, revert the settings saved before entering - for (Options.GameOption o : options.getSavedOptionList()) { + for (Options.GameOption o : optionList) { if (initialOptions.containsKey(o)) { o.read(initialOptions.get(o)); readOption(o); @@ -283,4 +324,21 @@ public class SBOverlay { } } + @Override + public void onLeave() { + menu = false; + if (speed != 0) { + MusicController.resume(); + } + } + + @Override + public void onSaveOption(GameOption option) { + if (optionsMap[index] == null) { + optionsMap[index] = new HashMap<>(); + } + optionsMap[index].put(option, option.write()); + readOption(option); + } + }